Skip to content

Commit d420094

Browse files
committed
Replace old graph with new graph which supports fast node deletion
1 parent 6f358b2 commit d420094

File tree

4 files changed

+93
-142
lines changed

4 files changed

+93
-142
lines changed

package-lock.json

+1-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"body-parser": "^1.18.2",
1616
"esprima": "^4.0.0",
1717
"express": "^4.16.3",
18-
"graph-data-structure": "^1.8.0",
1918
"npm": "^5.10.0",
2019
"sloc": "^0.2.0",
2120
"source-map": "^0.7.3",

src/graph.js

+92-85
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,86 @@
1414
}
1515

1616
define(function (require, exports) {
17-
var graph = require("graph-data-structure");
17+
const { LinkedList } = require('./linkedList');
18+
19+
class BasicGraph {
20+
constructor() {
21+
this._pred = {};
22+
this._succ = {};
23+
}
24+
25+
addNode(node) {
26+
this._pred[node] = this.pred(node);
27+
this._succ[node] = this.succ(node);
28+
}
29+
30+
// Remove all associated edges
31+
// Does nothing if the node doesn not exist
32+
removeNode(node) {
33+
if (this._pred[node]) {
34+
for (let p of this._pred[node]) {
35+
this._succ[p].remove(node);
36+
}
37+
delete this._pred[node];
38+
}
39+
if (this._succ[node]) {
40+
for (let s of this._succ[node]) {
41+
this._pred[s].remove(node);
42+
}
43+
delete this._succ[node];
44+
}
45+
}
46+
47+
pred(node) {
48+
return this._pred[node] || new LinkedList();
49+
}
50+
51+
succ(node) {
52+
return this._succ[node] || new LinkedList();
53+
}
54+
55+
addEdge(u, v) {
56+
this.addNode(u);
57+
this.addNode(v);
58+
this._succ[u].add(v);
59+
this._pred[v].add(u);
60+
}
61+
62+
// Does not remove the nodes
63+
// Does nothing if the edge does not exist
64+
removeEdge(u, v) {
65+
if (this._succ[u]) {
66+
this._succ[u].remove(v);
67+
}
68+
if (this._pred[v]) {
69+
this._pred[v].remove(u);
70+
}
71+
}
72+
73+
nodes() {
74+
return Object.keys(this._pred);
75+
}
76+
77+
serialize() {
78+
let serialized = {
79+
nodes: this.nodes().map((id) => {
80+
return {id: id};
81+
}),
82+
links: []
83+
}
84+
85+
serialized.nodes.forEach((node) => {
86+
let source = node.id;
87+
for (let target of this._succ[source]) {
88+
serialized.links.push({
89+
source: source,
90+
target: target
91+
});
92+
}
93+
});
94+
return serialized;
95+
}
96+
}
1897

1998
function nodeToString(nd) {
2099
return nd.attr.pp();
@@ -23,7 +102,7 @@ define(function (require, exports) {
23102
var cf = nodeToString;
24103

25104
function Graph() {
26-
this.graph = new graph();
105+
this.graph = new BasicGraph();
27106
this.node_pairings = {};
28107
this.edge_annotations = {};
29108
}
@@ -55,70 +134,26 @@ define(function (require, exports) {
55134
this.addEdge(from, tos[i]);
56135
};
57136

58-
Graph.prototype.update = function (old_cf, new_nd) {
59-
if (!(old_cf in this.node_pairings) || cf(new_nd) == old_cf)
60-
return;
61-
62-
this.node_pairings[cf(new_nd)] = new_nd;
63-
delete this.node_pairings[old_cf];
64-
65-
this.graph.addNode(cf(new_nd));
66-
67-
let gs = this.graph.serialize();
68-
69-
for (let i = 0; i < gs['links'].length; i++) {
70-
if (gs['links'][i]['source'] == old_cf)
71-
this.graph.addEdge(cf(new_nd), gs['links'][i]['target']);
72-
73-
if (gs['links'][i]['target'] == old_cf)
74-
this.graph.addEdge(gs['links'][i]['source'], cf(new_nd));
75-
}
76-
this.graph.removeNode(old_cf);
77-
}
78-
79-
Graph.prototype.merge = function (graph) {
80-
let nodes = graph.getNodes();
81-
82-
for (let i = 0; i < nodes.length; i++) {
83-
this.addNode(nodes[i]);
84-
}
85-
86-
let gs = graph.serialize();
87-
88-
for (let i = 0; i < gs['links'].length; i++) {
89-
this.graph.addEdge(gs['links'][i]['source'], gs['links'][i]['target'])
90-
}
91-
};
92-
93137
Graph.prototype.iter = function (cb) {
94-
let edges = this.graph.serialize()['links'];
95-
96-
for (let i = 0; i < edges.length; i++) {
97-
let from = edges[i]['source'];
98-
let to = edges[i]['target'];
99-
100-
let from_nd = this.node_pairings[from];
101-
let to_nd = this.node_pairings[to];
102-
103-
cb(from_nd, to_nd);
104-
105-
this.update(from, from_nd);
106-
this.update(to, to_nd);
138+
const nodes = this.graph.nodes();
139+
for (let u of nodes) {
140+
for (let v of this.graph.succ(u)) {
141+
let u_nd = this.node_pairings[u];
142+
let v_nd = this.node_pairings[v];
143+
cb(u_nd, v_nd);
144+
}
107145
}
108146
};
109147

110148
Graph.prototype.hasEdge = function (from, to) {
111-
return this.graph.adjacent(cf(from)).indexOf(cf(to)) >= 0;
149+
return this.graph.succ(cf(from)).has(cf(to));
112150
};
113151

114152
Graph.prototype.succ = function (nd) {
115-
let adj = this.graph.adjacent(cf(nd));
116-
153+
let succ = this.graph.succ(cf(nd));
117154
let lst = [];
118-
119-
for (let i = 0; i < adj.length; i++)
120-
lst.push(this.node_pairings[adj[i]])
121-
155+
for (let s of succ)
156+
lst.push(this.node_pairings[s])
122157
return lst;
123158
}
124159

@@ -135,33 +170,6 @@ define(function (require, exports) {
135170
return false;
136171
};
137172

138-
/* Remove all outward edges of a node */
139-
Graph.prototype.removeOutEdges = function (nd) {
140-
if (this.hasNode(nd)){
141-
let adjacency = this.graph.adjacent(cf(nd));
142-
143-
for (let i = 0; i < adjacency.length; i++) {
144-
this.graph.removeEdge(cf(nd), adjacency[i]);
145-
}
146-
return true;
147-
}
148-
return false;
149-
};
150-
151-
/* Remove all inward edges of a node */
152-
Graph.prototype.removeInEdges = function (nd) {
153-
if (this.hasNode(nd)){
154-
let gs = this.graph.serialize();
155-
156-
for (let i = 0; i < gs['links'].length; i++) {
157-
if (gs['links'][i]['target'] == cf(nd))
158-
this.graph.removeEdge(gs['links'][i]['source'], cf(nd));
159-
}
160-
return true;
161-
}
162-
return false;
163-
};
164-
165173
/* Remove a node and all associated edges from graph */
166174
Graph.prototype.removeNode = function (nd) {
167175
if (this.hasNode(nd)) {
@@ -178,7 +186,6 @@ define(function (require, exports) {
178186
let cfn = nodes[i];
179187
let n = this.node_pairings[cfn];
180188
cb(n);
181-
this.update(cfn, n);
182189
}
183190
};
184191

tests/jest/graph.test.js

-50
Original file line numberDiff line numberDiff line change
@@ -313,55 +313,6 @@ test('Add many edges, remove the edges and verify they are all removed', () => {
313313
expect(G.hasEdge(nodes[i], nodes[i + 1])).toBe(false);
314314
});
315315

316-
test('Add edges, remove outgoing edges and verify they are removed', () => {
317-
const G = new graph.Graph();
318-
319-
G.addEdge(fv1, fv2);
320-
G.addEdge(fv2, fv1);
321-
322-
G.addEdge(fv1, fv3);
323-
G.addEdge(fv3, fv1);
324-
325-
G.addEdge(fv2, fv3);
326-
G.addEdge(fv3, fv2);
327-
328-
expect(G.removeOutEdges(fv1)).toBe(true);
329-
330-
expect(G.hasEdge(fv1, fv2)).toBe(false);
331-
expect(G.hasEdge(fv2, fv1)).toBe(true);
332-
333-
expect(G.hasEdge(fv1, fv3)).toBe(false);
334-
expect(G.hasEdge(fv3, fv1)).toBe(true);
335-
336-
expect(G.hasEdge(fv2, fv3)).toBe(true);
337-
expect(G.hasEdge(fv3, fv2)).toBe(true);
338-
339-
});
340-
341-
test('Add edges, remove ingoing edges and verify they are removed', () => {
342-
const G = new graph.Graph();
343-
344-
G.addEdge(fv1, fv2);
345-
G.addEdge(fv2, fv1);
346-
347-
G.addEdge(fv1, fv3);
348-
G.addEdge(fv3, fv1);
349-
350-
G.addEdge(fv2, fv3);
351-
G.addEdge(fv3, fv2);
352-
353-
expect(G.removeInEdges(fv1)).toBe(true);
354-
355-
expect(G.hasEdge(fv1, fv2)).toBe(true);
356-
expect(G.hasEdge(fv2, fv1)).toBe(false);
357-
358-
expect(G.hasEdge(fv1, fv3)).toBe(true);
359-
expect(G.hasEdge(fv3, fv1)).toBe(false);
360-
361-
expect(G.hasEdge(fv2, fv3)).toBe(true);
362-
expect(G.hasEdge(fv3, fv2)).toBe(true);
363-
});
364-
365316
test('Add nodes and verify they were added', () => {
366317
const G = new graph.Graph();
367318

@@ -390,7 +341,6 @@ test('Add nodes, remove them and verify they were removed', () => {
390341
expect(G.hasNode(fv3)).toBe(false);
391342
});
392343

393-
394344
test('Add nodes, add edge, verify graph has edges and nodes', () => {
395345
const G = new graph.Graph();
396346

0 commit comments

Comments
 (0)