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

Initial commit.

parents
Branches main
No related tags found
No related merge requests found
Showing
with 432 additions and 0 deletions
# Disable line-ending conversions for this repository.
* -text
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:csce-310/eslint-config.git
# Example Code
A minimal project giving examples of map-reduce algorithms for the 250 section
of the CSCE 310 course at UNL.
# Quick Start
Recursively clone this repository and `cd` into the root folder:
```
$ git clone --recursive git@git.unl.edu:csce-310/map-reduce-examples.git
$ cd map-reduce-examples
```
(If you forget `--recursive` when cloning, you can `cd` into your clone and run
`git submodule update --init --recursive` instead.)
Install dependencies:
```
$ npm install
```
Subproject commit 24df42fb655d234b83c93b0fb24d012e4d9ecb58
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.eol": "LF",
"files.exclude": {
"**/node_modules": true
},
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
}
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
Source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "@unlsoft/map-reduce",
"version": "1.0.0",
"description": "Examples of map-reduce algorithms.",
"type": "module",
"private": true,
"license": "UNLICENSED",
"scripts": {
"lint:js": "eslint --max-warnings 0 ./src",
"lint": "run-s --continue-on-error lint:**",
"test-once:sell": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"the sell monoid\"",
"test-once:buySell": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"buy/sell monoid\"",
"test-once:parentheses": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"parentheses monoid\"",
"test-once:pipes": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"pipes monoid\"",
"test-once:indexOfMinimum": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"index-of-minimum monoid\"",
"test-once:strings": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage -t \"strings monoid\"",
"test-once": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watchAll --coverage"
},
"dependencies": {
"npm-run-all": "^4.1.5"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"eslint": "^7.30.0",
"jest": "^27.0.6",
"jest-environment-node": "^27.0.6"
},
"eslintConfig": {
"extends": "@unlsoft"
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/**/*.js",
"!src/testing/**/*.js"
],
"resetMocks": false,
"restoreMocks": false,
"testEnvironment": "./src/testing/failFast.js",
"transform": {}
},
"//": [
"See https://github.com/facebook/jest/issues/9430 for information on Jest+ES6."
]
}
// A map-reduce algorithm implementing the solution to Question 2 from the
// recitation at <https://canvas.unl.edu/courses/114253/files/10772858>.
class MonoidElement {
constructor(buyPrice, sellPrice, minimumPrice, maximumPrice) {
this.buyPrice = buyPrice;
this.sellPrice = sellPrice;
this.minimumPrice = minimumPrice;
this.maximumPrice = maximumPrice;
}
get profit() {
return this.sellPrice - this.buyPrice;
}
}
export const IDENTITY_ELEMENT = new MonoidElement(Infinity, -Infinity, Infinity, -Infinity);
export function encodeAsMonoidElement(price) {
return new MonoidElement(price, price, price, price);
}
export function combineMonoidElements(left, right) {
const minimumPrice = Math.min(left.minimumPrice, right.minimumPrice);
const maximumPrice = Math.max(left.maximumPrice, right.maximumPrice);
const buyAndSellOnLeft = new MonoidElement(
left.buyPrice,
left.sellPrice,
minimumPrice,
maximumPrice,
);
let best = buyAndSellOnLeft;
const buyOnLeftAndSellOnRight = new MonoidElement(
left.minimumPrice,
right.maximumPrice,
minimumPrice,
maximumPrice,
);
if (buyOnLeftAndSellOnRight.profit > best.profit) {
best = buyOnLeftAndSellOnRight;
}
const buyAndSellOnRight = new MonoidElement(
right.buyPrice,
right.sellPrice,
minimumPrice,
maximumPrice,
);
if (buyAndSellOnRight.profit > best.profit) {
best = buyAndSellOnRight;
}
return best;
}
/* eslint-disable no-magic-numbers */
import { parallelMapReduce } from './mapReduce.js';
describe('map-reduce with the buy/sell monoid', () => {
test('finds the optimal buy and sell prices from the example forecast with one thread', async() => {
const result = await parallelMapReduce('./buySell.js', [103, 101, 102, 100], 1);
expect(result.buyPrice).toBe(101);
expect(result.sellPrice).toBe(102);
});
test('finds the optimal buy and sell prices from the example forecast with two threads', async() => {
const result = await parallelMapReduce('./buySell.js', [103, 101, 102, 100], 2);
expect(result.buyPrice).toBe(101);
expect(result.sellPrice).toBe(102);
});
test('finds the optimal buy and sell prices from the example forecast with three threads', async() => {
const result = await parallelMapReduce('./buySell.js', [103, 101, 102, 100], 3);
expect(result.buyPrice).toBe(101);
expect(result.sellPrice).toBe(102);
});
});
// A map-reduce algorithm implementing the solution to Question 2 from the
// recitation at <https://canvas.unl.edu/courses/114253/files/10850616>.
class MonoidElement {
constructor(indexOfMinimum, count, valueOfMinimum) {
this.indexOfMinimum = indexOfMinimum;
this.count = count;
this.valueOfMinimum = valueOfMinimum;
}
}
export const IDENTITY_ELEMENT = new MonoidElement(0, 0, Infinity);
export function encodeAsMonoidElement(element) {
return new MonoidElement(0, 1, element);
}
export function combineMonoidElements(left, right) {
if (left.valueOfMinimum <= right.valueOfMinimum) {
return new MonoidElement(left.indexOfMinimum, left.count + right.count, left.valueOfMinimum);
}
return new MonoidElement(left.count + right.indexOfMinimum, left.count + right.count, right.valueOfMinimum);
}
/* eslint-disable no-magic-numbers */
import { parallelMapReduce } from './mapReduce.js';
describe('map-reduce with the index-of-minimum monoid', () => {
test('finds the index of the minimum of the example sequence with one thread', async() => {
expect((await parallelMapReduce('./indexOfMinimum.js', [4, 2, 3, 4, 2], 1)).indexOfMinimum).toBe(1);
});
test('finds the index of the minimum of the example sequence with two threads', async() => {
expect((await parallelMapReduce('./indexOfMinimum.js', [4, 2, 3, 4, 2], 2)).indexOfMinimum).toBe(1);
});
test('finds the index of the minimum of the example sequence with three threads', async() => {
expect((await parallelMapReduce('./indexOfMinimum.js', [4, 2, 3, 4, 2], 3)).indexOfMinimum).toBe(1);
});
});
import { Worker } from 'worker_threads';
class ModuleWorker extends Worker {
constructor(path) {
const code = `import '${new URL(path, import.meta.url).href}';`;
super(new URL(`data:text/javascript,${encodeURIComponent(code)}`));
}
dispatch(data) {
this.postMessage(data);
return new Promise((resolve) => this.once('message', resolve));
}
}
function iterativeReduce(identityElement, combineMonoidElements, sequence) {
let result = identityElement;
for (const element of sequence) {
result = combineMonoidElements(result, element);
}
return result;
}
export async function parallelMapReduce(proceduresURL, sequence, workerCount) {
console.assert(workerCount > 0, `Cannot distribute work to a nonpositive (${workerCount}) number of threads.`);
const {
IDENTITY_ELEMENT,
combineMonoidElements,
} = await import(proceduresURL);
const workers = [...Array(workerCount)].map(() => new ModuleWorker('./worker.js'));
const jobs = workers.map((worker, workerIndex) => {
const low = Math.floor(workerIndex * sequence.length / workerCount);
const high = Math.floor((workerIndex + 1) * sequence.length / workerCount);
return worker.dispatch({
proceduresURL,
data: [...sequence.slice(low, high)],
});
});
const result = iterativeReduce(IDENTITY_ELEMENT, combineMonoidElements, await Promise.all(jobs));
for (const worker of workers) {
worker.terminate();
}
return result;
}
// A map-reduce algorithm implementing the parentheses monoid from class.
class MonoidElement {
constructor(closeParenthesesCount, openParenthesesCount) {
this.closeParenthesesCount = closeParenthesesCount;
this.openParenthesesCount = openParenthesesCount;
}
get balanced() {
return this.closeParenthesesCount === 0 && this.openParenthesesCount === 0;
}
}
export const IDENTITY_ELEMENT = new MonoidElement(0, 0);
export function encodeAsMonoidElement(character) {
switch (character) {
case ')':
return new MonoidElement(1, 0);
case '(':
return new MonoidElement(0, 1);
default:
return IDENTITY_ELEMENT;
}
}
export function combineMonoidElements(left, right) {
const cancellations = Math.min(left.openParenthesesCount, right.closeParenthesesCount);
return new MonoidElement(
left.closeParenthesesCount + right.closeParenthesesCount - cancellations,
left.openParenthesesCount + right.openParenthesesCount - cancellations,
);
}
/* eslint-disable no-magic-numbers */
import { parallelMapReduce } from './mapReduce.js';
describe('map-reduce with the parentheses monoid', () => {
test('summarizes the example balanced string with one thread', async() => {
expect((await parallelMapReduce('./parentheses.js', '()(())', 1)).balanced).toBe(true);
});
test('summarizes the example balanced string with two threads', async() => {
expect((await parallelMapReduce('./parentheses.js', '()(())', 2)).balanced).toBe(true);
});
test('summarizes the example balanced string with three threads', async() => {
expect((await parallelMapReduce('./parentheses.js', '()(())', 3)).balanced).toBe(true);
});
test('summarizes the example imbalanced string with one thread', async() => {
expect((await parallelMapReduce('./parentheses.js', '())))(()', 1)).balanced).toBe(false);
});
test('summarizes the example imbalanced string with two threads', async() => {
expect((await parallelMapReduce('./parentheses.js', '())))(()', 1)).balanced).toBe(false);
});
test('summarizes the example imbalanced string with three threads', async() => {
expect((await parallelMapReduce('./parentheses.js', '())))(()', 1)).balanced).toBe(false);
});
});
// A map-reduce algorithm implementing the solution to Question 1 from the
// recitation at <https://canvas.unl.edu/courses/114253/files/10850616>.
class MonoidElement {
constructor(warningCounts, reversesSampling) {
this.warningCounts = warningCounts;
this.reversesSampling = reversesSampling;
}
}
export const IDENTITY_ELEMENT = new MonoidElement([0, 0], false);
export function encodeAsMonoidElement(logMessage) {
switch (logMessage) {
case 'X':
return new MonoidElement([0, 0], true);
case 'Y':
return new MonoidElement([1, 0], false);
default:
return IDENTITY_ELEMENT;
}
}
export function combineMonoidElements(left, right) {
const adjustedRightWarningCounts = left.reversesSampling ? [...right.warningCounts].reverse() : right.warningCounts;
const totals = [];
for (let i = 0; i < 2; ++i) {
totals.push(left.warningCounts[i] + adjustedRightWarningCounts[i]);
}
return new MonoidElement(totals, left.reversesSampling ^ right.reversesSampling);
}
/* eslint-disable no-magic-numbers */
import { parallelMapReduce } from './mapReduce.js';
describe('map-reduce with the pipes monoid', () => {
test('summarizes the example log with one thread', async() => {
expect((await parallelMapReduce('./pipes.js', 'XYYXY', 1)).warningCounts).toEqual([1, 2]);
});
test('summarizes the example log with two threads', async() => {
expect((await parallelMapReduce('./pipes.js', 'XYYXY', 2)).warningCounts).toEqual([1, 2]);
});
test('summarizes the example log with three threads', async() => {
expect((await parallelMapReduce('./pipes.js', 'XYYXY', 3)).warningCounts).toEqual([1, 2]);
});
});
// A map-reduce algorithm implementing the solution to Question 1 from the
// recitation at <https://canvas.unl.edu/courses/114253/files/10772858>.
class MonoidElement {
constructor(maximumCumulativeChange, totalChange) {
this.maximumCumulativeChange = maximumCumulativeChange;
this.totalChange = totalChange;
}
}
export const IDENTITY_ELEMENT = new MonoidElement(-Infinity, 0);
export function encodeAsMonoidElement(change) {
return new MonoidElement(Math.max(change, 0), change);
}
export function combineMonoidElements(left, right) {
return new MonoidElement(
Math.max(
left.maximumCumulativeChange,
left.totalChange + right.maximumCumulativeChange,
),
left.totalChange + right.totalChange,
);
}
/* eslint-disable no-magic-numbers */
import { parallelMapReduce } from './mapReduce.js';
describe('map-reduce with the sell monoid', () => {
test('finds the maximum cumulative change from the example forecast with one thread', async() => {
expect((await parallelMapReduce('./sell.js', [-1, 4, -3, 2], 1)).maximumCumulativeChange).toBe(3);
});
test('finds the maximum cumulative change from the example forecast with one thread', async() => {
expect((await parallelMapReduce('./sell.js', [-1, 4, -3, 2], 2)).maximumCumulativeChange).toBe(3);
});
test('finds the maximum cumulative change from the example forecast with one thread', async() => {
expect((await parallelMapReduce('./sell.js', [-1, 4, -3, 2], 3)).maximumCumulativeChange).toBe(3);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment