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

Initial commit.

parents
Branches
Tags
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
```js
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
```js
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:
```js
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:
* `P = (…, …, …)`
--------------------------------------------------------------------------------
# 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:
* `M = (…, …, …)`
--------------------------------------------------------------------------------
# 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
```js
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
```js
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
```js
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