Skip to content
Snippets Groups Projects
Commit 315de703 authored by Brady James Garvin's avatar Brady James Garvin
Browse files

Initial commit.

parents
No related branches found
No related tags found
No related merge requests found
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.eol": "\n",
"files.exclude": {
"**/node_modules": true
},
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
}
# Monoids
* Monoid:
* `M = (S, ∙, e)`
* Parts:
* Set of elements: `S` (actions that can always be taken)
* Binary operator: `∙` (do one action followed by the other)
* Identity element: `e` (the do-nothing action)
* Restrictions:
* `e ∈ S` (one of the actions is the do-nothing action)
* `∙: S × S → S` (for every sequence of actions, there is an equivalent single action)
* Monoid Laws:
* Associativity: `∀a.∀b.∀c. (a∙b)∙c = a∙(b∙c)` (grouping does not matter)
* Two-sided identity: `∀a. e∙a = a∙e = a` (the do-nothing action does nothing)
--------------------------------------------------------------------------------
# Example: The Lightswitch Monoid
* Monoid:
* `L = ({HOLD, OFF, ON, FLIP}, ∙, HOLD)`
* Parts:
* Set of elements: `{HOLD, OFF, ON, FLIP}`
* Binary operator: `∙`
* Identify element: `HOLD`
* Combination Table:
```
∙ HOLD OFF ON FLIP
\----------------------
HOLD |HOLD OFF ON FLIP
OFF |OFF OFF ON ON
ON |ON OFF ON OFF
FLIP |FLIP OFF ON HOLD
```
* Monoid Laws:
* Associativity: `∀a.∀b.∀c. (a∙b)∙c = a∙(b∙c)`
* Two-sided identity: `∀a. HOLD∙a = a∙HOLD = a`
--------------------------------------------------------------------------------
# Reducers
* Example Reducer:
* `f(A) = fold(∙, HOLD, A)`
* Examples Reducer Evaluation using a Left Fold:
* `f([OFF, ON, FLIP, HOLD]) = HOLD ∙ OFF ∙ ON ∙ FLIP ∙ HOLD = OFF`
* `f([FLIP, HOLD, FLIP, FLIP]) = HOLD ∙ FLIP ∙ HOLD ∙ FLIP ∙ FLIP = FLIP`
* `f([]) = HOLD = HOLD`
## Iterative Reducers
```
function iterativeReduce(monoid, sequence) {
let result = monoid.identity;
for (const element of sequence) {
result = monoid.combine(result, element);
}
return result;
}
```
* Example Evaluation: `((((HOLD ∙ FLIP) ∙ HOLD) ∙ FLIP) ∙ FLIP) = FLIP`
## Recursive Reducers
```
function recursiveReduce(monoid, sequence) {
if (sequence.length === 0) {
return monoid.identity;
}
if (sequence.length === 1) {
return sequence[0];
}
const middle = Math.floor(sequence.length / 2);
const left = recursiveReduce(monoid, new View(sequence, 0, middle));
const right = recursiveReduce(monoid, new View(sequence, middle, sequence.length));
return monoid.combine(left, right);
}
```
* Example Recursion:
```
In: [OFF ∙ ON ∙ FLIP ∙ HOLD]
Out: OFF
/ \
/ \
In: [OFF, ON] In: [FLIP, HOLD]
Out: ON Out: FLIP
/ \ / \
/ \ / \
In: [OFF] In: [ON] In: [FLIP] In: [HOLD]
Out: OFF Out: ON Out: FLIP Out: HOLD
```
* Example Evaluation: `(FLIP ∙ HOLD) ∙ (FLIP ∙ FLIP) = FLIP`
## Parallel Reducers (Hybrid Idea)
* Pictorially:
```
Thread 0
In: Entire List
//\\
……
/ / \ \
/ / \ \
Thread 1 Thread 2 Thread 3 … Thread n
In: 1st Part In: 2nd Part In: 3rd Part … In: nth Part
```
* In Code:
```
async function parallelReduce(monoid, sequence, threadCount) {
const jobs = [];
for (let threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
const low = Math.floor(threadIndex * sequence.length / threadCount);
const high = Math.floor((threadIndex + 1) * sequence.length / threadCount);
const job = dispatch(monoid, sequence.slice(low, high));
jobs.push(job);
}
return iterativeReduce(monoid, await Promise.all(jobs));
}
```
--------------------------------------------------------------------------------
# Parallel List Summation
Problem: Given a list of numbers, compute their total.
## Monoid Identification
* Interpretation of input elements as actions:
*
* Combination of two actions:
*
* Representation of actions:
*
* Identity element:
*
* Monoid:
* `A = (…, …, …)`
--------------------------------------------------------------------------------
# Parallel List Multiplication
Problem: Given a list of numbers, compute their product.
## Monoid Identification
* Interpretation of input elements as actions:
*
* Combination of two actions:
*
* Representation of actions:
*
* Identity element:
*
* Monoid:
* `A = (…, …, …)`
--------------------------------------------------------------------------------
# Parallel Minimization
Problem: Given a list of numbers, compute the minimum.
## Monoid Identification
* Interpretation of input elements as actions:
*
* Combination of two actions:
*
* Representation of actions:
*
* Identity element:
*
* Monoid:
* `A = (…, …, …)`
--------------------------------------------------------------------------------
# Parallel Averaging
Problem: Given a list of numbers, compute the average.
## Obstacle
It is not clear how to interpret the input elements as actions.
As another way to look at the problem, if we know the average of a left-hand side and the average of a right-hand side, there is no reliable way to combine those averages. For example:
* The average of `[1, 5]` is `3`, and the average of `[6]` is `6`, so `3 ∙ 6` should be the average of `[1, 5, 6]`, or `4`.
* But the average of `[3]` is `3`, and the average of `[4, 8]` is `6`, so `3 ∙ 6` should also be the average of `[3, 4, 8]`, or `5`.
This is the same issue we saw in divide-and-conquer design: we must be asking the wrong question.
If we ask for …, deferring …, then we can find a monoid.
## Monoid Identification
* Interpretation of input elements as actions:
*
* Combination of two actions:
*
*
* Representation of actions:
*
* Identity element:
*
* Monoid:
* `A = (…, ∙, …)` where `(a, b) ∙ (c, d) = (…, …)`
--------------------------------------------------------------------------------
# Parallel Parentheses Balancing
Problem: Given a string of parentheses, determine if the parentheses are balanced.
* Example of balanced parentheses: `()(())`
* Example of unbalanced parentheses: `…`
* Example of unbalanced parentheses with same number of each type: `…`
## Clues about the Monoid from an Iterative Design
```
function isBalanced(parentheses) {
let nesting = 0;
for (const character of parentheses) {
if (character === '(') {
++nesting;
} else if (character === ')') {
--nesting;
if (nesting < 0) {
return false;
}
}
}
return nesting === 0;
}
```
## Clues about the Monoid from Generated Submonoids
* Taking the submonoid generated by `(`:
* The identity is different than `(`, which is different than `((`, which is different than `(((`, etc.
* So it seems like our monoid will need ….
* Taking the submonoid generated by `)`:
* The identity is different than `)`, which is different than `))`, which is different than `)))`, etc.
* So it seems like our monoid will need ….
## Quotienting a Free Monoid by Relations
* The sequence `…` cancels to the identity.
* If we simplify any input by repeatedly removing occurrences of `()`, we eventually end up with a string of the form ….
## Parentheses Monoid
* Monoid:
* `P = (…, ∙, …)` where `(a, b) ∙ (c, d) = (…, …)`
## Divide-and-Conquer Design
```
In: '()(())'
Out:
/ \
/ \
/ \
In: '()(' In: '())'
Out: Out:
/ \ / \
/ \ / \
In: '(' In: ')(' In: '(' In: '))'
Out: Out: Out: Out:
/ \ / \
/ \ / \
In: ')' In: '(' In: ')' In: ')'
Out: Out: Out: Out:
```
## New Iterative Solution
```
function encode(character) {
if (character === '(') {
return […, …];
}
if (character === ')') {
return […, …];
}
return [0, 0];
}
function combine([leftCloses, leftOpens], [rightCloses, rightOpens]) {
const cancellations = …;
return […, …];
}
function iterativeReduce(parentheses) {
let result = …;
for (const element of …) {
result = combine(result, element);
}
return result;
}
```
## Parallel Solution
```
async function parallelReduce(parentheses, threadCount) {
const jobs = [];
for (let threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
const low = Math.floor(threadIndex * parentheses.length / threadCount);
const high = Math.floor((threadIndex + 1) * parentheses.length / threadCount);
const job = dispatch(parentheses.slice(low, high));
jobs.push(job);
}
const [closes, opens] = iterativeReduce(await Promise.all(jobs));
return ;
}
```
* This type of parallel algorithm is called a *map-reduce* algorithm.
* (Modern map-reduce frameworks also support a *shuffle step* for rearranging
data between the map and reduce steps.)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment