Skip to content

Commit 6a74470

Browse files
Create FlowGraph
Co-authored-by: Ioannis Panagiotas <ioannis.panagiotas@neo4j.com>
1 parent a88a16a commit 6a74470

File tree

5 files changed

+440
-0
lines changed

5 files changed

+440
-0
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
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.apache.commons.lang3.mutable.MutableLong;
23+
import org.neo4j.gds.api.Graph;
24+
import org.neo4j.gds.api.properties.relationships.RelationshipWithPropertyConsumer;
25+
import org.neo4j.gds.collections.ha.HugeDoubleArray;
26+
import org.neo4j.gds.collections.ha.HugeLongArray;
27+
28+
public final class FlowGraph {
29+
private final Graph graph;
30+
private final HugeLongArray indPtr;
31+
private final HugeDoubleArray originalCapacity;
32+
private final HugeDoubleArray flow;
33+
private final HugeLongArray reverseAdjacency;
34+
private final HugeLongArray reverseToRelIdx;
35+
private final HugeLongArray reverseIndPtr;
36+
37+
private FlowGraph(
38+
Graph graph,
39+
HugeLongArray indPtr,
40+
HugeDoubleArray originalCapacity,
41+
HugeDoubleArray flow,
42+
HugeLongArray reverseAdjacency,
43+
HugeLongArray reverseToRelIdx,
44+
HugeLongArray reverseIndPtr
45+
) {
46+
this.graph = graph;
47+
this.indPtr = indPtr;
48+
this.originalCapacity = originalCapacity;
49+
this.flow = flow;
50+
this.reverseAdjacency = reverseAdjacency;
51+
this.reverseToRelIdx = reverseToRelIdx;
52+
this.reverseIndPtr = reverseIndPtr;
53+
}
54+
55+
public static FlowGraph create(Graph graph) {
56+
var reverseDegree = HugeLongArray.newArray(graph.nodeCount());
57+
reverseDegree.setAll(x -> 0L);
58+
59+
for (long nodeId = 0; nodeId < graph.nodeCount(); nodeId++) {
60+
graph.forEachRelationship(
61+
nodeId, 0D, (s, t, capacity) -> {
62+
reverseDegree.addTo(t, 1);
63+
return true;
64+
}
65+
);
66+
}
67+
68+
//Construct CSR ptrs.
69+
var indPtr = HugeLongArray.newArray(graph.nodeCount() + 1);
70+
indPtr.set(0, 0);
71+
var reverseIndPtr = HugeLongArray.newArray(graph.nodeCount() + 1);
72+
reverseIndPtr.set(0, 0);
73+
for (long nodeId = 0; nodeId < graph.nodeCount(); nodeId++) {
74+
indPtr.set(nodeId + 1, indPtr.get(nodeId) + graph.degree(nodeId));
75+
reverseIndPtr.set(nodeId + 1, reverseIndPtr.get(nodeId) + reverseDegree.get(nodeId));
76+
}
77+
78+
var originalCapacity = HugeDoubleArray.newArray(graph.relationshipCount());
79+
var reverseToRelIdx = HugeLongArray.newArray(graph.relationshipCount());
80+
var reverseAdjacency = HugeLongArray.newArray(graph.relationshipCount());
81+
82+
//Populate CSRs
83+
reverseDegree.setAll(x -> 0L); //reuse
84+
var relIdx = new MutableLong(0L);
85+
RelationshipWithPropertyConsumer consumer = (s, t, capacity) -> {
86+
var reverseRelIdx = reverseIndPtr.get(t) + reverseDegree.get(t);
87+
reverseAdjacency.set(reverseRelIdx, s);
88+
reverseToRelIdx.set(reverseRelIdx, relIdx.longValue());
89+
reverseDegree.addTo(t, 1);
90+
originalCapacity.set(relIdx.longValue(), capacity);
91+
relIdx.increment();
92+
return true;
93+
};
94+
for (long nodeId = 0; nodeId < graph.nodeCount(); nodeId++) {
95+
graph.forEachRelationship(nodeId, 0D, consumer);
96+
}
97+
98+
99+
var flow = HugeDoubleArray.newArray(graph.relationshipCount());
100+
flow.setAll(x -> 0D);
101+
102+
return new FlowGraph(graph, indPtr, originalCapacity, flow, reverseAdjacency, reverseToRelIdx, reverseIndPtr);
103+
}
104+
105+
public FlowGraph concurrentCopy() {
106+
return new FlowGraph(
107+
graph.concurrentCopy(),
108+
indPtr,
109+
originalCapacity,
110+
flow,
111+
reverseAdjacency,
112+
reverseToRelIdx,
113+
reverseIndPtr
114+
);
115+
}
116+
117+
private void forEachOriginalRelationship(long nodeId, ResidualEdgeConsumer consumer) {
118+
var relIdx = new MutableLong(indPtr.get(nodeId));
119+
RelationshipWithPropertyConsumer originalConsumer = (s, t, capacity) -> {
120+
var residualCapacity = capacity - flow.get(relIdx.longValue());
121+
var isReverse = false;
122+
consumer.accept(s, t, relIdx.longValue(), residualCapacity, isReverse);
123+
relIdx.increment();
124+
return true;
125+
};
126+
graph.forEachRelationship(nodeId, 0D, originalConsumer);
127+
}
128+
129+
private void forEachReverseRelationship(long nodeId, ResidualEdgeConsumer consumer) {
130+
for (long reverseRelIdx = reverseIndPtr.get(nodeId); reverseRelIdx < reverseIndPtr.get(nodeId + 1); reverseRelIdx++) {
131+
var t = reverseAdjacency.get(reverseRelIdx);
132+
var relIdx = reverseToRelIdx.get(reverseRelIdx);
133+
var residualCapacity = flow.get(relIdx);
134+
var isReverse = true;
135+
consumer.accept(nodeId, t, relIdx, residualCapacity, isReverse);
136+
}
137+
}
138+
139+
public void forEachRelationship(long nodeId, ResidualEdgeConsumer consumer) {
140+
forEachOriginalRelationship(nodeId, consumer);
141+
forEachReverseRelationship(nodeId, consumer);
142+
}
143+
144+
public double flow(long relIdx) {
145+
return flow.get(relIdx);
146+
}
147+
148+
public void push(long relIdx, double delta, boolean isReverse) {
149+
//(s)-[rel]->(t)
150+
if (isReverse) {
151+
flow.addTo(relIdx, -delta);
152+
} else {
153+
flow.addTo(relIdx, delta);
154+
}
155+
}
156+
157+
long edgeCount() {
158+
return graph.relationshipCount();
159+
}
160+
161+
public long nodeCount() {
162+
return graph.nodeCount();
163+
}
164+
165+
long outDegree(long nodeId) {
166+
var degreeFromReverseEdges = reverseIndPtr.get(nodeId) + reverseIndPtr.get(nodeId + 1);
167+
return graph.degree(nodeId) + degreeFromReverseEdges;
168+
}
169+
170+
double residualCapacity(long relIdx, boolean isReverse) {
171+
if (!isReverse) {
172+
return flow.get(relIdx);
173+
}
174+
return originalCapacity.get(relIdx) - flow.get(relIdx);
175+
}
176+
177+
FlowResult createFlowResult(long target) {
178+
var flowResult = new FlowResult(edgeCount());
179+
var idx = new MutableLong(0L);
180+
for (long nodeId = 0; nodeId < nodeCount(); nodeId++) {
181+
var relIdx = new MutableLong(indPtr.get(nodeId));
182+
graph.forEachRelationship(
183+
nodeId,
184+
0D,
185+
(s, t, _capacity) -> {
186+
var flow = this.flow.get(relIdx.longValue());
187+
if (flow > 0.0) {
188+
var flowRelationship = new FlowRelationship(s, t, flow);
189+
flowResult.flow.set(idx.getAndIncrement(), flowRelationship);
190+
if (t == target) {
191+
flowResult.totalFlow += flow;
192+
}
193+
}
194+
relIdx.increment();
195+
196+
return true;
197+
}
198+
);
199+
}
200+
flowResult.chop(idx.longValue());
201+
return flowResult;
202+
}
203+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
public record FlowRelationship(long sourceId, long targetId, double flow) {
23+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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.collections.ha.HugeObjectArray;
23+
24+
public class FlowResult {
25+
HugeObjectArray<FlowRelationship> flow;
26+
double totalFlow;
27+
28+
FlowResult(long edgeCount) {
29+
flow = HugeObjectArray.newArray(FlowRelationship.class, edgeCount);
30+
totalFlow = 0;
31+
}
32+
33+
void chop(long newLength) {
34+
flow = flow.copyOf(newLength);
35+
}
36+
37+
38+
public double totalFlow() {
39+
return totalFlow;
40+
}
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
public interface ResidualEdgeConsumer {
23+
boolean accept(long s, long t, long relIdx, double residualCapacity, boolean isReverse);
24+
25+
default ResidualEdgeConsumer andThen(ResidualEdgeConsumer after) {
26+
return (s, t, relIdx, residualCapacity, isReverse) -> {
27+
this.accept(s, t, relIdx, residualCapacity, isReverse);
28+
return after.accept(s, t, relIdx, residualCapacity, isReverse);
29+
};
30+
}
31+
}

0 commit comments

Comments
 (0)