Skip to content

Commit 5770bfe

Browse files
committed
Introduce LabelFilterChecker
Co-Authored-By: Ioannis Panagiotas <ioannis.panagiotas@neotechnology.com>
1 parent 8b8d35f commit 5770bfe

File tree

12 files changed

+139
-157
lines changed

12 files changed

+139
-157
lines changed

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

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.neo4j.gds.triangle;
2121

2222
import org.neo4j.gds.Algorithm;
23-
import org.neo4j.gds.NodeLabel;
2423
import org.neo4j.gds.api.Graph;
2524
import org.neo4j.gds.api.IntersectionConsumer;
2625
import org.neo4j.gds.api.RelationshipIntersect;
@@ -35,7 +34,6 @@
3534

3635
import java.util.Collection;
3736
import java.util.List;
38-
import java.util.Optional;
3937
import java.util.concurrent.ExecutorService;
4038
import java.util.concurrent.atomic.AtomicLong;
4139
import java.util.concurrent.atomic.LongAdder;
@@ -66,9 +64,7 @@ public final class IntersectingTriangleCount extends Algorithm<TriangleCountResu
6664
private final HugeAtomicLongArray triangleCounts;
6765
private final long maxDegree;
6866
private final Concurrency concurrency;
69-
private final Optional<NodeLabel> aLabel;
70-
private final Optional<NodeLabel> bLabel;
71-
private final Optional<NodeLabel> cLabel;
67+
private final LabelFilterChecker labelFilterChecker;
7268
private long globalTriangleCount;
7369

7470
private final LongAdder globalTriangleCounter;
@@ -122,21 +118,7 @@ private IntersectingTriangleCount(
122118
this.globalTriangleCounter = new LongAdder();
123119
this.queue = new AtomicLong();
124120

125-
if (!labelFilter.isEmpty()) {
126-
this.aLabel = Optional.of(NodeLabel.of(labelFilter.getFirst()));
127-
} else {
128-
this.aLabel = Optional.empty();
129-
}
130-
if (labelFilter.size() > 1) {
131-
this.bLabel = Optional.of(NodeLabel.of(labelFilter.get(1)));
132-
} else {
133-
this.bLabel = Optional.empty();
134-
}
135-
if (labelFilter.size() > 2) {
136-
this.cLabel = Optional.of(NodeLabel.of(labelFilter.get(2)));
137-
} else {
138-
this.cLabel = Optional.empty();
139-
}
121+
this.labelFilterChecker = new LabelFilterChecker(labelFilter, graph::hasLabel);
140122

141123
this.terminationFlag = terminationFlag;
142124
}
@@ -150,7 +132,7 @@ public TriangleCountResult compute() {
150132
// create tasks
151133
final Collection<? extends Runnable> tasks = ParallelUtil.tasks(
152134
concurrency,
153-
() -> new IntersectTask(intersectFactory.load(graph, maxDegree, this.aLabel, this.bLabel, this.cLabel))
135+
() -> new IntersectTask(intersectFactory.load(graph, maxDegree, labelFilterChecker))
154136
);
155137
// run
156138
ParallelUtil.run(tasks, executorService);
@@ -177,10 +159,7 @@ public void run() {
177159
long node;
178160
while ((node = queue.getAndIncrement()) < graph.nodeCount() && terminationFlag.running()) {
179161
if (graph.degree(node) <= maxDegree) {
180-
if (cLabel.isEmpty() || bLabel.isEmpty() || aLabel.isEmpty()
181-
|| graph.hasLabel(node, aLabel.get())
182-
|| graph.hasLabel(node, bLabel.get())
183-
|| graph.hasLabel(node, cLabel.get())) {
162+
if (labelFilterChecker.check(node)) {
184163
intersect.intersectAll(node, this);
185164
}
186165
} else {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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.triangle;
21+
22+
import org.neo4j.gds.NodeLabel;
23+
24+
import java.util.List;
25+
import java.util.Optional;
26+
import java.util.function.BiFunction;
27+
28+
public class LabelFilterChecker {
29+
30+
private final Optional<NodeLabel> aLabel;
31+
private final Optional<NodeLabel> bLabel;
32+
private final Optional<NodeLabel> cLabel;
33+
private final BiFunction<Long, NodeLabel, Boolean> hasLabel;
34+
35+
public LabelFilterChecker(List<String> labelFilter, BiFunction<Long, NodeLabel, Boolean> hasLabel) {
36+
this.hasLabel = hasLabel;
37+
if (!labelFilter.isEmpty()) {
38+
this.aLabel = Optional.of(NodeLabel.of(labelFilter.getFirst()));
39+
} else {
40+
this.aLabel = Optional.empty();
41+
}
42+
if (labelFilter.size() > 1) {
43+
this.bLabel = Optional.of(NodeLabel.of(labelFilter.get(1)));
44+
} else {
45+
this.bLabel = Optional.empty();
46+
}
47+
if (labelFilter.size() > 2) {
48+
this.cLabel = Optional.of(NodeLabel.of(labelFilter.get(2)));
49+
} else {
50+
this.cLabel = Optional.empty();
51+
}
52+
}
53+
54+
public boolean check(long nodeId) {
55+
return cLabel.isEmpty() || bLabel.isEmpty() || aLabel.isEmpty()
56+
|| hasLabel.apply(nodeId, aLabel.get())
57+
|| hasLabel.apply(nodeId, bLabel.get())
58+
|| hasLabel.apply(nodeId, cLabel.get());
59+
}
60+
61+
public boolean check(long aNodeId, long bNodeId) {
62+
// No filters
63+
return (aLabel.isEmpty() && bLabel.isEmpty() && cLabel.isEmpty())
64+
// One filter
65+
|| (bLabel.isEmpty() && cLabel.isEmpty() && (hasLabel.apply(aNodeId, aLabel.get()) || hasLabel.apply(bNodeId, aLabel.get())))
66+
// Two filters
67+
|| (cLabel.isEmpty() && aLabel.isPresent() && bLabel.isPresent()
68+
&& (hasLabel.apply(aNodeId, aLabel.get()) || hasLabel.apply(aNodeId, bLabel.get()) || hasLabel.apply(bNodeId, aLabel.get()) && hasLabel.apply(bNodeId, bLabel.get())))
69+
// Three filters
70+
|| (aLabel.isPresent() && bLabel.isPresent() && cLabel.isPresent()
71+
&& ((hasLabel.apply(aNodeId, aLabel.get()) && hasLabel.apply(bNodeId, bLabel.get()))
72+
|| (hasLabel.apply(aNodeId, aLabel.get()) && hasLabel.apply(bNodeId, cLabel.get()))
73+
|| (hasLabel.apply(aNodeId, bLabel.get()) && hasLabel.apply(bNodeId, aLabel.get()))
74+
|| (hasLabel.apply(aNodeId, bLabel.get()) && hasLabel.apply(bNodeId, cLabel.get()))
75+
|| (hasLabel.apply(aNodeId, cLabel.get()) && hasLabel.apply(bNodeId, aLabel.get()))
76+
|| (hasLabel.apply(aNodeId, cLabel.get()) && hasLabel.apply(bNodeId, bLabel.get()))));
77+
}
78+
79+
public boolean check(long aNodeId, long bNodeId, long cNodeId) {
80+
// No filters
81+
return (aLabel.isEmpty() && bLabel.isEmpty() && cLabel.isEmpty())
82+
// One filter
83+
|| (bLabel.isEmpty() && cLabel.isEmpty() && (hasLabel.apply(aNodeId, aLabel.get()) || hasLabel.apply(bNodeId, aLabel.get()) || hasLabel.apply(cNodeId, aLabel.get())))
84+
// Two filters
85+
|| (cLabel.isEmpty() && aLabel.isPresent() && bLabel.isPresent()
86+
&& ((hasLabel.apply(aNodeId, bLabel.get()) && (hasLabel.apply(bNodeId, aLabel.get()) || hasLabel.apply(cNodeId, aLabel.get())))
87+
|| (hasLabel.apply(bNodeId, bLabel.get()) && (hasLabel.apply(aNodeId, aLabel.get()) || hasLabel.apply(cNodeId, aLabel.get())))
88+
|| (hasLabel.apply(cNodeId, bLabel.get()) && (hasLabel.apply(aNodeId, aLabel.get()) || hasLabel.apply(bNodeId, aLabel.get())))))
89+
// Three filters
90+
|| (aLabel.isPresent() && bLabel.isPresent() && cLabel.isPresent())
91+
&& ((hasLabel.apply(aNodeId, aLabel.get()) && hasLabel.apply(bNodeId, bLabel.get()) && hasLabel.apply(cNodeId, cLabel.get()))
92+
|| (hasLabel.apply(aNodeId, aLabel.get()) && hasLabel.apply(cNodeId, aLabel.get()) && hasLabel.apply(bNodeId, cLabel.get()))
93+
|| (hasLabel.apply(bNodeId, aLabel.get()) && hasLabel.apply(aNodeId, bLabel.get()) && hasLabel.apply(cNodeId, cLabel.get()))
94+
|| (hasLabel.apply(bNodeId, aLabel.get()) && hasLabel.apply(cNodeId, bLabel.get()) && hasLabel.apply(aNodeId, cLabel.get()))
95+
|| (hasLabel.apply(cNodeId, aLabel.get()) && hasLabel.apply(aNodeId, bLabel.get()) && hasLabel.apply(bNodeId, cLabel.get()))
96+
|| (hasLabel.apply(cNodeId, aLabel.get()) && hasLabel.apply(bNodeId, bLabel.get()) && hasLabel.apply(aNodeId, cLabel.get())));
97+
}
98+
}

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

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import com.carrotsearch.hppc.AbstractIterator;
2323
import org.neo4j.gds.Algorithm;
24-
import org.neo4j.gds.NodeLabel;
2524
import org.neo4j.gds.api.Graph;
2625
import org.neo4j.gds.api.IntersectionConsumer;
2726
import org.neo4j.gds.api.RelationshipIntersect;
@@ -36,7 +35,6 @@
3635
import java.util.Iterator;
3736
import java.util.List;
3837
import java.util.Objects;
39-
import java.util.Optional;
4038
import java.util.Spliterators;
4139
import java.util.concurrent.ArrayBlockingQueue;
4240
import java.util.concurrent.BlockingQueue;
@@ -58,12 +56,10 @@ public final class TriangleStream extends Algorithm<Stream<TriangleResult>> {
5856
private final ExecutorService executorService;
5957
private final AtomicInteger queue;
6058
private final Concurrency concurrency;
61-
private final Optional<NodeLabel> aLabel;
62-
private final Optional<NodeLabel> bLabel;
63-
private final Optional<NodeLabel> cLabel;
6459
private final int nodeCount;
6560
private final AtomicInteger runningThreads;
6661
private final BlockingQueue<TriangleResult> resultQueue;
62+
private final LabelFilterChecker labelFilterChecker;
6763

6864
public static TriangleStream create(
6965
Graph graph,
@@ -104,23 +100,7 @@ private TriangleStream(
104100
this.resultQueue = new ArrayBlockingQueue<>(concurrency.value() << 10);
105101
this.runningThreads = new AtomicInteger();
106102
this.queue = new AtomicInteger();
107-
108-
if (!labelFilter.isEmpty()) {
109-
this.aLabel = Optional.of(NodeLabel.of(labelFilter.getFirst()));
110-
} else {
111-
this.aLabel = Optional.empty();
112-
}
113-
if (labelFilter.size() > 1) {
114-
this.bLabel = Optional.of(NodeLabel.of(labelFilter.get(1)));
115-
} else {
116-
this.bLabel = Optional.empty();
117-
}
118-
if (labelFilter.size() > 2) {
119-
this.cLabel = Optional.of(NodeLabel.of(labelFilter.get(2)));
120-
} else {
121-
this.cLabel = Optional.empty();
122-
}
123-
103+
this.labelFilterChecker = new LabelFilterChecker(labelFilter, graph::hasLabel);
124104
this.terminationFlag = terminationFlag;
125105
}
126106

@@ -153,7 +133,7 @@ private void submitTasks() {
153133
final Collection<Runnable> tasks;
154134
tasks = ParallelUtil.tasks(
155135
concurrency,
156-
() -> new IntersectTask(intersectFactory.load(graph, Long.MAX_VALUE, this.aLabel, this.bLabel, this.cLabel))
136+
() -> new IntersectTask(intersectFactory.load(graph, Long.MAX_VALUE, labelFilterChecker))
157137
);
158138
ParallelUtil.run(tasks, false, executorService, null);
159139
}
@@ -169,10 +149,7 @@ public final void run() {
169149
try {
170150
int node;
171151
while ((node = queue.getAndIncrement()) < nodeCount && terminationFlag.running()) {
172-
if (cLabel.isEmpty() || bLabel.isEmpty() || aLabel.isEmpty()
173-
|| graph.hasLabel(node, aLabel.get())
174-
|| graph.hasLabel(node, bLabel.get())
175-
|| graph.hasLabel(node, cLabel.get())) {
152+
if (labelFilterChecker.check(node)) {
176153
evaluateNode(node);
177154
}
178155
progressTracker.logProgress();

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

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@
2020
package org.neo4j.gds.triangle.intersect;
2121

2222
import org.jetbrains.annotations.Nullable;
23-
import org.neo4j.gds.NodeLabel;
2423
import org.neo4j.gds.api.AdjacencyCursor;
2524
import org.neo4j.gds.api.AdjacencyCursorUtils;
2625
import org.neo4j.gds.api.IntersectionConsumer;
2726
import org.neo4j.gds.api.RelationshipIntersect;
27+
import org.neo4j.gds.triangle.LabelFilterChecker;
2828

29-
import java.util.Optional;
30-
import java.util.function.BiFunction;
3129
import java.util.function.IntPredicate;
3230

3331
import static org.neo4j.gds.api.AdjacencyCursor.NOT_FOUND;
@@ -41,29 +39,20 @@
4139
public abstract class GraphIntersect<CURSOR extends AdjacencyCursor> implements RelationshipIntersect {
4240

4341
private final IntPredicate degreeFilter;
44-
private final BiFunction<Long, NodeLabel, Boolean> hasLabel;
45-
private final Optional<NodeLabel> aLabel;
46-
private final Optional<NodeLabel> bLabel;
47-
private final Optional<NodeLabel> cLabel;
42+
private final LabelFilterChecker labelFilterChecker;
4843
private CURSOR origNeighborsOfa;
4944
private CURSOR helpingCursorOfa;
5045
private CURSOR helpingCursorOfb;
5146

5247

5348
protected GraphIntersect(
5449
long maxDegree,
55-
BiFunction<Long, NodeLabel, Boolean> hasLabel,
56-
Optional<NodeLabel> aLabel,
57-
Optional<NodeLabel> bLabel,
58-
Optional<NodeLabel> cLabel
50+
LabelFilterChecker labelFilterChecker
5951
) {
6052
this.degreeFilter = maxDegree < Long.MAX_VALUE
6153
? (degree) -> degree <= maxDegree
6254
: (ignore) -> true;
63-
this.hasLabel = hasLabel;
64-
this.aLabel = aLabel;
65-
this.bLabel = bLabel;
66-
this.cLabel = cLabel;
55+
this.labelFilterChecker = labelFilterChecker;
6756
}
6857

6958
@Override
@@ -90,21 +79,7 @@ private void triangles(
9079
) {
9180
long b = AdjacencyCursorUtils.next(neighborsOfa);
9281
while (b != NOT_FOUND && (b < a)) {
93-
// No filters
94-
if ((aLabel.isEmpty() && bLabel.isEmpty() && cLabel.isEmpty())
95-
// One filter
96-
|| (bLabel.isEmpty() && cLabel.isEmpty() && (hasLabel.apply(a, aLabel.get()) || hasLabel.apply(b, aLabel.get())))
97-
// Two filters
98-
|| (cLabel.isEmpty() && aLabel.isPresent() && bLabel.isPresent()
99-
&& (hasLabel.apply(a, aLabel.get()) || hasLabel.apply(a, bLabel.get()) || hasLabel.apply(b, aLabel.get()) && hasLabel.apply(b, bLabel.get())))
100-
// Three filters
101-
|| (aLabel.isPresent() && bLabel.isPresent() && cLabel.isPresent()
102-
&& ((hasLabel.apply(a, aLabel.get()) && hasLabel.apply(b, bLabel.get()))
103-
|| (hasLabel.apply(a, aLabel.get()) && hasLabel.apply(b, cLabel.get()))
104-
|| (hasLabel.apply(a, bLabel.get()) && hasLabel.apply(b, aLabel.get()))
105-
|| (hasLabel.apply(a, bLabel.get()) && hasLabel.apply(b, cLabel.get()))
106-
|| (hasLabel.apply(a, cLabel.get()) && hasLabel.apply(b, aLabel.get()))
107-
|| (hasLabel.apply(a, cLabel.get()) && hasLabel.apply(b, bLabel.get()))))) {
82+
if (labelFilterChecker.check(a, b)) {
10883
var degreeOfb = degree(b);
10984
if (degreeFilter.test(degreeOfb)) {
11085
helpingCursorOfb = cursorForNode(
@@ -140,24 +115,7 @@ private void triangles(
140115
long c = AdjacencyCursorUtils.next(neighborsOfb);
141116
long currentOfa = AdjacencyCursorUtils.next(neighborsOfa);
142117
while (c != NOT_FOUND && currentOfa != NOT_FOUND && (c < b)) {
143-
// No filters
144-
if ((aLabel.isEmpty() && bLabel.isEmpty() && cLabel.isEmpty())
145-
// One filter
146-
|| (bLabel.isEmpty() && cLabel.isEmpty() && (hasLabel.apply(a, aLabel.get()) || hasLabel.apply(b, aLabel.get()) || hasLabel.apply(c, aLabel.get())))
147-
// Two filters
148-
|| (cLabel.isEmpty() && aLabel.isPresent() && bLabel.isPresent()
149-
&& ((hasLabel.apply(a, bLabel.get()) && (hasLabel.apply(b, aLabel.get()) || hasLabel.apply(c, aLabel.get())))
150-
|| (hasLabel.apply(b, bLabel.get()) && (hasLabel.apply(a, aLabel.get()) || hasLabel.apply(c, aLabel.get())))
151-
|| (hasLabel.apply(c, bLabel.get()) && (hasLabel.apply(a, aLabel.get()) || hasLabel.apply(b, aLabel.get())))))
152-
// Three filters
153-
|| (aLabel.isPresent() && bLabel.isPresent() && cLabel.isPresent())
154-
&& ((hasLabel.apply(a, aLabel.get()) && hasLabel.apply(b, bLabel.get()) && hasLabel.apply(c, cLabel.get()))
155-
|| (hasLabel.apply(a, aLabel.get()) && hasLabel.apply(c, aLabel.get()) && hasLabel.apply(b, cLabel.get()))
156-
|| (hasLabel.apply(b, aLabel.get()) && hasLabel.apply(a, bLabel.get()) && hasLabel.apply(c, cLabel.get()))
157-
|| (hasLabel.apply(b, aLabel.get()) && hasLabel.apply(c, bLabel.get()) && hasLabel.apply(a, cLabel.get()))
158-
|| (hasLabel.apply(c, aLabel.get()) && hasLabel.apply(a, bLabel.get()) && hasLabel.apply(b, cLabel.get()))
159-
|| (hasLabel.apply(c, aLabel.get()) && hasLabel.apply(b, bLabel.get()) && hasLabel.apply(a, cLabel.get())))) {
160-
118+
if (labelFilterChecker.check(a, b, c)) {
161119
var degreeOfc = degree(c);
162120
if (degreeFilter.test(degreeOfc)) {
163121
currentOfa = AdjacencyCursorUtils.advance(neighborsOfa, currentOfa, c);

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

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,12 @@
2121

2222
import org.jetbrains.annotations.Nullable;
2323
import org.neo4j.annotations.service.ServiceProvider;
24-
import org.neo4j.gds.NodeLabel;
2524
import org.neo4j.gds.api.AdjacencyCursor;
2625
import org.neo4j.gds.api.AdjacencyList;
2726
import org.neo4j.gds.api.Graph;
2827
import org.neo4j.gds.api.RelationshipIntersect;
2928
import org.neo4j.gds.core.huge.HugeGraph;
30-
31-
import java.util.Optional;
32-
import java.util.function.BiFunction;
29+
import org.neo4j.gds.triangle.LabelFilterChecker;
3330

3431
public final class HugeGraphIntersect extends GraphIntersect<AdjacencyCursor> {
3532

@@ -38,12 +35,9 @@ public final class HugeGraphIntersect extends GraphIntersect<AdjacencyCursor> {
3835
private HugeGraphIntersect(
3936
AdjacencyList adjacency,
4037
long maxDegree,
41-
BiFunction<Long, NodeLabel, Boolean> hasLabel,
42-
Optional<NodeLabel> aLabel,
43-
Optional<NodeLabel> bLabel,
44-
Optional<NodeLabel> cLabel
38+
LabelFilterChecker labelFilterChecker
4539
) {
46-
super(maxDegree, hasLabel, aLabel, bLabel, cLabel);
40+
super(maxDegree, labelFilterChecker);
4741
this.adjacencyList = adjacency;
4842
}
4943

@@ -69,14 +63,12 @@ public boolean canLoad(Graph graph) {
6963
public RelationshipIntersect load(
7064
Graph graph,
7165
long maxDegree,
72-
Optional<NodeLabel> aLabel,
73-
Optional<NodeLabel> bLabel,
74-
Optional<NodeLabel> cLabel
66+
LabelFilterChecker labelFilterChecker
7567
) {
7668
assert graph instanceof HugeGraph;
7769
var hugeGraph = (HugeGraph) graph;
7870
var topology = hugeGraph.relationshipTopology().adjacencyList();
79-
return new HugeGraphIntersect(topology, maxDegree, graph::hasLabel, aLabel, bLabel, cLabel);
71+
return new HugeGraphIntersect(topology, maxDegree, labelFilterChecker);
8072
}
8173
}
8274
}

0 commit comments

Comments
 (0)