Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dijkstra - support undirected graphs #115

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 88 additions & 66 deletions lib/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,67 +69,65 @@ Graph.prototype._nodeCount = 0;
/* Number of edges in the graph. Should only be changed by the implementation. */
Graph.prototype._edgeCount = 0;


/* === Graph functions ========= */

Graph.prototype.isDirected = function() {
Graph.prototype.isDirected = function () {
return this._isDirected;
};

Graph.prototype.isMultigraph = function() {
Graph.prototype.isMultigraph = function () {
return this._isMultigraph;
};

Graph.prototype.isCompound = function() {
Graph.prototype.isCompound = function () {
return this._isCompound;
};

Graph.prototype.setGraph = function(label) {
Graph.prototype.setGraph = function (label) {
this._label = label;
return this;
};

Graph.prototype.graph = function() {
Graph.prototype.graph = function () {
return this._label;
};


/* === Node functions ========== */

Graph.prototype.setDefaultNodeLabel = function(newDefault) {
Graph.prototype.setDefaultNodeLabel = function (newDefault) {
if (!_.isFunction(newDefault)) {
newDefault = _.constant(newDefault);
}
this._defaultNodeLabelFn = newDefault;
return this;
};

Graph.prototype.nodeCount = function() {
Graph.prototype.nodeCount = function () {
return this._nodeCount;
};

Graph.prototype.nodes = function() {
Graph.prototype.nodes = function () {
return _.keys(this._nodes);
};

Graph.prototype.sources = function() {
Graph.prototype.sources = function () {
var self = this;
return _.filter(this.nodes(), function(v) {
return _.filter(this.nodes(), function (v) {
return _.isEmpty(self._in[v]);
});
};

Graph.prototype.sinks = function() {
Graph.prototype.sinks = function () {
var self = this;
return _.filter(this.nodes(), function(v) {
return _.filter(this.nodes(), function (v) {
return _.isEmpty(self._out[v]);
});
};

Graph.prototype.setNodes = function(vs, value) {
Graph.prototype.setNodes = function (vs, value) {
var args = arguments;
var self = this;
_.each(vs, function(v) {
_.each(vs, function (v) {
if (args.length > 1) {
self.setNode(v, value);
} else {
Expand All @@ -139,7 +137,7 @@ Graph.prototype.setNodes = function(vs, value) {
return this;
};

Graph.prototype.setNode = function(v, value) {
Graph.prototype.setNode = function (v, value) {
if (_.has(this._nodes, v)) {
if (arguments.length > 1) {
this._nodes[v] = value;
Expand All @@ -161,23 +159,25 @@ Graph.prototype.setNode = function(v, value) {
return this;
};

Graph.prototype.node = function(v) {
Graph.prototype.node = function (v) {
return this._nodes[v];
};

Graph.prototype.hasNode = function(v) {
Graph.prototype.hasNode = function (v) {
return _.has(this._nodes, v);
};

Graph.prototype.removeNode = function(v) {
Graph.prototype.removeNode = function (v) {
var self = this;
if (_.has(this._nodes, v)) {
var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); };
var removeEdge = function (e) {
self.removeEdge(self._edgeObjs[e]);
};
delete this._nodes[v];
if (this._isCompound) {
this._removeFromParentsChildList(v);
delete this._parent[v];
_.each(this.children(v), function(child) {
_.each(this.children(v), function (child) {
self.setParent(child);
});
delete this._children[v];
Expand All @@ -193,7 +193,7 @@ Graph.prototype.removeNode = function(v) {
return this;
};

Graph.prototype.setParent = function(v, parent) {
Graph.prototype.setParent = function (v, parent) {
if (!this._isCompound) {
throw new Error("Cannot set parent in a non-compound graph");
}
Expand All @@ -203,12 +203,15 @@ Graph.prototype.setParent = function(v, parent) {
} else {
// Coerce parent to string
parent += "";
for (var ancestor = parent;
for (
var ancestor = parent;
!_.isUndefined(ancestor);
ancestor = this.parent(ancestor)) {
ancestor = this.parent(ancestor)
) {
if (ancestor === v) {
throw new Error("Setting " + parent+ " as parent of " + v +
" would create a cycle");
throw new Error(
"Setting " + parent + " as parent of " + v + " would create a cycle"
);
}
}

Expand All @@ -222,11 +225,11 @@ Graph.prototype.setParent = function(v, parent) {
return this;
};

Graph.prototype._removeFromParentsChildList = function(v) {
Graph.prototype._removeFromParentsChildList = function (v) {
delete this._children[this._parent[v]][v];
};

Graph.prototype.parent = function(v) {
Graph.prototype.parent = function (v) {
if (this._isCompound) {
var parent = this._parent[v];
if (parent !== GRAPH_NODE) {
Expand All @@ -235,7 +238,7 @@ Graph.prototype.parent = function(v) {
}
};

Graph.prototype.children = function(v) {
Graph.prototype.children = function (v) {
if (_.isUndefined(v)) {
v = GRAPH_NODE;
}
Expand All @@ -252,21 +255,21 @@ Graph.prototype.children = function(v) {
}
};

Graph.prototype.predecessors = function(v) {
Graph.prototype.predecessors = function (v) {
var predsV = this._preds[v];
if (predsV) {
return _.keys(predsV);
}
};

Graph.prototype.successors = function(v) {
Graph.prototype.successors = function (v) {
var sucsV = this._sucs[v];
if (sucsV) {
return _.keys(sucsV);
}
};

Graph.prototype.neighbors = function(v) {
Graph.prototype.neighbors = function (v) {
var preds = this.predecessors(v);
if (preds) {
return _.union(preds, this.successors(v));
Expand All @@ -283,23 +286,23 @@ Graph.prototype.isLeaf = function (v) {
return neighbors.length === 0;
};

Graph.prototype.filterNodes = function(filter) {
Graph.prototype.filterNodes = function (filter) {
var copy = new this.constructor({
directed: this._isDirected,
multigraph: this._isMultigraph,
compound: this._isCompound
compound: this._isCompound,
});

copy.setGraph(this.graph());

var self = this;
_.each(this._nodes, function(value, v) {
_.each(this._nodes, function (value, v) {
if (filter(v)) {
copy.setNode(v, value);
}
});

_.each(this._edgeObjs, function(e) {
_.each(this._edgeObjs, function (e) {
if (copy.hasNode(e.v) && copy.hasNode(e.w)) {
copy.setEdge(e, self.edge(e));
}
Expand All @@ -319,7 +322,7 @@ Graph.prototype.filterNodes = function(filter) {
}

if (this._isCompound) {
_.each(copy.nodes(), function(v) {
_.each(copy.nodes(), function (v) {
copy.setParent(v, findParent(v));
});
}
Expand All @@ -329,26 +332,26 @@ Graph.prototype.filterNodes = function(filter) {

/* === Edge functions ========== */

Graph.prototype.setDefaultEdgeLabel = function(newDefault) {
Graph.prototype.setDefaultEdgeLabel = function (newDefault) {
if (!_.isFunction(newDefault)) {
newDefault = _.constant(newDefault);
}
this._defaultEdgeLabelFn = newDefault;
return this;
};

Graph.prototype.edgeCount = function() {
Graph.prototype.edgeCount = function () {
return this._edgeCount;
};

Graph.prototype.edges = function() {
Graph.prototype.edges = function () {
return _.values(this._edgeObjs);
};

Graph.prototype.setPath = function(vs, value) {
Graph.prototype.setPath = function (vs, value) {
var self = this;
var args = arguments;
_.reduce(vs, function(v, w) {
_.reduce(vs, function (v, w) {
if (args.length > 1) {
self.setEdge(v, w, value);
} else {
Expand All @@ -363,7 +366,7 @@ Graph.prototype.setPath = function(vs, value) {
* setEdge(v, w, [value, [name]])
* setEdge({ v, w, [name] }, [value])
*/
Graph.prototype.setEdge = function() {
Graph.prototype.setEdge = function () {
var v, w, name, value;
var valueSpecified = false;
var arg0 = arguments[0];
Expand Down Expand Up @@ -409,7 +412,9 @@ Graph.prototype.setEdge = function() {
this.setNode(v);
this.setNode(w);

this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name);
this._edgeLabels[e] = valueSpecified
? value
: this._defaultEdgeLabelFn(v, w, name);

var edgeObj = edgeArgsToObj(this._isDirected, v, w, name);
// Ensure we add undirected edges in a consistent way.
Expand All @@ -426,24 +431,27 @@ Graph.prototype.setEdge = function() {
return this;
};

Graph.prototype.edge = function(v, w, name) {
var e = (arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name));
Graph.prototype.edge = function (v, w, name) {
var e =
arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name);
return this._edgeLabels[e];
};

Graph.prototype.hasEdge = function(v, w, name) {
var e = (arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name));
Graph.prototype.hasEdge = function (v, w, name) {
var e =
arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name);
return _.has(this._edgeLabels, e);
};

Graph.prototype.removeEdge = function(v, w, name) {
var e = (arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name));
Graph.prototype.removeEdge = function (v, w, name) {
var e =
arguments.length === 1
? edgeObjToId(this._isDirected, arguments[0])
: edgeArgsToId(this._isDirected, v, w, name);
var edge = this._edgeObjs[e];
if (edge) {
v = edge.v;
Expand All @@ -459,32 +467,44 @@ Graph.prototype.removeEdge = function(v, w, name) {
return this;
};

Graph.prototype.inEdges = function(v, u) {
Graph.prototype.getInEdges = function (v, u) {
var inV = this._in[v];
if (inV) {
var edges = _.values(inV);
if (!u) {
return edges;
}
return _.filter(edges, function(edge) { return edge.v === u; });
return _.filter(edges, function (edge) {
return edge.v === u;
});
}
};

Graph.prototype.outEdges = function(v, w) {
Graph.prototype.inEdges = function (v, u) {
return this.isDirected() ? this.getInEdges(v, u) : this.nodeEdges(v, u);
};

Graph.prototype.getOutEdges = function (v, w) {
var outV = this._out[v];
if (outV) {
var edges = _.values(outV);
if (!w) {
return edges;
}
return _.filter(edges, function(edge) { return edge.w === w; });
return _.filter(edges, function (edge) {
return edge.w === w;
});
}
};

Graph.prototype.nodeEdges = function(v, w) {
var inEdges = this.inEdges(v, w);
Graph.prototype.outEdges = function (v, w) {
return this.isDirected() ? this.getOutEdges(v, w) : this.nodeEdges(v, w);
};

Graph.prototype.nodeEdges = function (v, w) {
var inEdges = this.getInEdges(v, w, false);
if (inEdges) {
return inEdges.concat(this.outEdges(v, w));
return inEdges.concat(this.getOutEdges(v, w, false));
Comment on lines +505 to +507

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, getInEdges only has two arguments, v and optionally w, making the false seem useless here.

}
};

Expand All @@ -497,7 +517,9 @@ function incrementOrInitEntry(map, k) {
}

function decrementOrRemoveEntry(map, k) {
if (!--map[k]) { delete map[k]; }
if (!--map[k]) {
delete map[k];
}
}

function edgeArgsToId(isDirected, v_, w_, name) {
Expand All @@ -520,7 +542,7 @@ function edgeArgsToObj(isDirected, v_, w_, name) {
v = w;
w = tmp;
}
var edgeObj = { v: v, w: w };
var edgeObj = { v: v, w: w };
if (name) {
edgeObj.name = name;
}
Expand Down
Loading