Skip to content

Commit c16aa5d

Browse files
committed
Handle filter triangle count edge cases
1 parent b6ba416 commit c16aa5d

File tree

12 files changed

+256
-94
lines changed

12 files changed

+256
-94
lines changed

algo/src/main/java/org/neo4j/gds/triangle/IntersectingTriangleCount.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ public final class IntersectingTriangleCount extends Algorithm<TriangleCountResu
6868
private final Optional<NodeLabel> ALabel;
6969
private final Optional<NodeLabel> BLabel;
7070
private final Optional<NodeLabel> CLabel;
71+
private final boolean bTraversal;
72+
private final boolean cTraversal;
7173
private long globalTriangleCount;
7274

7375
private final LongAdder globalTriangleCounter;
@@ -130,6 +132,12 @@ private IntersectingTriangleCount(
130132
this.globalTriangleCounter = new LongAdder();
131133
this.queue = new AtomicLong();
132134

135+
this.bTraversal = ALabel.isPresent() && ((BLabel.isPresent() && !ALabel.get()
136+
.equals(BLabel.get())) || BLabel.isEmpty());
137+
this.cTraversal = (ALabel.isPresent() && ((CLabel.isPresent() && !ALabel.get()
138+
.equals(CLabel.get())) || CLabel.isEmpty()) && (BLabel.isPresent() && ((CLabel.isPresent() && !BLabel.get()
139+
.equals(CLabel.get())) || CLabel.isEmpty())));
140+
133141
this.terminationFlag = terminationFlag;
134142
}
135143

@@ -139,11 +147,10 @@ public TriangleCountResult compute() {
139147
queue.set(0);
140148
globalTriangleCounter.reset();
141149

142-
boolean filtered = ALabel.isPresent() || BLabel.isPresent() || CLabel.isPresent();
143150
// create tasks
144151
final Collection<? extends Runnable> tasks = ParallelUtil.tasks(
145152
concurrency,
146-
() -> new IntersectTask(intersectFactory.load(graph, maxDegree, BLabel, CLabel, filtered))
153+
() -> new IntersectTask(intersectFactory.load(graph, maxDegree))
147154
);
148155
// run
149156
ParallelUtil.run(tasks, executorService);
@@ -171,7 +178,13 @@ public void run() {
171178
while ((node = queue.getAndIncrement()) < graph.nodeCount() && terminationFlag.running()) {
172179
if (graph.degree(node) <= maxDegree) {
173180
if (ALabel.isEmpty() || graph.hasLabel(node, ALabel.get())) {
174-
intersect.intersectAll(node, this);
181+
intersect.intersectAll(node, this, BLabel, CLabel);
182+
}
183+
if (bTraversal && (BLabel.isEmpty() || graph.hasLabel(node, BLabel.get()))) {
184+
intersect.intersectAll(node, this, CLabel, ALabel);
185+
}
186+
if (cTraversal && (CLabel.isEmpty() || graph.hasLabel(node, CLabel.get()))) {
187+
intersect.intersectAll(node, this, ALabel, BLabel);
175188
}
176189
} else {
177190
triangleCounts.set(node, EXCLUDED_NODE_TRIANGLE_COUNT);

algo/src/main/java/org/neo4j/gds/triangle/TriangleStream.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ private void submitTasks() {
143143
queue.set(0);
144144
runningThreads.set(0);
145145
final Collection<Runnable> tasks;
146-
boolean filtered = ALabel.isPresent() || BLabel.isPresent() || CLabel.isPresent();
147146
tasks = ParallelUtil.tasks(
148147
concurrency,
149-
() -> new IntersectTask(intersectFactory.load(graph, Long.MAX_VALUE, BLabel, CLabel, filtered))
148+
() -> new IntersectTask(intersectFactory.load(graph, Long.MAX_VALUE))
150149
);
151150
ParallelUtil.run(tasks, false, executorService, null);
152151
}
@@ -163,7 +162,13 @@ public final void run() {
163162
int node;
164163
while ((node = queue.getAndIncrement()) < nodeCount && terminationFlag.running()) {
165164
if (ALabel.isEmpty() || graph.hasLabel(node, ALabel.get())) {
166-
evaluateNode(node);
165+
evaluateNode(node, BLabel, CLabel);
166+
}
167+
if (BLabel.isEmpty() || graph.hasLabel(node, BLabel.get())) {
168+
evaluateNode(node, CLabel, ALabel);
169+
}
170+
if (CLabel.isEmpty() || graph.hasLabel(node, CLabel.get())) {
171+
evaluateNode(node, ALabel, BLabel);
167172
}
168173
progressTracker.logProgress();
169174
}
@@ -172,7 +177,7 @@ public final void run() {
172177
}
173178
}
174179

175-
abstract void evaluateNode(int nodeId);
180+
abstract void evaluateNode(int nodeId, Optional<NodeLabel> blabel, Optional<NodeLabel> cLabel);
176181

177182
void emit(long nodeA, long nodeB, long nodeC) {
178183
var result = new TriangleResult(
@@ -193,8 +198,8 @@ private final class IntersectTask extends BaseTask implements IntersectionConsum
193198
}
194199

195200
@Override
196-
void evaluateNode(final int nodeId) {
197-
intersect.intersectAll(nodeId, this);
201+
void evaluateNode(final int nodeId, Optional<NodeLabel> bLabel, Optional<NodeLabel> cLabel) {
202+
intersect.intersectAll(nodeId, this, bLabel, cLabel);
198203
}
199204

200205
@Override

algo/src/main/java/org/neo4j/gds/triangle/intersect/GraphIntersect.java

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,33 +41,29 @@
4141
public abstract class GraphIntersect<CURSOR extends AdjacencyCursor> implements RelationshipIntersect {
4242

4343
private final IntPredicate degreeFilter;
44-
private final Optional<NodeLabel> BLabel;
45-
private final Optional<NodeLabel> CLabel;
4644
private final BiFunction<Long, NodeLabel, Boolean> hasLabel;
47-
private final boolean filtered;
4845
private CURSOR origNeighborsOfa;
4946
private CURSOR helpingCursorOfa;
5047
private CURSOR helpingCursorOfb;
5148

5249

5350
protected GraphIntersect(
5451
long maxDegree,
55-
Optional<NodeLabel> BLabel,
56-
Optional<NodeLabel> CLabel,
57-
BiFunction<Long, NodeLabel, Boolean> hasLabel,
58-
boolean filtered
52+
BiFunction<Long, NodeLabel, Boolean> hasLabel
5953
) {
6054
this.degreeFilter = maxDegree < Long.MAX_VALUE
6155
? (degree) -> degree <= maxDegree
6256
: (ignore) -> true;
63-
this.BLabel = BLabel;
64-
this.CLabel = CLabel;
6557
this.hasLabel = hasLabel;
66-
this.filtered = filtered;
6758
}
6859

6960
@Override
70-
public void intersectAll(long a, IntersectionConsumer consumer) {
61+
public void intersectAll(
62+
long a,
63+
IntersectionConsumer consumer,
64+
Optional<NodeLabel> bLabel,
65+
Optional<NodeLabel> cLabel
66+
) {
7167
// check the first node's degree
7268
int degreeOfa = degree(a);
7369
if (!degreeFilter.test(degreeOfa)) {
@@ -76,18 +72,23 @@ public void intersectAll(long a, IntersectionConsumer consumer) {
7672

7773
origNeighborsOfa = cursorForNode(origNeighborsOfa, a, degreeOfa);
7874

79-
triangles(a, degreeOfa, origNeighborsOfa, consumer);
75+
triangles(a, degreeOfa, origNeighborsOfa, consumer, bLabel, cLabel);
8076
}
8177

8278
private void triangles(
8379
long a,
8480
int degreeOfa,
8581
CURSOR neighborsOfa,
86-
IntersectionConsumer consumer
82+
IntersectionConsumer consumer,
83+
Optional<NodeLabel> bLabel,
84+
Optional<NodeLabel> cLabel
8785
) {
8886
long b = AdjacencyCursorUtils.next(neighborsOfa);
89-
while (b != NOT_FOUND && (b < a || filtered)) {
90-
if (BLabel.isEmpty() || hasLabel.apply(b, BLabel.get())) {
87+
boolean cTraversal = bLabel.isPresent() && ((cLabel.isPresent() && !bLabel.get()
88+
.equals(cLabel.get())) || cLabel.isEmpty());
89+
90+
while (b != NOT_FOUND && (b < a)) {
91+
if (bLabel.isEmpty() || hasLabel.apply(b, bLabel.get())) {
9192
var degreeOfb = degree(b);
9293
if (degreeFilter.test(degreeOfb)) {
9394
helpingCursorOfb = cursorForNode(
@@ -103,21 +104,52 @@ private void triangles(
103104
b,
104105
helpingCursorOfa,
105106
helpingCursorOfb,
106-
consumer
107+
consumer,
108+
cLabel
107109
); //find all triangles involving the edge (a-b)
108110
}
109111
}
112+
if (cTraversal) {
113+
if (cLabel.isEmpty() || hasLabel.apply(b, cLabel.get())) {
114+
var degreeOfb = degree(b);
115+
if (degreeFilter.test(degreeOfb)) {
116+
helpingCursorOfb = cursorForNode(
117+
helpingCursorOfb,
118+
b,
119+
degreeOfb
120+
);
121+
122+
helpingCursorOfa = cursorForNode(helpingCursorOfa, a, degreeOfa);
123+
124+
triangles(
125+
a,
126+
b,
127+
helpingCursorOfa,
128+
helpingCursorOfb,
129+
consumer,
130+
bLabel
131+
); //find all triangles involving the edge (a-b)
132+
}
133+
}
134+
}
110135

111136
b = AdjacencyCursorUtils.next(neighborsOfa);
112137
}
113138

114139
}
115140

116-
private void triangles(long a, long b, CURSOR neighborsOfa, CURSOR neighborsOfb, IntersectionConsumer consumer) {
141+
private void triangles(
142+
long a,
143+
long b,
144+
CURSOR neighborsOfa,
145+
CURSOR neighborsOfb,
146+
IntersectionConsumer consumer,
147+
Optional<NodeLabel> cLabel
148+
) {
117149
long c = AdjacencyCursorUtils.next(neighborsOfb);
118150
long currentOfa = AdjacencyCursorUtils.next(neighborsOfa);
119-
while (c != NOT_FOUND && currentOfa != NOT_FOUND && (c < b || filtered)) {
120-
if (CLabel.isEmpty() || hasLabel.apply(c, CLabel.get())) {
151+
while (c != NOT_FOUND && currentOfa != NOT_FOUND && (c < b)) {
152+
if (cLabel.isEmpty() || hasLabel.apply(c, cLabel.get())) {
121153
var degreeOfc = degree(c);
122154
if (degreeFilter.test(degreeOfc)) {
123155
currentOfa = AdjacencyCursorUtils.advance(neighborsOfa, currentOfa, c);

algo/src/main/java/org/neo4j/gds/triangle/intersect/HugeGraphIntersect.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,9 @@ public final class HugeGraphIntersect extends GraphIntersect<AdjacencyCursor> {
3838
private HugeGraphIntersect(
3939
AdjacencyList adjacency,
4040
long maxDegree,
41-
Optional<NodeLabel> BLabel,
42-
Optional<NodeLabel> CLabel,
43-
BiFunction<Long, NodeLabel, Boolean> hasLabel,
44-
boolean filtered
41+
BiFunction<Long, NodeLabel, Boolean> hasLabel
4542
) {
46-
super(maxDegree, BLabel, CLabel, hasLabel, filtered);
43+
super(maxDegree, hasLabel);
4744
this.adjacencyList = adjacency;
4845
}
4946

@@ -68,15 +65,12 @@ public boolean canLoad(Graph graph) {
6865
@Override
6966
public RelationshipIntersect load(
7067
Graph graph,
71-
long maxDegree,
72-
Optional<NodeLabel> BLabel,
73-
Optional<NodeLabel> CLabel,
74-
boolean filtered
68+
long maxDegree
7569
) {
7670
assert graph instanceof HugeGraph;
7771
var hugeGraph = (HugeGraph) graph;
7872
var topology = hugeGraph.relationshipTopology().adjacencyList();
79-
return new HugeGraphIntersect(topology, maxDegree, BLabel, CLabel, graph::hasLabel, filtered);
73+
return new HugeGraphIntersect(topology, maxDegree, graph::hasLabel);
8074
}
8175
}
8276
}

algo/src/main/java/org/neo4j/gds/triangle/intersect/NodeFilteredGraphIntersect.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private NodeFilteredGraphIntersect(
4848
}
4949

5050
@Override
51-
public void intersectAll(long nodeIdA, IntersectionConsumer consumer) {
51+
public void intersectAll(long nodeIdA, IntersectionConsumer consumer, Optional<NodeLabel> bLabel, Optional<NodeLabel> cLabel) {
5252
wrappedRelationshipIntersect.intersectAll(
5353
filteredGraph.toRootNodeId(nodeIdA), (a, b, c) -> {
5454
if (filteredGraph.containsRootNodeId(a) && filteredGraph.containsRootNodeId(b) && filteredGraph.containsRootNodeId(
@@ -59,7 +59,9 @@ public void intersectAll(long nodeIdA, IntersectionConsumer consumer) {
5959
filteredGraph.toFilteredNodeId(c)
6060
);
6161
}
62-
}
62+
},
63+
bLabel,
64+
cLabel
6365
);
6466
}
6567

@@ -74,10 +76,7 @@ public boolean canLoad(Graph graph) {
7476
@Override
7577
public RelationshipIntersect load(
7678
Graph graph,
77-
long maxDegree,
78-
Optional<NodeLabel> BLabel,
79-
Optional<NodeLabel> CLabel,
80-
boolean filtered
79+
long maxDegree
8180
) {
8281
assert graph instanceof NodeFilteredGraph;
8382
var nodeFilteredGraph = (NodeFilteredGraph) graph;
@@ -86,7 +85,7 @@ public RelationshipIntersect load(
8685
var relationshipIntersect = RelationshipIntersectFactoryLocator
8786
.lookup(innerGraph)
8887
.orElseThrow(() -> new IllegalArgumentException("No intersect factory found for graph type " + innerGraph.getClass()))
89-
.load(innerGraph, maxDegree, BLabel, CLabel, filtered);
88+
.load(innerGraph, maxDegree);
9089

9190
return new NodeFilteredGraphIntersect(nodeFilteredGraph, relationshipIntersect);
9291
}

algo/src/main/java/org/neo4j/gds/triangle/intersect/RelationshipIntersectFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public interface RelationshipIntersectFactory {
3131

3232
boolean canLoad(Graph graph);
3333

34-
RelationshipIntersect load(Graph graph, long maxDegree, Optional<NodeLabel> BLabel, Optional<NodeLabel> CLabel,
35-
boolean filtered
34+
RelationshipIntersect load(
35+
Graph graph,
36+
long maxDegree
3637
);
3738
}

algo/src/main/java/org/neo4j/gds/triangle/intersect/UnionGraphIntersect.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,9 @@ private UnionGraphIntersect(
4343
LongToLongFunction fromFilteredIdFunction,
4444
CompositeAdjacencyList compositeAdjacencyList,
4545
long maxDegree,
46-
Optional<NodeLabel> BLabel,
47-
Optional<NodeLabel> CLabel,
48-
BiFunction<Long, NodeLabel, Boolean> hasLabel,
49-
boolean filtered
46+
BiFunction<Long, NodeLabel, Boolean> hasLabel
5047
) {
51-
super(maxDegree, BLabel, CLabel, hasLabel, filtered);
48+
super(maxDegree, hasLabel);
5249
this.degreeFunction = degreeFunction;
5350
this.fromFilteredIdFunction = fromFilteredIdFunction;
5451
this.compositeAdjacencyList = compositeAdjacencyList;
@@ -78,10 +75,7 @@ public boolean canLoad(Graph graph) {
7875
@Override
7976
public UnionGraphIntersect load(
8077
Graph graph,
81-
long maxDegree,
82-
Optional<NodeLabel> BLabel,
83-
Optional<NodeLabel> CLabel,
84-
boolean filtered
78+
long maxDegree
8579
) {
8680
assert graph instanceof UnionGraph;
8781
var topology = ((UnionGraph) graph).relationshipTopology();
@@ -90,10 +84,7 @@ public UnionGraphIntersect load(
9084
i -> i,
9185
topology,
9286
maxDegree,
93-
BLabel,
94-
CLabel,
95-
graph::hasLabel,
96-
filtered
87+
graph::hasLabel
9788
);
9889
}
9990
}
@@ -112,10 +103,7 @@ public boolean canLoad(Graph graph) {
112103
@Override
113104
public UnionGraphIntersect load(
114105
Graph graph,
115-
long maxDegree,
116-
Optional<NodeLabel> BLabel,
117-
Optional<NodeLabel> CLabel,
118-
boolean filtered
106+
long maxDegree
119107
) {
120108
assert graph instanceof UnionGraph;
121109
var topology = ((UnionGraph) graph).relationshipTopology();
@@ -124,10 +112,7 @@ public UnionGraphIntersect load(
124112
graph::toRootNodeId,
125113
topology,
126114
maxDegree,
127-
BLabel,
128-
CLabel,
129-
graph::hasLabel,
130-
filtered
115+
graph::hasLabel
131116
);
132117
}
133118
}

0 commit comments

Comments
 (0)