Skip to content

Commit

Permalink
Merge pull request #8521 from lassewesth/pfdijk
Browse files Browse the repository at this point in the history
migrate pathfinding algorithms to being reusable
  • Loading branch information
lassewesth authored Jan 2, 2024
2 parents 60e25f5 + a8d8e01 commit e8af456
Show file tree
Hide file tree
Showing 82 changed files with 2,145 additions and 220 deletions.
22 changes: 18 additions & 4 deletions algo/src/main/java/org/neo4j/gds/paths/astar/AStar.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.neo4j.gds.paths.astar.config.ShortestPathAStarBaseConfig;
import org.neo4j.gds.paths.dijkstra.Dijkstra;
import org.neo4j.gds.paths.dijkstra.PathFindingResult;
import org.neo4j.gds.termination.TerminationFlag;

import java.util.Optional;

Expand All @@ -36,16 +37,29 @@ public final class AStar extends Algorithm<PathFindingResult> {

private final Dijkstra dijkstra;

private AStar(Dijkstra dijkstra) {
private AStar(Dijkstra dijkstra, TerminationFlag terminationFlag) {
super(dijkstra.getProgressTracker());
this.dijkstra = dijkstra;
this.terminationFlag = dijkstra.getTerminationFlag();
this.terminationFlag = terminationFlag;
}

/**
* @deprecated Use the one with termination flag
*/
@Deprecated
public static AStar sourceTarget(
Graph graph,
ShortestPathAStarBaseConfig config,
ProgressTracker progressTracker
) {
return sourceTarget(graph, config, progressTracker, TerminationFlag.RUNNING_TRUE);
}

public static AStar sourceTarget(
Graph graph,
ShortestPathAStarBaseConfig config,
ProgressTracker progressTracker,
TerminationFlag terminationFlag
) {
var latitudeProperty = config.latitudeProperty();
var longitudeProperty = config.longitudeProperty();
Expand All @@ -70,8 +84,8 @@ public static AStar sourceTarget(
var heuristic = new HaversineHeuristic(latitudeProperties, longitudeProperties, targetNode);

// Init dijkstra algorithm for computing shortest paths
var dijkstra = Dijkstra.sourceTarget(graph, config, Optional.of(heuristic), progressTracker);
return new AStar(dijkstra);
var dijkstra = Dijkstra.sourceTarget(graph, config, Optional.of(heuristic), progressTracker, terminationFlag);
return new AStar(dijkstra, terminationFlag);
}


Expand Down
63 changes: 53 additions & 10 deletions algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.neo4j.gds.paths.ImmutablePathResult;
import org.neo4j.gds.paths.PathResult;
import org.neo4j.gds.paths.SourceTargetShortestPathBaseConfig;
import org.neo4j.gds.termination.TerminationFlag;

import java.util.Optional;
import java.util.function.LongToDoubleFunction;
Expand Down Expand Up @@ -71,53 +72,94 @@ public final class Dijkstra extends Algorithm<PathFindingResult> {
private RelationshipFilter relationshipFilter = (sourceId, targetId, relationshipId) -> true;

/**
* Configure Dijkstra to compute at most one source-target shortest path.
* @deprecated Use the other one, with termination flag
*/
@Deprecated
public static Dijkstra sourceTarget(
Graph graph,
SourceTargetShortestPathBaseConfig config,
Optional<HeuristicFunction> heuristicFunction,
ProgressTracker progressTracker
) {
long sourceNode = graph.toMappedNodeId(config.sourceNode());
long targetNode = graph.toMappedNodeId(config.targetNode());
return sourceTarget(
graph,
config,
heuristicFunction,
progressTracker,
TerminationFlag.RUNNING_TRUE
);
}

/**
* Configure Dijkstra to compute at most one source-target shortest path.
*/
public static Dijkstra sourceTarget(
Graph graph,
SourceTargetShortestPathBaseConfig configuration,
Optional<HeuristicFunction> heuristicFunction,
ProgressTracker progressTracker,
TerminationFlag terminationFlag
) {
long sourceNode = graph.toMappedNodeId(configuration.sourceNode());
long targetNode = graph.toMappedNodeId(configuration.targetNode());

return new Dijkstra(
graph,
sourceNode,
node -> node == targetNode ? EMIT_AND_STOP : CONTINUE,
config.trackRelationships(),
configuration.trackRelationships(),
heuristicFunction,
progressTracker
progressTracker,
terminationFlag
);
}

/**
* Configure Dijkstra to compute all single-source shortest path.
* @deprecated Use the other one with termination flag
*/
public static Dijkstra singleSource(
Graph graph,
AllShortestPathsBaseConfig config,
Optional<HeuristicFunction> heuristicFunction,
ProgressTracker progressTracker
) {
return singleSource(
graph,
config,
heuristicFunction,
progressTracker,
TerminationFlag.RUNNING_TRUE
);
}

/**
* Configure Dijkstra to compute all single-source shortest path.
*/
public static Dijkstra singleSource(
Graph graph,
AllShortestPathsBaseConfig config,
Optional<HeuristicFunction> heuristicFunction,
ProgressTracker progressTracker,
TerminationFlag terminationFlag
) {
return new Dijkstra(graph,
graph.toMappedNodeId(config.sourceNode()),
node -> EMIT_AND_CONTINUE,
config.trackRelationships(),
heuristicFunction,
progressTracker
progressTracker,
terminationFlag
);
}

private Dijkstra(
public Dijkstra(
Graph graph,
long sourceNode,
TraversalPredicate traversalPredicate,
boolean trackRelationships,
Optional<HeuristicFunction> heuristicFunction,
ProgressTracker progressTracker
) {
ProgressTracker progressTracker,
TerminationFlag terminationFlag) {
super(progressTracker);
this.graph = graph;
this.sourceNode = sourceNode;
Expand All @@ -131,6 +173,7 @@ private Dijkstra(
this.relationships = trackRelationships ? new HugeLongLongMap() : null;
this.visited = new BitSet();
this.pathIndex = 0L;
this.terminationFlag = terminationFlag;
}

public Dijkstra withSourceNode(long sourceNode) {
Expand Down
27 changes: 27 additions & 0 deletions applications/algorithms/path-finding/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apply plugin: 'java-library'

description = 'Neo4j Graph Data Science :: Path Finding Algorithms'

group = 'org.neo4j.gds'

dependencies {
compileOnly(group: 'org.neo4j', name: 'neo4j-logging', version: ver.'neo4j') { transitive = false }

implementation project(':algo')
implementation project(':algo-common')
implementation project(':config-api')
implementation project(':core')
implementation project(':logging')
implementation project(':memory-usage')
implementation project(':metrics-api')
implementation project(':progress-tracking')
implementation project(':string-formatting')
implementation project(':termination')

testImplementation group: 'org.assertj', name: 'assertj-core', version: ver.'assertj'
testImplementation platform(dep.junit5bom)
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: ver.'junit5bom'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: ver.'junit5bom'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: ver.'junit5bom'
testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: ver.'mockito-junit-jupiter'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gds.applications.algorithms.pathfinding;

import org.neo4j.gds.api.Graph;

public interface AlgorithmComputation<RESULT> {
RESULT compute(Graph graph);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gds.applications.algorithms.pathfinding;

import org.neo4j.gds.api.GraphName;
import org.neo4j.gds.config.AlgoBaseConfig;
import org.neo4j.gds.config.RelationshipWeightConfig;
import org.neo4j.gds.core.utils.mem.MemoryEstimation;

import java.util.Optional;
import java.util.function.Supplier;

/**
* So, internally, all (pathfinding) algorithms in all modes follow these same steps:
* <ol>
* <li>You load a graph
* <li>You validate the algorithms vs the graph (is it empty, memory usage, etc.)
* <li>You establish timings and metrics
* <li>YOU CALL THE ACTUAL ALGORITHM! Minor detail...
* <li>Next up, mutates or writes, the possible side effect steps
* <li>Lastly result rendering
* </ol>
* Result rendering is a whole chapter. It is independent of where call came from, so injected.
* It can use any of these parameters:
* <ul>
* <li>graph
* <li>graph store
* <li>configuration
* <li>timings, including side effect steps
* </ul>
* Injected, plus mode-conditional: it's a wide parameter list...
* There is always a result. For streaming, it is duh; for the others it is metadata.
* Side effect steps are optional, and they are injected in some sense, but also _selected_.
* Because luckily those behaviours are generic enough that you can just select them from a catalogue.
* So we _instrument_ with zero or one mode behaviour selected from a catalogue,
* and _instrument_ with bespoke result rendering.
*/
public interface AlgorithmProcessingTemplate {
<CONFIGURATION extends AlgoBaseConfig & RelationshipWeightConfig, RESULT_TO_CALLER, RESULT_FROM_ALGORITHM>
RESULT_TO_CALLER processAlgorithm(
GraphName graphName,
CONFIGURATION configuration,
String humanReadableAlgorithmName,
Supplier<MemoryEstimation> estimationFactory,
AlgorithmComputation<RESULT_FROM_ALGORITHM> algorithmComputation,
Optional<MutateOrWriteStep<RESULT_FROM_ALGORITHM>> mutateOrWriteStep,
ResultBuilder<RESULT_FROM_ALGORITHM, RESULT_TO_CALLER> resultBuilder
);
}
Loading

0 comments on commit e8af456

Please sign in to comment.