diff --git a/js/common/modules/graph.js b/js/common/modules/graph.js index 7e28579bab..9bd32f0593 100644 --- a/js/common/modules/graph.js +++ b/js/common/modules/graph.js @@ -768,61 +768,72 @@ Vertex.prototype.setProperty = function (name, value) { //////////////////////////////////////////////////////////////////////////////// Vertex.prototype.pathTo = function (target_node) { - var pathes = {}, // {ID => [Node]} - determined_list = [], // [ID] - todo_list = [], // [ID] - i, // Number - current_node_id, // ID - neighbor_list = [], // [ID] - new_path = [], // [Node] - current_neighbor; // ID + var determined_list = [], // [ID] + todo_list = [], // [ID] + distances = {}, // { ID => Number } + predecessors = {}, // { ID => [ID] } + raw_neighborlist = [], // [ID] + i, // Number + current_distance = 0, // Number + pathes = [], // + current_node_id, // ID + current_neighbor_id; // ID - this._pushNeigborsToArray(todo_list); - - for (i = 0; i < todo_list.length; i++) { - pathes[todo_list[i]] = [this, this._graph.getVertex(todo_list[i])]; - } + todo_list.push(this.getId()); + distances[this.getId()] = 0; while (todo_list.length > 0) { - current_node_id = this._getShortestDistanceFor(todo_list, pathes); - determined_list.push(current_node_id); + current_node_id = this._getShortestDistance(todo_list, distances); + + if (current_node_id === target_node) { + break; + } + todo_list.removeLastOccurrenceOf(current_node_id); + determined_list.push(current_node_id); - neighbor_list = []; - this._graph.getVertex(current_node_id)._pushNeigborsToArray(neighbor_list); - for (i = 0; i < neighbor_list.length; i++) { - current_neighbor = neighbor_list[i]; + raw_neighborlist = this._graph.getVertex(current_node_id).getNeighbors(); + for (i = 0; i < raw_neighborlist.length; i += 1) { + current_neighbor_id = raw_neighborlist[i]; - new_path = [] - .concat(pathes[current_node_id]) - .concat(this._graph.getVertex(current_neighbor)); + if (determined_list.lastIndexOf(current_neighbor_id) === -1) { + current_distance = distances[current_node_id] + 1; - if (pathes[current_neighbor] === undefined || (new_path.length < pathes[current_neighbor].length)) { - pathes[current_neighbor] = new_path; - - if (determined_list.lastIndexOf(neighbor_list[i]) === -1) { - todo_list.push(neighbor_list[i]); + if (todo_list.lastIndexOf(current_neighbor_id) === -1) { + todo_list.push(current_neighbor_id); + predecessors[current_neighbor_id] = [current_node_id]; + distances[current_neighbor_id] = current_distance; + } else if (distances[current_neighbor_id] > current_distance) { + predecessors[current_neighbor_id] = [current_node_id]; + distances[current_neighbor_id] = current_distance; + } else if (distances[current_neighbor_id] === current_distance) { + predecessors[current_neighbor_id].push(current_node_id); } } } - - if (current_node_id === target_node.getId()) { - break; - } } - return pathes[target_node.getId()]; + current_node_id = target_node.getId(); + if (determined_list.lastIndexOf(current_node_id) === -1) { + pathes = []; + } else { + pathes[0] = []; + while (current_node_id !== undefined) { + pathes[0].push(current_node_id); + current_node_id = predecessors[current_node_id]; + } + pathes[0].reverse(); + } + + return pathes; }; -//////////////////////////////////////////////////////////////////////////////// -/// @brief find the shortest path to a certain node -/// -/// @FUN{@FA{vertex}._pushNeigborsToArray(@FA{target_array})} -/// -//////////////////////////////////////////////////////////////////////////////// - -Vertex.prototype._pushNeigborsToArray = function (target_array) { - var i, current_node_id; +// Later: Take Inbound, Outbound, Both. Currently Both only +// and: Only return neighbors with a certain label +Vertex.prototype.getNeighbors = function () { + var i, + current_node_id, + target_array = []; for (i = 0; i < this.getOutEdges().length; i++) { current_node_id = this.getOutEdges()[i].getInVertex().getId(); @@ -833,8 +844,48 @@ Vertex.prototype._pushNeigborsToArray = function (target_array) { current_node_id = this.getInEdges()[i].getOutVertex().getId(); target_array.push(current_node_id); } + + return target_array; }; +Vertex.prototype._getShortestDistance = function (todo_list, distances) { + var shortest_distance = Infinity, + node = null, + i, + distance; + + for (i = 0; i < todo_list.length; i += 1) { + distance = distances[todo_list[i]]; + if (distance < shortest_distance) { + shortest_distance = distance; + node = todo_list[i]; + } + } + + return node; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief find the shortest path to a certain node +/// +/// @FUN{@FA{vertex}._pushNeigborsToArray(@FA{target_array})} +/// +//////////////////////////////////////////////////////////////////////////////// + +// Vertex.prototype._pushNeigborsToArray = function (target_array) { +// var i, current_node_id; +// +// for (i = 0; i < this.getOutEdges().length; i++) { +// current_node_id = this.getOutEdges()[i].getInVertex().getId(); +// target_array.push(current_node_id); +// } +// +// for (i = 0; i < this.getInEdges().length; i++) { +// current_node_id = this.getInEdges()[i].getOutVertex().getId(); +// target_array.push(current_node_id); +// } +// }; + //////////////////////////////////////////////////////////////////////////////// /// @brief Find the node with the shortest distance for a given list /// @@ -842,21 +893,21 @@ Vertex.prototype._pushNeigborsToArray = function (target_array) { /// //////////////////////////////////////////////////////////////////////////////// -Vertex.prototype._getShortestDistanceFor = function (todo_list, distances) { - var current_node_id, - shortest_distance = [null, Infinity], - i; - - for (i = 0; i < todo_list.length; i++) { - current_node_id = todo_list[i]; - - if (distances[current_node_id].length < shortest_distance[1]) { - shortest_distance = [current_node_id, distances[current_node_id].length]; - } - } - - return shortest_distance[0]; -}; +// Vertex.prototype._getShortestDistanceFor = function (todo_list, prede) { +// var current_node_id, +// shortest_distance = [null, Infinity], +// i; +// +// for (i = 0; i < todo_list.length; i++) { +// current_node_id = todo_list[i]; +// +// if (distances[current_node_id].length < shortest_distance[1]) { +// shortest_distance = [current_node_id, distances[current_node_id].length]; +// } +// } +// +// return shortest_distance[0]; +// }; //////////////////////////////////////////////////////////////////////////////// /// @} diff --git a/js/common/tests/shell-graph-algorithms.js b/js/common/tests/shell-graph-algorithms.js index 22ac2c7d2f..922622fb0e 100644 --- a/js/common/tests/shell-graph-algorithms.js +++ b/js/common/tests/shell-graph-algorithms.js @@ -93,52 +93,37 @@ function dijkstraSuite() { } }, - testPushToNeighbors : function () { - var v1 = graph.addVertex(1), - v2 = graph.addVertex(2), - v3 = graph.addVertex(3), - test_array = []; - - graph.addEdge(v1, v2); - graph.addEdge(v1, v3); - - v1._pushNeigborsToArray(test_array); - - assertEqual(test_array[0], 2); - assertEqual(test_array[1], 3); - }, - - testShortestDistanceFor : function () { - var v1 = graph.addVertex(1), - v2 = graph.addVertex(2), - v3 = graph.addVertex(3), - todo_list = [v1.getId(), v2.getId()], // [ID] - distances = {}, // {ID => [Node]} - node_id_found; // Node - - distances[v1.getId()] = [v1, v2, v3]; - distances[v2.getId()] = [v2, v3]; - distances[v3.getId()] = [v3]; - - node_id_found = v1._getShortestDistanceFor(todo_list, distances); - assertEqual(node_id_found, v2.getId()); - }, + // testPushToNeighbors : function () { + // var v1 = graph.addVertex(1), + // v2 = graph.addVertex(2), + // v3 = graph.addVertex(3), + // test_array = []; + // + // graph.addEdge(v1, v2); + // graph.addEdge(v1, v3); + // + // v1._pushNeigborsToArray(test_array); + // + // assertEqual(test_array[0], 2); + // assertEqual(test_array[1], 3); + // }, //////////////////////////////////////////////////////////////////////////////// /// @brief get a short, distinct path //////////////////////////////////////////////////////////////////////////////// testGetAShortDistinctPath : function () { - var v1, v2, e1; + var v1, v2, e1, path; v1 = graph.addVertex(1); v2 = graph.addVertex(2); e1 = graph.addEdge(v1, v2); - assertEqual(v1.pathTo(v2).length, 2); - assertEqual(v1.pathTo(v2)[0].getId(), v1.getId()); - assertEqual(v1.pathTo(v2)[1].getId(), v2.getId()); + path = v1.pathTo(v2)[0]; + assertEqual(path.length, 2); + assertEqual(path[0].toString(), v1.getId()); + assertEqual(path[1].toString(), v2.getId()); }, //////////////////////////////////////////////////////////////////////////////// @@ -146,7 +131,7 @@ function dijkstraSuite() { //////////////////////////////////////////////////////////////////////////////// testGetALongerDistinctPath : function () { - var v1, v2, v3, e1, e2; + var v1, v2, v3, e1, e2, path; v1 = graph.addVertex(1); v2 = graph.addVertex(2); @@ -155,23 +140,26 @@ function dijkstraSuite() { e1 = graph.addEdge(v1, v2); e2 = graph.addEdge(v2, v3); - assertEqual(v1.pathTo(v3).length, 3); - assertEqual(v1.pathTo(v3)[0].getId(), v1.getId()); - assertEqual(v1.pathTo(v3)[1].getId(), v2.getId()); - assertEqual(v1.pathTo(v3)[2].getId(), v3.getId()); + path = v1.pathTo(v3)[0]; + assertEqual(path.length, 3); + assertEqual(path[0].toString(), v1.getId()); + assertEqual(path[1].toString(), v2.getId()); + assertEqual(path[2].toString(), v3.getId()); }, //////////////////////////////////////////////////////////////////////////////// -/// @brief compare with Neo4j on a random network of size 500 +/// @brief compare with Neo4j on a network of size 500 //////////////////////////////////////////////////////////////////////////////// - testComparisonWithNeo4j500 : function () { + testComparisonWithNeo4j500LengthOnly : function () { var base_path = "js/common/test-data/random500/", v1, v2, e, - path, + pathes, results = [], + neo4j_results = [], + single_result = {}, counter; Helper.process(base_path + "generated_edges.csv", function (row) { @@ -184,8 +172,13 @@ function dijkstraSuite() { v1 = graph.getVertex(row[0]); v2 = graph.getVertex(row[1]); if (v1 !== null && v2 !== null) { - path = v1.pathTo(v2); - results.push(path); + pathes = v1.pathTo(v2); + single_result = { + 'from' : v1.getId(), + 'to' : v2.getId(), + 'path_length' : pathes[0].length + }; + results.push(single_result); } }); @@ -198,18 +191,26 @@ function dijkstraSuite() { if (!(v1 === row[0] && v2 === row[row.length - 1])) { v1 = row[0]; v2 = row[row.length - 1]; - result = results[counter]; - assertEqual(v1, result[0].getId()); - assertEqual(v2, result[result.length - 1].getId()); - - if (result.length !== row.length) { - console.log("Neo4j: Path from " + v1 + " to " + v2 + " has length " + result.length + " (should be " + row.length + ")"); - } - counter += 1; + single_result = { + 'from' : v1, + 'to' : v2, + 'path_length' : row.length + }; + neo4j_results.push(single_result); } - //assertEqual(raw_row, results[index]); }); + + for (counter = 0; counter < neo4j_results.results; counter += 1) { + assertEqual(results[counter].from, + neo4j_results[counter].from); + + assertEqual(results[counter].to, + neo4j_results[counter].to); + + assertEqual(results[counter].path_length, + neo4j_results[counter].path_length); + } } }; }