39
39
import java .util .Comparator ;
40
40
import java .util .Optional ;
41
41
import java .util .PriorityQueue ;
42
+ import java .util .function .ToLongBiFunction ;
42
43
import java .util .stream .Stream ;
43
44
44
45
import static org .neo4j .gds .utils .StringFormatting .formatWithLocale ;
@@ -50,9 +51,10 @@ public final class Yens extends Algorithm<DijkstraResult> {
50
51
private final Graph graph ;
51
52
private final ShortestPathYensBaseConfig config ;
52
53
private final Dijkstra dijkstra ;
53
-
54
- private final LongScatterSet nodeBlackList ;
55
- private final LongObjectScatterMap <LongHashSet > relationshipBlackList ;
54
+ private final LongScatterSet nodeAvoidList ;
55
+ private final LongObjectScatterMap <LongHashSet > relationshipAvoidList ;
56
+ private final ToLongBiFunction
57
+ <MutablePathResult , Integer > relationshipAvoidMapper ;
56
58
57
59
/**
58
60
* Configure Yens to compute at most one source-target shortest path.
@@ -63,22 +65,15 @@ public static Yens sourceTarget(
63
65
ProgressTracker progressTracker
64
66
) {
65
67
// If the input graph is a multi-graph, we need to track
66
- // parallel relationships. This is necessary since shortest
68
+ // parallel relationships ids . This is necessary since shortest
67
69
// paths can visit the same nodes via different relationships.
70
+ //If not, we need to track which is the next neighbor.
68
71
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
-
72
+ boolean shouldTrackRelationships = graph .isMultiGraph ();
78
73
var newConfig = ImmutableShortestPathYensBaseConfig
79
74
.builder ()
80
75
.from (config )
81
- .trackRelationships (graph . isMultiGraph () )
76
+ .trackRelationships (shouldTrackRelationships )
82
77
.build ();
83
78
// Init dijkstra algorithm for computing shortest paths
84
79
var dijkstra = Dijkstra .sourceTarget (graph , newConfig , Optional .empty (), progressTracker );
@@ -105,16 +100,35 @@ private Yens(Graph graph, Dijkstra dijkstra, ShortestPathYensBaseConfig config,
105
100
this .config = config ;
106
101
// Track nodes and relationships that are skipped in a single iteration.
107
102
// The content of these data structures is reset after each of k iterations.
108
- this .nodeBlackList = new LongScatterSet ();
109
- this .relationshipBlackList = new LongObjectScatterMap <>();
110
- // set filter in Dijkstra to respect our blacklists
103
+ this .nodeAvoidList = new LongScatterSet ();
104
+ this .relationshipAvoidList = new LongObjectScatterMap <>();
105
+ // set filter in Dijkstra to respect our list of relationships to avoid
111
106
this .dijkstra = dijkstra ;
107
+
108
+ if (config .trackRelationships ()) {
109
+ // if we are in a multi-graph, we must store the relationships ids as they are
110
+ //since two nodes may be connected by multiple relationships and we must know which to avoid
111
+ relationshipAvoidMapper = (path , position ) -> path .relationship (position );
112
+ } else {
113
+ //otherwise the graph has surely no parallel edges, we do not need to explicitly store relationship ids
114
+ //we can just store endpoints, so that we know which nodes a node should avoid
115
+ relationshipAvoidMapper = (path , position ) -> path .node (position + 1 );
116
+ }
112
117
dijkstra .withRelationshipFilter ((source , target , relationshipId ) ->
113
- !nodeBlackList .contains (target ) &&
114
- !(relationshipBlackList .getOrDefault (source , EMPTY_SET ).contains (relationshipId )) &&
115
- !(relationshipBlackList .getOrDefault (source , EMPTY_SET ).contains (-target - 1 )));
118
+ !nodeAvoidList .contains (target )
119
+ && !shouldAvoidRelationship (source , target , relationshipId )
120
+
121
+ );
116
122
}
117
123
124
+ private boolean shouldAvoidRelationship (long source , long target , long relationshipId ) {
125
+ long forbidden = target ;
126
+ if (config .trackRelationships ()) {
127
+ forbidden = relationshipId ;
128
+ }
129
+ return relationshipAvoidList .getOrDefault (source , EMPTY_SET ).contains (forbidden );
130
+
131
+ }
118
132
119
133
@ Override
120
134
public DijkstraResult compute () {
@@ -150,22 +164,21 @@ public DijkstraResult compute() {
150
164
// Filter relationships that are part of the previous
151
165
// shortest paths which share the same root path.
152
166
if (rootPath .matchesExactly (path , n + 1 )) {
153
- System .out .println (i + ": " + rootPath + " |" + prevPath );
154
- var relationshipId = graph .isMultiGraph () ? path .relationship (n ) : -(1 + path .node (n + 1 ));
167
+ var relationshipId = relationshipAvoidMapper .applyAsLong (path , n );
155
168
156
- var neighbors = relationshipBlackList .get (spurNode );
169
+ var neighbors = relationshipAvoidList .get (spurNode );
157
170
158
171
if (neighbors == null ) {
159
172
neighbors = new LongHashSet ();
160
- relationshipBlackList .put (spurNode , neighbors );
173
+ relationshipAvoidList .put (spurNode , neighbors );
161
174
}
162
175
neighbors .add (relationshipId );
163
176
}
164
177
}
165
178
166
179
// Filter nodes from root path to avoid cyclic path searches.
167
180
for (int j = 0 ; j < n ; j ++) {
168
- nodeBlackList .add (rootPath .node (j ));
181
+ nodeAvoidList .add (rootPath .node (j ));
169
182
}
170
183
171
184
// Calculate the spur path from the spur node to the sink.
@@ -174,8 +187,8 @@ public DijkstraResult compute() {
174
187
var spurPath = computeDijkstra (graph .toOriginalNodeId (spurNode ));
175
188
176
189
// Clear filters for next spur node
177
- nodeBlackList .clear ();
178
- relationshipBlackList .clear ();
190
+ nodeAvoidList .clear ();
191
+ relationshipAvoidList .clear ();
179
192
180
193
// No new candidate from this spur node, continue with next node.
181
194
if (spurPath .isEmpty ()) {
@@ -201,10 +214,7 @@ public DijkstraResult compute() {
201
214
progressTracker .endSubTask ();
202
215
203
216
progressTracker .endSubTask ();
204
- System .out .println ("----" );
205
- for (var path : kShortestPaths ) {
206
- System .out .println (path );
207
- }
217
+
208
218
return new DijkstraResult (kShortestPaths .stream ().map (MutablePathResult ::toPathResult ));
209
219
}
210
220
0 commit comments