Skip to content

Commit d0c814b

Browse files
Implement local stats facade
Co-authored-by: Ioannis Panagiotas <ioannis.panagiotas@neo4j.com>
1 parent 897f2ba commit d0c814b

File tree

11 files changed

+304
-2
lines changed

11 files changed

+304
-2
lines changed

applications/algorithms/path-finding/src/main/java/org/neo4j/gds/applications/algorithms/pathfinding/PathFindingAlgorithmsStatsModeBusinessFacade.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTemplateConvenience;
2525
import org.neo4j.gds.applications.algorithms.machinery.StatsResultBuilder;
2626
import org.neo4j.gds.collections.ha.HugeLongArray;
27+
import org.neo4j.gds.maxflow.FlowResult;
28+
import org.neo4j.gds.maxflow.MaxFlowStatsConfig;
2729
import org.neo4j.gds.paths.bellmanford.AllShortestPathsBellmanFordStatsConfig;
2830
import org.neo4j.gds.paths.bellmanford.BellmanFordResult;
2931
import org.neo4j.gds.paths.delta.config.AllShortestPathsDeltaStatsConfig;
@@ -42,6 +44,7 @@
4244
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.BFS;
4345
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.BellmanFord;
4446
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.DeltaStepping;
47+
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.MaxFlow;
4548
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.PCST;
4649
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.RandomWalk;
4750
import static org.neo4j.gds.applications.algorithms.machinery.AlgorithmLabel.SteinerTree;
@@ -107,6 +110,21 @@ public <RESULT> RESULT deltaStepping(
107110
);
108111
}
109112

113+
public <RESULT> RESULT maxFlow(
114+
GraphName graphName,
115+
MaxFlowStatsConfig configuration,
116+
StatsResultBuilder<FlowResult, RESULT> resultBuilder
117+
) {
118+
return algorithmProcessingTemplateConvenience.processRegularAlgorithmInStatsMode(
119+
graphName,
120+
configuration,
121+
MaxFlow,
122+
estimationFacade::maxFlow,
123+
(graph, __) -> pathFindingAlgorithms.maxFlow(graph, configuration),
124+
resultBuilder
125+
);
126+
}
127+
110128
public <RESULT> RESULT pcst(
111129
GraphName graphName,
112130
PCSTStatsConfig configuration,

doc/modules/ROOT/pages/operations-reference/algorithm-references.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@
227227
| `gds.louvain.stream.estimate` label:procedure[Procedure]
228228
| `gds.louvain.stats` label:procedure[Procedure]
229229
| `gds.louvain.stats.estimate` label:procedure[Procedure]
230-
.1+<.^|Max flow
230+
.2+<.^|Max flow
231+
| `gds.maxFlow.stats` label:procedure[Procedure]
231232
| `gds.maxFlow.stream` label:procedure[Procedure]
232233
.4+<.^|xref:algorithms/approx-max-k-cut.adoc[Approximate Maximum k-cut]
233234
| `gds.maxkcut.mutate` label:procedure[Procedure]

open-packaging/src/test/java/org/neo4j/gds/OpenGdsProcedureSmokeTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ class OpenGdsProcedureSmokeTest extends BaseProcTest {
469469
"gds.louvain.write",
470470
"gds.louvain.write.estimate",
471471

472+
"gds.maxFlow.stats",
472473
"gds.maxFlow.stream",
473474

474475
"gds.nodeSimilarity.mutate",
@@ -631,7 +632,7 @@ void countShouldMatch() {
631632
);
632633

633634
// If you find yourself updating this count, please also update the count in SmokeTest.kt
634-
int expectedCount = 469;
635+
int expectedCount = 470;
635636
assertEquals(
636637
expectedCount,
637638
returnedRows,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.maxflow;
21+
22+
import org.neo4j.gds.procedures.GraphDataScienceProcedures;
23+
import org.neo4j.gds.procedures.algorithms.pathfinding.MaxFlowStatsResult;
24+
import org.neo4j.procedure.Context;
25+
import org.neo4j.procedure.Description;
26+
import org.neo4j.procedure.Name;
27+
import org.neo4j.procedure.Procedure;
28+
29+
import java.util.Map;
30+
import java.util.stream.Stream;
31+
32+
import static org.neo4j.gds.paths.maxflow.MaxFlowConstants.MAXFLOW_DESCRIPTION;
33+
import static org.neo4j.procedure.Mode.READ;
34+
35+
public class MaxFlowStatsProc {
36+
@Context
37+
public GraphDataScienceProcedures facade;
38+
39+
@Procedure(name = "gds.maxFlow.stats", mode = READ)
40+
@Description(MAXFLOW_DESCRIPTION)
41+
public Stream<MaxFlowStatsResult> stream(
42+
@Name(value = "graphName") String graphName,
43+
@Name(value = "configuration", defaultValue = "{}") Map<String, Object> configuration
44+
) {
45+
return facade.algorithms().pathFinding().maxFlowStats(graphName, configuration);
46+
}
47+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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.maxflow;
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.catalog.GraphProjectProc;
27+
import org.neo4j.gds.extension.IdFunction;
28+
import org.neo4j.gds.extension.Inject;
29+
import org.neo4j.gds.extension.Neo4jGraph;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
33+
class MaxFlowStatsProcTest extends BaseProcTest {
34+
@Neo4jGraph(offsetIds = true)
35+
static final String DB_CYPHER = """
36+
CREATE
37+
(a:Node {id: 0}),
38+
(b:Node {id: 1}),
39+
(c:Node {id: 2}),
40+
(d:Node {id: 3}),
41+
(e:Node {id: 4}),
42+
(a)-[:R {w: 4.0}]->(d),
43+
(b)-[:R {w: 3.0}]->(a),
44+
(c)-[:R {w: 2.0}]->(a),
45+
(c)-[:R {w: 0.0}]->(b),
46+
(d)-[:R {w: 5.0}]->(e)
47+
""";
48+
49+
@Inject
50+
private IdFunction idFunction;
51+
52+
@BeforeEach
53+
void setup() throws Exception {
54+
registerProcedures(MaxFlowStatsProc.class, GraphProjectProc.class);
55+
var createQuery = GdsCypher.call(DEFAULT_GRAPH_NAME)
56+
.graphProject()
57+
.withAnyLabel()
58+
.withRelationshipProperty("w")
59+
.yields();
60+
runQuery(createQuery);
61+
}
62+
63+
@Test
64+
void testYields() {
65+
String query = GdsCypher.call(DEFAULT_GRAPH_NAME)
66+
.algo("gds.maxFlow")
67+
.statsMode()
68+
.addParameter("sourceNodes", idFunction.of("a"))
69+
.addParameter("capacityProperty", "w")
70+
.addParameter("targetNodes", idFunction.of("e"))
71+
.yields(
72+
"preProcessingMillis",
73+
"computeMillis",
74+
"totalFlow"
75+
);
76+
77+
var rowCount = runQueryWithRowConsumer(
78+
query,
79+
res -> {
80+
assertThat(res.getNumber("totalFlow").doubleValue()).isEqualTo(4D);
81+
assertThat(res.getNumber("preProcessingMillis").longValue()).isGreaterThanOrEqualTo(0L);
82+
assertThat(res.getNumber("computeMillis").longValue()).isGreaterThanOrEqualTo(0L);
83+
}
84+
);
85+
assertThat(rowCount).isEqualTo(1L);
86+
87+
}
88+
}

procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/pathfinding/LocalPathFindingProcedureFacade.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.neo4j.gds.dag.longestPath.DagLongestPathStreamConfig;
3535
import org.neo4j.gds.dag.topologicalsort.TopologicalSortStreamConfig;
3636
import org.neo4j.gds.kspanningtree.KSpanningTreeWriteConfig;
37+
import org.neo4j.gds.maxflow.MaxFlowStatsConfig;
3738
import org.neo4j.gds.maxflow.MaxFlowStreamConfig;
3839
import org.neo4j.gds.paths.astar.config.ShortestPathAStarStreamConfig;
3940
import org.neo4j.gds.paths.astar.config.ShortestPathAStarWriteConfig;
@@ -609,6 +610,20 @@ public Stream<PathFindingStreamResult> longestPathStream(String graphName, Map<S
609610
);
610611
}
611612

613+
@Override
614+
public Stream<MaxFlowStatsResult> maxFlowStats(String graphName, Map<String, Object> rawConfiguration) {
615+
var configuration = configurationParser.parseConfiguration(
616+
rawConfiguration,
617+
MaxFlowStatsConfig::of
618+
);
619+
620+
return statsModeBusinessFacade.maxFlow(
621+
GraphName.parse(graphName),
622+
configuration,
623+
new MaxFlowResultBuilderForStatsMode(configuration)
624+
);
625+
}
626+
612627
@Override
613628
public Stream<MaxFlowStreamResult> maxFlowStream(String graphName, Map<String, Object> configuration) {
614629
var resultBuilder = new MaxFlowResultBuilderForStreamMode();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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.procedures.algorithms.pathfinding;
21+
22+
import org.neo4j.gds.api.Graph;
23+
import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTimings;
24+
import org.neo4j.gds.applications.algorithms.machinery.StatsResultBuilder;
25+
import org.neo4j.gds.maxflow.FlowResult;
26+
import org.neo4j.gds.maxflow.MaxFlowStatsConfig;
27+
28+
import java.util.Optional;
29+
import java.util.stream.Stream;
30+
31+
class MaxFlowResultBuilderForStatsMode implements StatsResultBuilder<FlowResult, Stream<MaxFlowStatsResult>> {
32+
private final MaxFlowStatsConfig configuration;
33+
34+
MaxFlowResultBuilderForStatsMode(MaxFlowStatsConfig configuration) {
35+
this.configuration = configuration;
36+
}
37+
38+
@Override
39+
public Stream<MaxFlowStatsResult> build(
40+
Graph graph,
41+
Optional<FlowResult> flowResult,
42+
AlgorithmProcessingTimings timings
43+
) {
44+
return Stream.of(flowResult.map(flowResult1 -> new MaxFlowStatsResult(
45+
timings.preProcessingMillis,
46+
timings.computeMillis,
47+
flowResult1.totalFlow(),
48+
configuration.toMap()
49+
)).orElse(MaxFlowStatsResult.emptyFrom(timings, configuration.toMap())));
50+
}
51+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.maxflow;
21+
22+
import org.neo4j.gds.annotation.Configuration;
23+
import org.neo4j.gds.core.CypherMapWrapper;
24+
25+
@Configuration
26+
public interface MaxFlowStatsConfig extends MaxFlowBaseConfig {
27+
static MaxFlowStatsConfig of(CypherMapWrapper userInput) {
28+
return new MaxFlowStatsConfigImpl(userInput);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.procedures.algorithms.pathfinding;
21+
22+
import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTimings;
23+
import org.neo4j.gds.procedures.algorithms.results.ModeResult;
24+
25+
import java.util.Map;
26+
27+
public record MaxFlowStatsResult(
28+
long preProcessingMillis,
29+
long computeMillis,
30+
double totalFlow,
31+
Map<String, Object> configuration
32+
) implements ModeResult {
33+
34+
35+
static MaxFlowStatsResult emptyFrom(AlgorithmProcessingTimings timings, Map<String, Object> configuration)
36+
{
37+
return new MaxFlowStatsResult(
38+
timings.preProcessingMillis,
39+
timings.computeMillis,
40+
0D,
41+
configuration
42+
);
43+
}
44+
}

procedures/facade-api/path-finding-facade-api/src/main/java/org/neo4j/gds/procedures/algorithms/pathfinding/PathFindingProcedureFacade.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ Stream<MemoryEstimateResult> depthFirstSearchStreamEstimate(
146146

147147
Stream<MaxFlowStreamResult> maxFlowStream(String graphName, Map<String, Object> configuration);
148148

149+
Stream<MaxFlowStatsResult> maxFlowStats(String graphName, Map<String, Object> configuration);
150+
149151
Stream<SpanningTreeStreamResult> prizeCollectingSteinerTreeStream(String graphName, Map<String, Object> configuration);
150152

151153
Stream<PrizeCollectingSteinerTreeMutateResult> prizeCollectingSteinerTreeMutate(String graphName, Map<String, Object> configuration);

0 commit comments

Comments
 (0)