Skip to content

Commit b33af58

Browse files
Mutate push-back facade
Co-authored-by: Ioannis Panagiotas <ioannis.panagiotas@neo4j.com>
1 parent f9e70bf commit b33af58

File tree

6 files changed

+273
-2
lines changed

6 files changed

+273
-2
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ public Stream<PathFindingStreamResult> longestPathStream(String graphName, Map<S
266266

267267
@Override
268268
public Stream<MaxFlowMutateResult> maxFlowMutate(String graphName, Map<String, Object> configuration) {
269-
return Stream.empty();
269+
return mutateProcedureFacade.maxFlow(graphName, configuration);
270270
}
271271

272272
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.mutate;
21+
22+
import org.neo4j.gds.api.Graph;
23+
import org.neo4j.gds.api.GraphStore;
24+
import org.neo4j.gds.applications.algorithms.metadata.RelationshipsWritten;
25+
import org.neo4j.gds.core.utils.ProgressTimer;
26+
import org.neo4j.gds.maxflow.FlowResult;
27+
import org.neo4j.gds.pathfinding.MaxFlowMutateStep;
28+
import org.neo4j.gds.procedures.algorithms.pathfinding.MaxFlowMutateResult;
29+
import org.neo4j.gds.result.TimedAlgorithmResult;
30+
import org.neo4j.gds.results.ResultTransformer;
31+
32+
import java.util.Map;
33+
import java.util.concurrent.atomic.AtomicLong;
34+
import java.util.stream.Stream;
35+
36+
public class MaxFlowMutateResultTransformer implements ResultTransformer<TimedAlgorithmResult<FlowResult>, Stream<MaxFlowMutateResult>> {
37+
38+
private final MaxFlowMutateStep mutateStep;
39+
private final Graph graph;
40+
private final GraphStore graphStore;
41+
private final Map<String, Object> configuration;
42+
43+
MaxFlowMutateResultTransformer(
44+
MaxFlowMutateStep mutateStep,
45+
Graph graph,
46+
GraphStore graphStore,
47+
Map<String, Object> configuration
48+
) {
49+
this.mutateStep = mutateStep;
50+
this.graph = graph;
51+
this.graphStore = graphStore;
52+
this.configuration = configuration;
53+
}
54+
55+
@Override
56+
public Stream<MaxFlowMutateResult> apply(TimedAlgorithmResult<FlowResult> algorithmResult) {
57+
58+
RelationshipsWritten relationshipsWritten;
59+
var mutateMillis = new AtomicLong();
60+
try (var ignored = ProgressTimer.start(mutateMillis::set)) {
61+
relationshipsWritten = mutateStep.execute(graph, graphStore, algorithmResult.result());
62+
}
63+
var flowResult = algorithmResult.result();
64+
return Stream.of(
65+
new MaxFlowMutateResult(
66+
0,
67+
algorithmResult.computeMillis(),
68+
mutateMillis.get(),
69+
relationshipsWritten.value(),
70+
flowResult.totalFlow(),
71+
configuration
72+
)
73+
);
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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.mutate;
21+
22+
import org.neo4j.gds.applications.algorithms.machinery.MutateRelationshipService;
23+
import org.neo4j.gds.core.loading.GraphResources;
24+
import org.neo4j.gds.maxflow.FlowResult;
25+
import org.neo4j.gds.maxflow.MaxFlowMutateConfig;
26+
import org.neo4j.gds.pathfinding.MaxFlowMutateStep;
27+
import org.neo4j.gds.procedures.algorithms.pathfinding.MaxFlowMutateResult;
28+
import org.neo4j.gds.result.TimedAlgorithmResult;
29+
import org.neo4j.gds.results.ResultTransformerBuilder;
30+
31+
import java.util.stream.Stream;
32+
33+
class MaxFlowMutateResultTransformerBuilder implements ResultTransformerBuilder<TimedAlgorithmResult<FlowResult>, Stream<MaxFlowMutateResult>> {
34+
35+
private final MutateRelationshipService mutateRelationshipService;
36+
private final MaxFlowMutateConfig configuration;
37+
38+
MaxFlowMutateResultTransformerBuilder(
39+
MutateRelationshipService mutateRelationshipService,
40+
MaxFlowMutateConfig configuration
41+
) {
42+
this.mutateRelationshipService = mutateRelationshipService;
43+
this.configuration = configuration;
44+
}
45+
46+
@Override
47+
public MaxFlowMutateResultTransformer build(
48+
GraphResources graphResources
49+
) {
50+
var mutateStep = new MaxFlowMutateStep(
51+
configuration.mutateRelationshipType(),
52+
configuration.mutateProperty(),
53+
mutateRelationshipService
54+
);
55+
return new MaxFlowMutateResultTransformer(
56+
mutateStep,
57+
graphResources.graph(),
58+
graphResources.graphStore(),
59+
configuration.toMap()
60+
);
61+
}
62+
}

procedures/pushback-procedures-facade/src/main/java/org/neo4j/gds/procedures/algorithms/pathfinding/mutate/PushbackPathFindingMutateProcedureFacade.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.neo4j.gds.api.GraphName;
2323
import org.neo4j.gds.applications.algorithms.machinery.MutateNodePropertyService;
2424
import org.neo4j.gds.applications.algorithms.machinery.MutateRelationshipService;
25+
import org.neo4j.gds.maxflow.MaxFlowMutateConfig;
2526
import org.neo4j.gds.pathfinding.PathFindingComputeBusinessFacade;
2627
import org.neo4j.gds.paths.astar.config.ShortestPathAStarMutateConfig;
2728
import org.neo4j.gds.paths.bellmanford.AllShortestPathsBellmanFordMutateConfig;
@@ -34,6 +35,7 @@
3435
import org.neo4j.gds.pcst.PCSTMutateConfig;
3536
import org.neo4j.gds.procedures.algorithms.configuration.UserSpecificConfigurationParser;
3637
import org.neo4j.gds.procedures.algorithms.pathfinding.BellmanFordMutateResult;
38+
import org.neo4j.gds.procedures.algorithms.pathfinding.MaxFlowMutateResult;
3739
import org.neo4j.gds.procedures.algorithms.pathfinding.PathFindingMutateResult;
3840
import org.neo4j.gds.procedures.algorithms.pathfinding.PrizeCollectingSteinerTreeMutateResult;
3941
import org.neo4j.gds.procedures.algorithms.pathfinding.RandomWalkMutateResult;
@@ -137,6 +139,26 @@ public Stream<PathFindingMutateResult> depthFirstSearch(String graphName, Map<St
137139
).join();
138140
}
139141

142+
public Stream<MaxFlowMutateResult> maxFlow(
143+
String graphName,
144+
Map<String, Object> configuration
145+
) {
146+
var config = configurationParser.parseConfiguration(
147+
configuration,
148+
MaxFlowMutateConfig::of
149+
);
150+
151+
return businessFacade.maxFlow(
152+
GraphName.parse(graphName),
153+
config.toGraphParameters(),
154+
config.relationshipWeightProperty(),
155+
config.toParameters(),
156+
config.jobId(),
157+
config.logProgress(),
158+
new MaxFlowMutateResultTransformerBuilder(mutateRelationshipService, config)
159+
).join();
160+
}
161+
140162
public Stream<PrizeCollectingSteinerTreeMutateResult> prizeCollectingSteinerTree(
141163
String graphName,
142164
Map<String, Object> configuration

procedures/pushback-procedures-facade/src/test/java/org/neo4j/gds/procedures/algorithms/pathfinding/PushbackPathFindingProcedureFacadeTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ void depthFirstSearchMutate() {
9292
verifyNoInteractions(statsFacadeMock, streamFacadeMock, writeFacadeMock);
9393
}
9494

95+
@Test
96+
void maxFlowMutate() {
97+
facade.maxFlowMutate(graphName, config);
98+
verify(mutateFacadeMock).maxFlow(graphName, config);
99+
verifyNoInteractions(streamFacadeMock, statsFacadeMock, writeFacadeMock);
100+
}
101+
95102
@Test
96103
void prizeCollectingSteinerTreeMutate() {
97104
facade.prizeCollectingSteinerTreeMutate(graphName, config);
@@ -253,7 +260,7 @@ void longestPathStream() {
253260
}
254261

255262
@Test
256-
void maxFlow() {
263+
void maxFlowStream() {
257264
facade.maxFlowStream(graphName, config);
258265
verify(streamFacadeMock).maxFlow(graphName, config);
259266
verifyNoInteractions(mutateFacadeMock, statsFacadeMock, writeFacadeMock);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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.mutate;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.neo4j.gds.api.Graph;
24+
import org.neo4j.gds.api.GraphStore;
25+
import org.neo4j.gds.applications.algorithms.machinery.MutateRelationshipService;
26+
import org.neo4j.gds.applications.algorithms.metadata.RelationshipsWritten;
27+
import org.neo4j.gds.logging.Log;
28+
import org.neo4j.gds.maxflow.FlowResult;
29+
import org.neo4j.gds.pathfinding.MaxFlowMutateStep;
30+
import org.neo4j.gds.result.TimedAlgorithmResult;
31+
32+
import java.util.Map;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.mockito.ArgumentMatchers.any;
36+
import static org.mockito.Mockito.mock;
37+
import static org.mockito.Mockito.times;
38+
import static org.mockito.Mockito.verify;
39+
import static org.mockito.Mockito.verifyNoMoreInteractions;
40+
import static org.mockito.Mockito.when;
41+
42+
class MaxFlowMutateResultTransformerTest {
43+
@Test
44+
void shouldTransformToMutateResult() {
45+
var config = Map.<String, Object>of("foo", "bar");
46+
var graph = mock(Graph.class);
47+
var graphStore = mock(GraphStore.class);
48+
var mutateStep = mock(MaxFlowMutateStep.class);
49+
50+
var algoResult = mock(FlowResult.class);
51+
when(algoResult.totalFlow()).thenReturn(3D);
52+
53+
54+
var relationshipsWritten = new RelationshipsWritten(5L);
55+
when(mutateStep.execute(any(), any(), any())).thenReturn(relationshipsWritten);
56+
57+
var timedResult = new TimedAlgorithmResult<>(algoResult, 123L);
58+
59+
var transformer = new MaxFlowMutateResultTransformer(mutateStep, graph, graphStore, config);
60+
61+
var resultStream = transformer.apply(timedResult);
62+
var result = resultStream.findFirst().orElseThrow();
63+
64+
assertThat(result.preProcessingMillis()).isZero();
65+
assertThat(result.computeMillis()).isEqualTo(123L);
66+
assertThat(result.mutateMillis()).isNotNegative();
67+
assertThat(result.configuration()).isEqualTo(config);
68+
assertThat(result.totalFlow()).isEqualTo(3D);
69+
70+
assertThat(result.relationshipsWritten()).isEqualTo(5L);
71+
72+
verify(mutateStep, times(1)).execute(graph, graphStore, algoResult);
73+
verifyNoMoreInteractions(mutateStep);
74+
}
75+
76+
@Test
77+
void shouldTransformEmptyResultToMutateResult() {
78+
var config = Map.<String, Object>of("boo", "foo");
79+
var graph = mock(Graph.class);
80+
var graphStore = mock(GraphStore.class);
81+
var mutateRelationshipService = new MutateRelationshipService(Log.noOpLog());
82+
var mutateStep = new MaxFlowMutateStep(
83+
"r",
84+
"foo",
85+
mutateRelationshipService
86+
);
87+
88+
var algoResult = FlowResult.EMPTY;
89+
90+
var timedResult = new TimedAlgorithmResult<>(algoResult, 123L);
91+
92+
var transformer = new MaxFlowMutateResultTransformer(mutateStep, graph, graphStore, config);
93+
94+
var resultStream = transformer.apply(timedResult);
95+
var result = resultStream.findFirst().orElseThrow();
96+
97+
assertThat(result.preProcessingMillis()).isZero();
98+
assertThat(result.computeMillis()).isEqualTo(123L);
99+
assertThat(result.mutateMillis()).isNotNegative();
100+
assertThat(result.configuration()).isEqualTo(config);
101+
assertThat(result.totalFlow()).isEqualTo(0D);
102+
assertThat(result.relationshipsWritten()).isEqualTo(0);
103+
104+
}
105+
}

0 commit comments

Comments
 (0)