Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 1193466

Browse files
committed
Port over needed code from JSNetworkX
1 parent 336aaf7 commit 1193466

2 files changed

Lines changed: 316 additions & 3 deletions

File tree

runestone/parsons/js/dagGrader.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import LineBasedGrader from "./lineGrader";
2-
import { DiGraph } from "jsnetworkx/node/classes";
3-
import { hasPath } from "jsnetworkx/node/algorithms/shortestPaths/generic";
4-
import { isDirectedAcyclicGraph } from "jsnetworkx/node/algorithms/dag";
2+
// import { DiGraph } from "jsnetworkx/node/classes";
3+
// import { hasPath } from "jsnetworkx/node/algorithms/shortestPaths/generic";
4+
// import { isDirectedAcyclicGraph } from "jsnetworkx/node/algorithms/dag";
5+
6+
import { DiGraph, hasPath, isDirectedAcyclicGraph } from "./dagHelpers"
57

68
function graphToNX(answerLines) {
79
var graph = new DiGraph();

runestone/parsons/js/dagHelpers.js

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
/**
2+
* This file adapted from JSNetworkX: https://github.com/fkling/JSNetworkX
3+
* Copyright (C) 2012 Felix Kling <felix.kling@gmx.net>
4+
* JSNetworkX is distributed with the BSD license
5+
*/
6+
7+
export function hasPath(G, {source, target}) {
8+
try {
9+
shortestPath(G, {source, target});
10+
} catch(error) {
11+
if (error instanceof JSNetworkXNoPath) {
12+
return false;
13+
}
14+
throw error;
15+
}
16+
return true;
17+
}
18+
19+
function shortestPath(G, {source, target, weight}={}) {
20+
return bidirectionalShortestPath(G, source, target);
21+
}
22+
23+
function bidirectionalShortestPath(G, source, target) {
24+
// call helper to do the real work
25+
var [pred, succ, w] = bidirectionalPredSucc(G, source, target);
26+
27+
// build path from pred+w+succ
28+
var path = [];
29+
// from source to w
30+
while (w != null) {
31+
path.push(w);
32+
w = pred.get(w);
33+
}
34+
w = succ.get(path[0]);
35+
path.reverse();
36+
// from w to target
37+
while (w != null) {
38+
path.push(w);
39+
w = succ.get(w);
40+
}
41+
return path;
42+
}
43+
44+
function bidirectionalPredSucc(G, source, target) {
45+
// does BFS from both source and target and meets in the middle
46+
if (nodesAreEqual(source, target)) {
47+
return [new Map([[source, null]]), new Map([[target, null]]), source];
48+
}
49+
50+
// handle either directed or undirected
51+
var gpred, gsucc;
52+
gpred = G.predecessorsIter.bind(G);
53+
gsucc = G.successorsIter.bind(G);
54+
55+
// predecesssor and successors in search
56+
var pred = new Map([[source, null]]);
57+
var succ = new Map([[target, null]]);
58+
//
59+
// initialize fringes, start with forward
60+
var forwardFringe = [source];
61+
var reverseFringe = [target];
62+
var thisLevel;
63+
64+
/*jshint newcap:false*/
65+
while (forwardFringe.length > 0 && reverseFringe.length > 0) {
66+
if (forwardFringe.length <= reverseFringe.length) {
67+
thisLevel = forwardFringe;
68+
forwardFringe = [];
69+
for (let v of thisLevel) {
70+
for (let w of gsucc(v)) {
71+
if (!pred.has(w)) {
72+
forwardFringe.push(w);
73+
pred.set(w, v);
74+
}
75+
if (succ.has(w)) {
76+
return [pred, succ, w]; // found path
77+
}
78+
}
79+
}
80+
}
81+
else {
82+
thisLevel = reverseFringe;
83+
reverseFringe = [];
84+
for (let v of thisLevel) {
85+
for (let w of gpred(v)) {
86+
if (!succ.has(w)) {
87+
reverseFringe.push(w);
88+
succ.set(w, v);
89+
}
90+
if (pred.has(w)) {
91+
return [pred, succ, w]; // found path
92+
}
93+
}
94+
}
95+
}
96+
}
97+
throw new JSNetworkXNoPath(sprintf(
98+
'No path between `%j` and `%j`.',
99+
source,
100+
target
101+
));
102+
}
103+
104+
function topologicalSort(G, optNbunch) {
105+
// nonrecursive version
106+
var seen = new Set();
107+
var orderExplored = []; // provide order and
108+
// fast search without more general priorityDictionary
109+
var explored = new Set();
110+
111+
if (optNbunch == null) {
112+
optNbunch = G.nodesIter();
113+
}
114+
115+
optNbunch.forEach(function(v) { // process all vertices in G
116+
if (explored.has(v)) {
117+
return; // continue
118+
}
119+
120+
var fringe = [v]; // nodes yet to look at
121+
while (fringe.length > 0) {
122+
var w = fringe[fringe.length - 1]; // depth first search
123+
if (explored.has(w)) { // already looked down this branch
124+
fringe.pop();
125+
continue;
126+
}
127+
seen.add(w); // mark as seen
128+
// Check successors for cycles for new nodes
129+
var newNodes = [];
130+
/*eslint-disable no-loop-func*/
131+
G.get(w).forEach(function(_, n) {
132+
if (!explored.has(n)) {
133+
if (seen.has(n)) { // CYCLE !!
134+
throw new JSNetworkXUnfeasible('Graph contains a cycle.');
135+
}
136+
newNodes.push(n);
137+
}
138+
});
139+
/*eslint-enable no-loop-func*/
140+
if (newNodes.length > 0) { // add new nodes to fringe
141+
fringe.push.apply(fringe, newNodes);
142+
}
143+
else {
144+
explored.add(w);
145+
orderExplored.unshift(w);
146+
}
147+
}
148+
});
149+
150+
return orderExplored;
151+
}
152+
153+
export function isDirectedAcyclicGraph(G) {
154+
try {
155+
topologicalSort(G);
156+
return true;
157+
}
158+
catch(ex) {
159+
if (ex instanceof JSNetworkXUnfeasible) {
160+
return false;
161+
}
162+
throw ex;
163+
}
164+
}
165+
166+
export class DiGraph {
167+
168+
constructor() {
169+
this.graph = {}; // dictionary for graph attributes
170+
this.node = new Map(); // dictionary for node attributes
171+
// We store two adjacency lists:
172+
// the predecessors of node n are stored in the dict self.pred
173+
// the successors of node n are stored in the dict self.succ=self.adj
174+
this.adj = new Map(); // empty adjacency dictionary
175+
this.pred = new Map(); // predecessor
176+
this.succ = this.adj; // successor
177+
178+
this.edge = this.adj;
179+
}
180+
181+
addNode(n) {
182+
if(!this.succ.has(n)) {
183+
this.succ.set(n, new Map());
184+
this.pred.set(n, new Map());
185+
this.node.set(n);
186+
}
187+
}
188+
189+
addEdge(u, v) {
190+
// add nodes
191+
if (!this.succ.has(u)) {
192+
this.succ.set(u, new Map());
193+
this.pred.set(u, new Map());
194+
this.node.set(u, {});
195+
}
196+
197+
if (!this.succ.has(v)) {
198+
this.succ.set(v, new Map());
199+
this.pred.set(v, new Map());
200+
this.node.set(v, {});
201+
}
202+
203+
// add the edge
204+
var datadict = this.adj.get(u).get(v) || {};
205+
this.succ.get(u).set(v, datadict);
206+
this.pred.get(v).set(u, datadict);
207+
}
208+
209+
nodes(optData=false) {
210+
return Array.from(optData ? this.node.entries() : this.node.keys());
211+
}
212+
213+
edges(optNbunch, optData=false) {
214+
return Array.from(this.edgesIter(optNbunch, optData));
215+
}
216+
217+
nodesIter(optData=false) {
218+
if (optData) {
219+
return toIterator(this.node);
220+
}
221+
return this.node.keys();
222+
}
223+
224+
reverse(optCopy=true) {
225+
var H;
226+
if(optCopy) {
227+
H = new this.constructor(null, {name: 'Reverse of (' + this.name + ')'});
228+
H.addNodesFrom(this);
229+
H.addEdgesFrom(mapIterator(
230+
this.edgesIter(null, true),
231+
edge => tuple3c(edge[1], edge[0], deepcopy(edge[2]), edge)
232+
));
233+
H.graph = deepcopy(this.graph);
234+
H.node = deepcopy(this.node);
235+
}
236+
else {
237+
var thisPred = this.pred;
238+
var thisSucc = this.succ;
239+
240+
this.succ = thisPred;
241+
this.pred = thisSucc;
242+
this.adj = this.succ;
243+
H = this;
244+
}
245+
return H;
246+
}
247+
248+
successorsIter(n) {
249+
var nbrs = this.succ.get(n);
250+
if (nbrs !== undefined) {
251+
return nbrs.keys();
252+
}
253+
throw new JSNetworkXError(
254+
sprintf('The node "%j" is not in the digraph.', n)
255+
);
256+
}
257+
258+
predecessorsIter(n) {
259+
var nbrs = this.pred.get(n);
260+
if (nbrs !== undefined) {
261+
return nbrs.keys();
262+
}
263+
throw new JSNetworkXError(
264+
sprintf('The node "%j" is not in the digraph.', n)
265+
);
266+
}
267+
268+
successors(n) {
269+
return Array.from(this.successorsIter(n));
270+
}
271+
272+
predecessors(n) {
273+
return Array.from(this.predecessorsIter(n));
274+
}
275+
276+
}
277+
278+
class JSNetworkXException {
279+
constructor(message) {
280+
this.name = 'JSNetworkXException';
281+
this.message = message;
282+
}
283+
}
284+
285+
class JSNetworkXAlgorithmError extends JSNetworkXException {
286+
constructor(message) {
287+
super(message);
288+
this.name = 'JSNetworkXAlgorithmError';
289+
}
290+
}
291+
292+
class JSNetworkXError extends JSNetworkXException {
293+
constructor(message) {
294+
super(message);
295+
this.name = 'JSNetworkXError';
296+
}
297+
}
298+
299+
class JSNetworkXUnfeasible extends JSNetworkXAlgorithmError {
300+
constructor(message) {
301+
super(message);
302+
this.name = 'JSNetworkXUnfeasible';
303+
}
304+
}
305+
306+
class JSNetworkXNoPath extends JSNetworkXUnfeasible {
307+
constructor(message) {
308+
super(message);
309+
this.name = 'JSNetworkXNoPath';
310+
}
311+
}

0 commit comments

Comments
 (0)