Skip to content
This repository was archived by the owner on Apr 22, 2020. It is now read-only.

Commit 18ca399

Browse files
authored
Feature unify community algo results (#801)
* WIP * impl unified community algo result, chg louvain result * adapt UnionFind result to CommunityResult * WIP * WIP * WIP * WIP * WIP * WIP * wip * adapt SCC * WIP * add syntactic sugar methods in builder, adapt triangle count * map resulting nodeIds back to graphId * fix mapping * update percentiles * fix triangle count percentiles * don't think we need top3 * add modularity back * min and max size for SCC * remove top3 and bring back min/max set size * We need to keep around setCount in case someone relies on it * remove top3 from test * we need setCount here too * unused exports * typo
1 parent 6bd75b0 commit 18ca399

34 files changed

+984
-596
lines changed

algo/src/main/java/org/neo4j/graphalgo/LabelPropagationProc.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ public Stream<LabelPropagationStats> labelPropagation(
105105

106106
if(graph.nodeCount() == 0) {
107107
graph.release();
108-
return Stream.of(stats.build());
108+
return Stream.of(LabelPropagationStats.EMPTY);
109109
}
110110

111-
int[] labels = compute(direction, iterations, batchSize, concurrency, graph, stats);
111+
final int[] labels = compute(direction, iterations, batchSize, concurrency, graph, stats);
112112
if (configuration.isWriteFlag(DEFAULT_WRITE) && partitionProperty != null) {
113113
write(concurrency, partitionProperty, graph, labels, stats);
114114
}
115115

116-
return Stream.of(stats.build());
116+
return Stream.of(stats.build(graph.nodeCount(), l -> (long) labels[(int) l]));
117117
}
118118

119119
@Procedure(value = "algo.labelPropagation.stream")
@@ -205,7 +205,6 @@ private int[] compute(
205205

206206
stats.iterations(labelPropagation.ranIterations());
207207
stats.didConverge(labelPropagation.didConverge());
208-
stats.nodes(result.length);
209208

210209
labelPropagation.release();
211210
graph.release();

algo/src/main/java/org/neo4j/graphalgo/LouvainProc.java

Lines changed: 130 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@
1818
*/
1919
package org.neo4j.graphalgo;
2020

21+
import com.carrotsearch.hppc.LongLongMap;
22+
import org.HdrHistogram.Histogram;
2123
import org.neo4j.graphalgo.api.*;
2224
import org.neo4j.graphalgo.core.GraphLoader;
2325
import org.neo4j.graphalgo.core.ProcedureConfiguration;
24-
import org.neo4j.graphalgo.core.ProcedureConstants;
25-
import org.neo4j.graphalgo.core.heavyweight.HeavyGraph;
26-
import org.neo4j.graphalgo.core.heavyweight.HeavyGraphFactory;
2726
import org.neo4j.graphalgo.core.utils.Pools;
2827
import org.neo4j.graphalgo.core.utils.ProgressLogger;
2928
import org.neo4j.graphalgo.core.utils.ProgressTimer;
3029
import org.neo4j.graphalgo.core.utils.TerminationFlag;
3130
import org.neo4j.graphalgo.core.utils.paged.AllocationTracker;
3231
import org.neo4j.graphalgo.impl.louvain.*;
33-
import org.neo4j.graphalgo.results.LouvainResult;
32+
import org.neo4j.graphalgo.results.AbstractCommunityResultBuilder;
3433
import org.neo4j.kernel.api.KernelTransaction;
35-
import org.neo4j.kernel.impl.store.PropertyType;
3634
import org.neo4j.kernel.internal.GraphDatabaseAPI;
3735
import org.neo4j.logging.Log;
3836
import org.neo4j.procedure.*;
3937

38+
import java.util.ArrayList;
39+
import java.util.List;
4040
import java.util.Map;
4141
import java.util.stream.Stream;
4242

@@ -75,44 +75,45 @@ public Stream<LouvainResult> louvain(
7575
.overrideNodeLabelOrQuery(label)
7676
.overrideRelationshipTypeOrQuery(relationship);
7777

78-
LouvainResult.Builder builder = LouvainResult.builder();
78+
79+
final Builder builder = new Builder();
7980

8081
final Graph graph;
8182
try (ProgressTimer timer = builder.timeLoad()) {
8283
graph = graph(label, relationship, configuration);
8384
}
8485

85-
builder.withNodeCount(graph.nodeCount());
86-
8786
if(graph.nodeCount() == 0) {
8887
graph.release();
89-
return Stream.of(builder.build());
88+
return Stream.of(LouvainResult.EMPTY);
9089
}
9190

9291
final Louvain louvain = new Louvain(graph, Pools.DEFAULT, configuration.getConcurrency(), AllocationTracker.create())
9392
.withProgressLogger(ProgressLogger.wrap(log, "Louvain"))
9493
.withTerminationFlag(TerminationFlag.wrap(transaction));
9594

9695
// evaluation
96+
int iterations = configuration.getIterations(10);
9797
try (ProgressTimer timer = builder.timeEval()) {
9898
if (configuration.getString(DEFAULT_CLUSTER_PROPERTY).isPresent()) {
9999
// use predefined clustering
100100
final WeightMapping communityMap = ((NodeProperties) graph).nodeProperties(CLUSTERING_IDENTIFIER);
101-
louvain.compute(communityMap, configuration.getIterations(10), configuration.get("innerIterations", 10));
101+
louvain.compute(communityMap, iterations, configuration.get("innerIterations", 10));
102102
} else {
103-
louvain.compute(configuration.getIterations(10), configuration.get("innerIterations", 10));
103+
louvain.compute(iterations, configuration.get("innerIterations", 10));
104104
}
105-
builder.withIterations(louvain.getLevel())
106-
.withCommunityCount(louvain.getCommunityCount())
107-
.withModularities(louvain.getModularities())
108-
.withFinalModularity(louvain.getFinalModularity());
109105
}
110106

111107
if (configuration.isWriteFlag()) {
112108
builder.timeWrite(() -> write(graph, louvain.getDendrogram(), louvain.getCommunityIds(), configuration));
113109
}
114110

115-
return Stream.of(builder.build());
111+
builder.withIterations(louvain.getLevel());
112+
builder.withModularities(louvain.getModularities() );
113+
builder.withFinalModularity(louvain.getFinalModularity());
114+
115+
final int[] communityIds = louvain.getCommunityIds();
116+
return Stream.of(builder.build(graph.nodeCount(), n -> (long) communityIds[(int) n]));
116117
}
117118

118119
@Procedure(value = "algo.louvain.stream")
@@ -183,4 +184,117 @@ private void write(Graph graph, int[][] allCommunities, int[] finalCommunities,
183184
configuration.get(INTERMEDIATE_COMMUNITIES_WRITE_PROPERTY, "communities"))
184185
.export(allCommunities, finalCommunities, includeIntermediateCommunities);
185186
}
187+
188+
public static class LouvainResult {
189+
190+
public static final LouvainResult EMPTY = new LouvainResult(
191+
0,
192+
0,
193+
0,
194+
0,
195+
0,
196+
0,
197+
-1,
198+
-1,
199+
-1,
200+
-1,
201+
-1,
202+
-1,
203+
-1,
204+
-1,
205+
-1,
206+
-1,
207+
0,
208+
new double[] {}, -1);
209+
210+
public final long loadMillis;
211+
public final long computeMillis;
212+
public final long postProcessingMillis;
213+
public final long writeMillis;
214+
public final long nodes;
215+
public final long communityCount;
216+
public final long p100;
217+
public final long p99;
218+
public final long p95;
219+
public final long p90;
220+
public final long p75;
221+
public final long p50;
222+
public final long p25;
223+
public final long p10;
224+
public final long p05;
225+
public final long p01;
226+
public final long iterations;
227+
public final List<Double> modularities;
228+
public final double modularity;
229+
230+
public LouvainResult(long loadMillis, long computeMillis, long postProcessingMillis, long writeMillis, long nodes, long communityCount, long p100, long p99, long p95, long p90, long p75, long p50, long p25, long p10, long p05, long p01, long iterations, double[] modularities, double finalModularity) {
231+
this.loadMillis = loadMillis;
232+
this.computeMillis = computeMillis;
233+
this.postProcessingMillis = postProcessingMillis;
234+
this.writeMillis = writeMillis;
235+
this.nodes = nodes;
236+
this.communityCount = communityCount;
237+
this.p100 = p100;
238+
this.p99 = p99;
239+
this.p95 = p95;
240+
this.p90 = p90;
241+
this.p75 = p75;
242+
this.p50 = p50;
243+
this.p25 = p25;
244+
this.p10 = p10;
245+
this.p05 = p05;
246+
this.p01 = p01;
247+
this.iterations = iterations;
248+
this.modularities = new ArrayList<>(modularities.length);
249+
for (double mod : modularities) this.modularities.add(mod);
250+
this.modularity = finalModularity;
251+
}
252+
}
253+
254+
public static class Builder extends AbstractCommunityResultBuilder<LouvainResult> {
255+
256+
private long iterations = -1;
257+
private double[] modularities = new double[] {};
258+
private double finalModularity = -1;
259+
260+
public Builder withIterations(long iterations) {
261+
this.iterations = iterations;
262+
return this;
263+
}
264+
265+
@Override
266+
protected LouvainResult build(long loadMillis, long computeMillis, long writeMillis, long postProcessingMillis, long nodeCount, long communityCount, LongLongMap communitySizeMap, Histogram communityHistogram) {
267+
return new LouvainResult(
268+
loadMillis,
269+
computeMillis,
270+
postProcessingMillis,
271+
writeMillis,
272+
nodeCount,
273+
communityCount,
274+
communityHistogram.getValueAtPercentile(100),
275+
communityHistogram.getValueAtPercentile(99),
276+
communityHistogram.getValueAtPercentile(95),
277+
communityHistogram.getValueAtPercentile(90),
278+
communityHistogram.getValueAtPercentile(75),
279+
communityHistogram.getValueAtPercentile(50),
280+
communityHistogram.getValueAtPercentile(25),
281+
communityHistogram.getValueAtPercentile(10),
282+
communityHistogram.getValueAtPercentile(5),
283+
communityHistogram.getValueAtPercentile(1),
284+
iterations, modularities, finalModularity
285+
);
286+
}
287+
288+
public Builder withModularities(double[] modularities) {
289+
this.modularities = modularities;
290+
return this;
291+
}
292+
293+
public Builder withFinalModularity(double finalModularity) {
294+
this.finalModularity = finalModularity;
295+
return null;
296+
}
297+
}
298+
299+
186300
}

algo/src/main/java/org/neo4j/graphalgo/MSColoringProc.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.neo4j.graphalgo.core.write.Exporter;
2727
import org.neo4j.graphalgo.core.write.Translators;
2828
import org.neo4j.graphalgo.impl.MSColoring;
29-
import org.neo4j.graphalgo.results.UnionFindResult;
29+
import org.neo4j.graphalgo.impl.UnionFindProcExec;
3030
import org.neo4j.graphdb.Direction;
3131
import org.neo4j.kernel.internal.GraphDatabaseAPI;
3232
import org.neo4j.logging.Log;
@@ -54,7 +54,7 @@ public class MSColoringProc {
5454
@Description("CALL algo.unionFind.mscoloring(label:String, relationship:String, " +
5555
"{property:'weight', threshold:0.42, defaultValue:1.0, write: true, partitionProperty:'partition', concurrency:4}) " +
5656
"YIELD nodes, setCount, loadMillis, computeMillis, writeMillis")
57-
public Stream<UnionFindResult> unionFind(
57+
public Stream<UnionFindProcExec.UnionFindResult> unionFind(
5858
@Name(value = "label", defaultValue = "") String label,
5959
@Name(value = "relationship", defaultValue = "") String relationship,
6060
@Name(value = "config", defaultValue = "{}") Map<String, Object> config) {
@@ -63,7 +63,7 @@ public Stream<UnionFindResult> unionFind(
6363
.overrideNodeLabelOrQuery(label)
6464
.overrideRelationshipTypeOrQuery(relationship);
6565

66-
UnionFindResult.Builder builder = UnionFindResult.builder();
66+
final UnionFindProcExec.Builder builder = new UnionFindProcExec.Builder();
6767

6868
// loading
6969
final Graph graph;
@@ -73,7 +73,7 @@ public Stream<UnionFindResult> unionFind(
7373

7474
if (graph.nodeCount() == 0) {
7575
graph.release();
76-
return Stream.of(builder.build());
76+
return Stream.of(UnionFindProcExec.UnionFindResult.EMPTY);
7777
}
7878

7979
// evaluation
@@ -88,7 +88,7 @@ public Stream<UnionFindResult> unionFind(
8888
write(graph, struct, configuration));
8989
}
9090

91-
return Stream.of(builder.build());
91+
return Stream.of(builder.build(graph.nodeCount(), n -> (long) struct.get((int) n)));
9292
}
9393

9494
@Procedure(value = "algo.unionFind.mscoloring.stream")

0 commit comments

Comments
 (0)