Skip to content

Commit e9d8b9c

Browse files
Find and solve the issue
Co-authored-by: Veselin Nikolov <veselin.nikolov@neotechnology.com>
1 parent 1a7bcfd commit e9d8b9c

File tree

5 files changed

+131
-12
lines changed

5 files changed

+131
-12
lines changed

algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ private PathResult next(TraversalPredicate traversalPredicate, ImmutablePathResu
217217
return pathResult(node, pathResultBuilder);
218218
}
219219
}
220+
220221
return PathResult.EMPTY;
221222
}
222223

algo/src/main/java/org/neo4j/gds/paths/yens/MutablePathResult.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ boolean matches(MutablePathResult path, int index) {
131131
*/
132132
boolean matchesExactly(MutablePathResult path, int index) {
133133

134-
if (relationshipIds == null || path.relationshipIds == null) {
134+
if (relationshipIds.length == 0 || path.relationshipIds.length == 0) {
135135
return matches(path, index);
136136
}
137-
137+
//System.out.println(Arrays.toString(Arrays.stream(relationshipIds).toArray()));
138138
for (int i = 0; i < index; i++) {
139139
if (nodeIds[i] != path.nodeIds[i]) {
140140
return false;
@@ -157,7 +157,7 @@ boolean matchesExactly(MutablePathResult path, int index) {
157157
* The cost value associated with the last value in this path, is added to
158158
* the costs for each node in the second path.
159159
*/
160-
void append(MutablePathResult path) {
160+
void append(MutablePathResult path, boolean shouldstore) {
161161
// spur node is end of first and beginning of second path
162162
assert nodeIds[nodeIds.length - 1] == path.nodeIds[0];
163163

@@ -186,7 +186,7 @@ void append(MutablePathResult path) {
186186
}
187187

188188
this.nodeIds = newNodeIds;
189-
this.relationshipIds = newRelationshipIds;
189+
this.relationshipIds = shouldstore ? newRelationshipIds : new long[0];
190190
this.costs = newCosts;
191191
}
192192

algo/src/main/java/org/neo4j/gds/paths/yens/Yens.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ public static Yens sourceTarget(
6565
// If the input graph is a multi-graph, we need to track
6666
// parallel relationships. This is necessary since shortest
6767
// paths can visit the same nodes via different relationships.
68+
69+
System.out.println(graph.schema().relationshipSchema().toMap());
70+
graph.forEachNode(nodeId -> {
71+
graph.forEachRelationship(nodeId, 1.0, (s, t, w) -> {
72+
System.out.println(s + "-[" + w + "]->" + t);
73+
return true;
74+
});
75+
return true;
76+
});
77+
6878
var newConfig = ImmutableShortestPathYensBaseConfig
6979
.builder()
7080
.from(config)
@@ -101,10 +111,11 @@ private Yens(Graph graph, Dijkstra dijkstra, ShortestPathYensBaseConfig config,
101111
this.dijkstra = dijkstra;
102112
dijkstra.withRelationshipFilter((source, target, relationshipId) ->
103113
!nodeBlackList.contains(target) &&
104-
!(relationshipBlackList.getOrDefault(source, EMPTY_SET).contains(relationshipId))
105-
);
114+
!(relationshipBlackList.getOrDefault(source, EMPTY_SET).contains(relationshipId)) &&
115+
!(relationshipBlackList.getOrDefault(source, EMPTY_SET).contains(-target - 1)));
106116
}
107117

118+
108119
@Override
109120
public DijkstraResult compute() {
110121
progressTracker.beginSubTask();
@@ -139,7 +150,8 @@ public DijkstraResult compute() {
139150
// Filter relationships that are part of the previous
140151
// shortest paths which share the same root path.
141152
if (rootPath.matchesExactly(path, n + 1)) {
142-
var relationshipId = path.relationship(n);
153+
System.out.println(i + ": " + rootPath + " |" + prevPath);
154+
var relationshipId = graph.isMultiGraph() ? path.relationship(n) : -(1 + path.node(n + 1));
143155

144156
var neighbors = relationshipBlackList.get(spurNode);
145157

@@ -171,7 +183,7 @@ public DijkstraResult compute() {
171183
}
172184

173185
// Entire path is made up of the root path and spur path.
174-
rootPath.append(MutablePathResult.of(spurPath.get()));
186+
rootPath.append(MutablePathResult.of(spurPath.get()), graph.isMultiGraph());
175187
// Add the potential k-shortest path to the heap.
176188
if (!candidates.contains(rootPath)) {
177189
candidates.add(rootPath);
@@ -189,7 +201,10 @@ public DijkstraResult compute() {
189201
progressTracker.endSubTask();
190202

191203
progressTracker.endSubTask();
192-
204+
System.out.println("----");
205+
for (var path : kShortestPaths) {
206+
System.out.println(path);
207+
}
193208
return new DijkstraResult(kShortestPaths.stream().map(MutablePathResult::toPathResult));
194209
}
195210

algo/src/test/java/org/neo4j/gds/paths/yens/MutablePathResultTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void append() {
170170
var path1 = testPath(0, 1, 2);
171171
var path2 = testPath(2, 3, 4);
172172

173-
path1.append(path2);
173+
path1.append(path2, true);
174174
var expected = testPath(0, 1, 2, 3, 4);
175175

176176
assertEquals(expected, path1);
@@ -184,7 +184,7 @@ void appendWithOffset() {
184184
var expected = MutablePathResult.of(ImmutablePathResult.builder().nodeIds(0, 1, 2, 3, 4).relationshipIds(0, 1, 2, 3).costs(0, 1, 42, 55, 79).sourceNode(0).targetNode(2).index(2).build());
185185
//@formatter:on
186186

187-
p1.append(p2);
187+
p1.append(p2, true);
188188

189189
assertEquals(expected, p1);
190190
assertEquals(expected.totalCost(), p1.totalCost());
@@ -195,6 +195,6 @@ void appendFailedAssertion() {
195195
var path1 = testPath(0, 1, 2);
196196
var path2 = testPath(4, 3, 5);
197197

198-
assertThatThrownBy(() -> path1.append(path2)).isInstanceOf(AssertionError.class);
198+
assertThatThrownBy(() -> path1.append(path2, true)).isInstanceOf(AssertionError.class);
199199
}
200200
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Neo4j is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
package org.neo4j.gds.paths.sourcetarget;
21+
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
import org.neo4j.gds.BaseProcTest;
25+
import org.neo4j.gds.GdsCypher;
26+
import org.neo4j.gds.RelationshipProjection;
27+
import org.neo4j.gds.catalog.GraphProjectProc;
28+
import org.neo4j.gds.core.Aggregation;
29+
import org.neo4j.gds.extension.Neo4jGraph;
30+
31+
class ShortestPathYensProc2Tes extends BaseProcTest{
32+
33+
@Neo4jGraph
34+
private static final String DB_CYPHER =
35+
"CREATE (a:CITY), " +
36+
"(b:CITY), " +
37+
"(c:CITY), " +
38+
"(d:CITY), " +
39+
"(e:CITY), " +
40+
"(f:CITY), " +
41+
"(a)-[:ROAD]->(b), " +
42+
"(a)-[:ROAD]->(b), " +
43+
"(b)-[:ROAD]->(c), " +
44+
"(b)-[:ROAD]->(d), " +
45+
"(c)-[:ROAD]->(f), " +
46+
"(d)-[:ROAD]->(e), " +
47+
"(e)-[:ROAD]->(c), " +
48+
"(e)-[:ROAD]->(f), " +
49+
"(a)-[:PATH]->(b), " +
50+
"(d)-[:PATH]->(e), " +
51+
"(d)-[:PATH]->(e)";
52+
53+
@BeforeEach
54+
void setup() throws Exception {
55+
registerProcedures(
56+
ShortestPathYensStreamProc.class,
57+
GraphProjectProc.class
58+
);
59+
60+
runQuery(GdsCypher.call("graph")
61+
.graphProject()
62+
.withAnyLabel()
63+
.withRelationshipType("TYPE", RelationshipProjection.builder().type("*").aggregation(Aggregation.SINGLE).build())
64+
.yields());
65+
}
66+
67+
@Test
68+
void foo() {
69+
runQuery("" +
70+
"MATCH (source), (target) " +
71+
"WHERE id(source)=0 AND id(target)=5 " +
72+
"CALL gds.shortestPath.yens.stream(" +
73+
" 'graph', " +
74+
" {sourceNode:source, targetNode:target, k:3} " +
75+
") " +
76+
"YIELD sourceNode " +
77+
"RETURN *"
78+
);
79+
80+
81+
82+
}
83+
@Test
84+
void bar() {
85+
runQuery("CALL gds.graph.project.cypher(\n" +
86+
" 'graphSingleType_NP',\n" +
87+
" 'MATCH (n) RETURN id(n) AS id',\n" +
88+
" 'MATCH (n)-[r]->(m) RETURN DISTINCT id(n) AS source, id(m) AS target'\n" +
89+
")");
90+
91+
runQuery("" +
92+
"MATCH (source), (target) " +
93+
"WHERE id(source)=0 AND id(target)=5 " +
94+
"CALL gds.shortestPath.yens.stream(" +
95+
" 'graphSingleType_NP', " +
96+
" {sourceNode:source, targetNode:target, k:3} " +
97+
") " +
98+
"YIELD sourceNode " +
99+
"RETURN *"
100+
);
101+
}
102+
}
103+

0 commit comments

Comments
 (0)