From ef3ea6d104d24a072cfa856e2f73b28126dfd0e3 Mon Sep 17 00:00:00 2001 From: Brady James Garvin <bgarvin@cse.unl.edu> Date: Tue, 23 Oct 2018 06:45:01 -0500 Subject: [PATCH] Introduced starter code for Homework 3.6. --- index.html | 1 + js/heat_map.js | 86 +++++++++++++++++++++++++++++++ unit_tests/test_heat_map.js | 100 ++++++++++++++++++++++++++++++++++++ unit_tests/unit_tests.html | 3 ++ 4 files changed, 190 insertions(+) create mode 100644 js/heat_map.js create mode 100644 unit_tests/test_heat_map.js diff --git a/index.html b/index.html index a65528b..cf7d809 100644 --- a/index.html +++ b/index.html @@ -30,6 +30,7 @@ <script src="js/undirected_graph.js"></script> <script src="js/transit.js"></script> <script src="js/patching.js"></script> + <script src="js/heat_map.js"></script> <script src="js/positioned_graph.js"></script> <script src="js/throttled_simulation.js"></script> <script src="js/visualization_style.js"></script> diff --git a/js/heat_map.js b/js/heat_map.js new file mode 100644 index 0000000..8a0ad23 --- /dev/null +++ b/js/heat_map.js @@ -0,0 +1,86 @@ +/* exported computeHeatMap */ + +class EdgeLabeledGraph { + constructor(vertices, defaultLabel) { + const verticesList = [...vertices]; // copy the vertices to a list in case they can only be iterated over once + this.edges = new Map(); + for (const source of verticesList) { + const adjacencies = new Map(); + for (const destination of verticesList) { + adjacencies.set(destination, defaultLabel); + } + this.edges.set(source, adjacencies); + } + } + + get vertices() { + return this.edges.keys(); + } + + getLabel(source, destination) { + return this.edges.get(source).get(destination); + } + + setLabel(source, destination, label) { + return this.edges.get(source).set(destination, label); + } + + capLabel(source, destination, label) { + const adjacencies = this.edges.get(source); + adjacencies.set(destination, Math.min(adjacencies.get(destination), label)); + } + + increaseLabel(source, destination, increase) { + const adjacencies = this.edges.get(source); + adjacencies.set(destination, adjacencies.get(destination) + increase); + } +} + +function computeTransitGraph(city) { + return undefined; // TODO: stub +} + +// Preliminaries: +// W[u][v] is the (possibly infinite) weight from u to v. +// +// Standard Floyd–Warshall Recurrence: +// D^(i)[u][v] is the distance from u to v using only the first i vertices as intermediates, so +// D^(n)[u][v] is the distance from u to v. +// +// D^(0)[u][v] = … +// D^(i)[u][v] = … for 1 ≤ i ≤ n +// +// Recurrence for Shortest-Path Successors: +// S^(i)[u][v] is a vertex that immediately follows u in a shortest path from u to v using only the first i vertices as intermediates, so +// S^(n)[u][v] is a vertex that immediately follows u in a shortest path from u to v. +// +// S^(0)[u][v] = … if … +// … otherwise +// S^(i)[u][v] = … if … +// … if … +// … otherwise for 1 ≤ i ≤ n +function computeShortestPathSuccessors(transitGraph) { + return undefined; // TODO: stub +} + +// Preliminaries: +// S^(n)[u][v] is defined as above computeShortestPathSuccessors. +// +// Recurrence for Traffic Matrix: +// T^(i)[u][v] is the number of distinct paths that visit a vertex \(u\) in their first \(i\) steps while traveling to a final destination \(v\), so +// T^(n-1)[u][v] is the number of distinct paths that travel via \(u\) to their final destination \(v\). +// +// T^(0)[u][v] = 1 +// T^(i)[u][v] = 1 + sum_{w|S^(n)[w][v]=u} T^(i-1)[w][v] for 1 ≤ i ≤ n +// +function computeTrafficMatrix(successors) { + return undefined; // TODO: stub +} + +function computeHeatFromTraffic(traffic) { + return undefined; // TODO: stub +} + +function computeHeatMap(city) { + return undefined; // TODO: stub +} diff --git a/unit_tests/test_heat_map.js b/unit_tests/test_heat_map.js new file mode 100644 index 0000000..d897773 --- /dev/null +++ b/unit_tests/test_heat_map.js @@ -0,0 +1,100 @@ +QUnit.module('heat_map.js'); +/* globals QUnit Vertex UndirectedEdge UndirectedGraph City Route Bus EdgeLabeledGraph */ +/* globals computeTransitGraph computeShortestPathSuccessors computeTrafficMatrix computeHeatFromTraffic computeHeatMap */ +/* eslint-disable no-magic-numbers */ + +// This reformatting is not necessary, but makes the output from failed tests easier to read. +function toTriples(edgeLabeledGraph) { + const accumulator = []; + for (const [source, adjacencies] of edgeLabeledGraph.edges) { + for (const [destination, label] of adjacencies) { + accumulator.push([source, destination, label]); + } + } + let result = ''; + for (const triple of accumulator.sort()) { + result += `(${triple}); `; + } + return result.slice(0, -2); +} + +// Building graphs from strings is not necessary, but makes tests easier to write. +function fromTriples(triplesString) { + const triples = triplesString.split('; ').map((triple) => triple.slice(1, -1).split(',')); + const vertices = new Set(); + for (const [source, destination, _] of triples) { + vertices.add(source); + vertices.add(destination); + } + const result = new EdgeLabeledGraph(vertices, undefined); + for (const [source, destination, label] of triples) { + let cast = Number(label); + if (label === '') { + cast = undefined; + } else if (Number.isNaN(cast)){ + cast = label; + } + result.setLabel(source, destination, cast); + } + return result; +} + +const CITY_FOR_SMOKE_TESTS = (() => { + const a = new Vertex('a'); + const b = new Vertex('b'); + const c = new Vertex('c'); + const walkGraph = new UndirectedGraph(); + walkGraph.addVertex(a); + walkGraph.addVertex(b); + walkGraph.addVertex(c); + walkGraph.addEdge(a, new UndirectedEdge(2.0), b); + walkGraph.addEdge(a, new UndirectedEdge(4.0), c); + walkGraph.addEdge(b, new UndirectedEdge(8.0), c); + const driveGraph = new UndirectedGraph(); + driveGraph.addVertex(a); + driveGraph.addVertex(b); + driveGraph.addVertex(c); + driveGraph.addEdge(a, new UndirectedEdge(1.0), c); + const city = new City(walkGraph, driveGraph); + const route = new Route(city, a, c); + const x = new Bus(route.getArc(a)); // eslint-disable-line no-unused-vars + const y = new Bus(route.getArc(c)); // eslint-disable-line no-unused-vars + return city; +})(); + +QUnit.test('smoke test computeTransitGraph', (assert) => { + const transitGraph = computeTransitGraph(CITY_FOR_SMOKE_TESTS); + assert.deepEqual(toTriples(transitGraph), '(a,a,Infinity); (a,b,2); (a,c,2); (b,a,2); (b,b,Infinity); (b,c,8); (c,a,2); (c,b,8); (c,c,Infinity)'); +}); + +QUnit.test('smoke test computeShortestPathSuccessors', (assert) => { + const transitGraph = fromTriples('(a,a,Infinity); (a,b,2); (a,c,2); (b,a,2); (b,b,Infinity); (b,c,8); (c,a,2); (c,b,8); (c,c,Infinity)'); + const successors = computeShortestPathSuccessors(transitGraph); + assert.deepEqual(toTriples(successors), '(a,a,); (a,b,b); (a,c,c); (b,a,a); (b,b,); (b,c,a); (c,a,a); (c,b,a); (c,c,)'); +}); + +QUnit.test('smoke test computeTrafficMatrix', (assert) => { + const successors = fromTriples('(a,a,); (a,b,b); (a,c,c); (b,a,a); (b,b,); (b,c,a); (c,a,a); (c,b,a); (c,c,)'); + const traffic = computeTrafficMatrix(successors); + assert.deepEqual(toTriples(traffic), '(a,a,3); (a,b,2); (a,c,2); (b,a,1); (b,b,3); (b,c,1); (c,a,1); (c,b,1); (c,c,3)'); +}); + +QUnit.test('smoke test computeHeatFromTraffic', (assert) => { + const traffic = fromTriples('(a,a,3); (a,b,2); (a,c,2); (b,a,1); (b,b,3); (b,c,1); (c,a,1); (c,b,1); (c,c,3)'); + const heat = computeHeatFromTraffic(traffic); + assert.deepEqual(heat.size, 3); + assert.deepEqual(heat.get('a'), 7); // fromTriples will give us string keys, not vertex keys + assert.deepEqual(heat.get('b'), 5); + assert.deepEqual(heat.get('c'), 5); +}); + +QUnit.test('smoke test computeHeatMap', (assert) => { + const a = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'a'); + const b = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'b'); + const c = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'c'); + const heat = computeHeatMap(CITY_FOR_SMOKE_TESTS); + assert.deepEqual(heat.size, 3); + assert.deepEqual(heat.get(a), 7); + assert.deepEqual(heat.get(b), 5); + assert.deepEqual(heat.get(c), 5); +}); diff --git a/unit_tests/unit_tests.html b/unit_tests/unit_tests.html index 1ff7e4d..4681968 100644 --- a/unit_tests/unit_tests.html +++ b/unit_tests/unit_tests.html @@ -42,6 +42,9 @@ <script src="../js/patching.js"></script> <script src="test_patching.js"></script> + <script src="../js/heat_map.js"></script> + <script src="test_heat_map.js"></script> + <script src="../js/positioned_graph.js"></script> <script src="test_positioned_graph.js"></script> -- GitLab