Skip to content

Commit 5d8a546

Browse files
committed
Add minimal test-case for union graphs with incomplete triangles
1 parent 3caabcd commit 5d8a546

File tree

1 file changed

+76
-59
lines changed

1 file changed

+76
-59
lines changed

algo/src/test/java/org/neo4j/gds/triangle/IntersectingTriangleCountTest.java

Lines changed: 76 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,28 @@
2424
import org.junit.jupiter.params.provider.Arguments;
2525
import org.junit.jupiter.params.provider.MethodSource;
2626
import org.junit.jupiter.params.provider.ValueSource;
27-
import org.neo4j.gds.triangle.IntersectingTriangleCount.TriangleCountResult;
27+
import org.neo4j.gds.Orientation;
28+
import org.neo4j.gds.TestSupport;
2829
import org.neo4j.gds.api.Graph;
2930
import org.neo4j.gds.core.concurrency.Pools;
31+
import org.neo4j.gds.triangle.IntersectingTriangleCount.TriangleCountResult;
3032

3133
import java.util.stream.Stream;
3234

35+
import static org.assertj.core.api.Assertions.assertThat;
3336
import static org.junit.jupiter.api.Assertions.assertEquals;
37+
import static org.neo4j.gds.Orientation.UNDIRECTED;
3438
import static org.neo4j.gds.triangle.IntersectingTriangleCount.EXCLUDED_NODE_TRIANGLE_COUNT;
3539
import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
36-
import static org.neo4j.gds.Orientation.UNDIRECTED;
37-
import static org.neo4j.gds.TestSupport.fromGdl;
3840

3941
class IntersectingTriangleCountTest {
4042

4143
private static Stream<Arguments> noTriangleQueries() {
4244
return Stream.of(
43-
Arguments.of(fromGdl("CREATE ()-[:T]->()-[:T]->()", UNDIRECTED), "line"),
44-
Arguments.of(fromGdl("CREATE (), (), ()", UNDIRECTED), "no rels"),
45-
Arguments.of(fromGdl("CREATE ()-[:T]->(), ()", UNDIRECTED), "one rel"),
46-
Arguments.of(fromGdl("CREATE (a1)-[:T]->()-[:T]->(a1), ()", UNDIRECTED), "back and forth")
45+
Arguments.of(fromGdl("CREATE ()-[:T]->()-[:T]->()"), "line"),
46+
Arguments.of(fromGdl("CREATE (), (), ()"), "no rels"),
47+
Arguments.of(fromGdl("CREATE ()-[:T]->(), ()"), "one rel"),
48+
Arguments.of(fromGdl("CREATE (a1)-[:T]->()-[:T]->(a1), ()"), "back and forth")
4749
);
4850
}
4951

@@ -67,10 +69,10 @@ void independentTriangles(int nbrOfTriangles) {
6769
gdl.append(formatWithLocale("(a%d)-[:T]->()-[:T]->()-[:T]->(a%d) ", i, i));
6870
}
6971

70-
TriangleCountResult result = compute(fromGdl(gdl.toString(), UNDIRECTED));
72+
TriangleCountResult result = compute(fromGdl(gdl.toString()));
7173

7274
assertEquals(nbrOfTriangles, result.globalTriangles());
73-
assertEquals(3 * nbrOfTriangles, result.localTriangles().size());
75+
assertEquals(3L * nbrOfTriangles, result.localTriangles().size());
7476
for (int i = 0; i < result.localTriangles().size(); ++i) {
7577
assertEquals(1, result.localTriangles().get(i));
7678
}
@@ -89,8 +91,7 @@ void clique5() {
8991
" (a2)-[:T]->(a5), " +
9092
" (a3)-[:T]->(a4), " +
9193
" (a3)-[:T]->(a5), " +
92-
" (a4)-[:T]->(a5)",
93-
UNDIRECTED
94+
" (a4)-[:T]->(a5)"
9495
);
9596

9697
TriangleCountResult result = compute(graph);
@@ -115,8 +116,7 @@ void clique5UnionGraph() {
115116
" (a2)-[:T2]->(a5), " +
116117
" (a3)-[:T3]->(a4), " +
117118
" (a3)-[:T1]->(a5), " +
118-
" (a4)-[:T4]->(a5)",
119-
UNDIRECTED
119+
" (a4)-[:T4]->(a5)"
120120
);
121121

122122
TriangleCountResult result = compute(graph);
@@ -133,8 +133,7 @@ void twoAdjacentTriangles() {
133133
var graph = fromGdl(
134134
"CREATE " +
135135
" (a)-[:T]->()-[:T]->()-[:T]->(a) " +
136-
", (a)-[:T]->()-[:T]->()-[:T]->(a)",
137-
UNDIRECTED
136+
", (a)-[:T]->()-[:T]->()-[:T]->(a)"
138137
);
139138

140139
TriangleCountResult result = compute(graph);
@@ -149,8 +148,7 @@ void twoTrianglesWithLine() {
149148
"CREATE " +
150149
" (a)-[:T]->(b)-[:T]->(c)-[:T]->(a) " +
151150
", (q)-[:T]->(r)-[:T]->(t)-[:T]->(q) " +
152-
", (a)-[:T]->(q)",
153-
UNDIRECTED
151+
", (a)-[:T]->(q)"
154152
);
155153

156154
TriangleCountResult result = compute(graph);
@@ -165,7 +163,7 @@ void twoTrianglesWithLine() {
165163

166164
@Test
167165
void selfLoop() {
168-
var graph = fromGdl("CREATE (a)-[:T]->(a)-[:T]->(a)-[:T]->(a)", UNDIRECTED);
166+
var graph = fromGdl("CREATE (a)-[:T]->(a)-[:T]->(a)-[:T]->(a)");
169167

170168
TriangleCountResult result = compute(graph);
171169

@@ -176,7 +174,7 @@ void selfLoop() {
176174

177175
@Test
178176
void selfLoop2() {
179-
var graph = fromGdl("CREATE (a)-[:T]->(b)-[:T]->(c)-[:T]->(a)-[:T]->(a)", UNDIRECTED);
177+
var graph = fromGdl("CREATE (a)-[:T]->(b)-[:T]->(c)-[:T]->(a)-[:T]->(a)");
180178

181179
TriangleCountResult result = compute(graph);
182180

@@ -192,8 +190,7 @@ void parallelRelationships() {
192190
var graph = fromGdl(
193191
"CREATE" +
194192
" (a)-[:T]->(b)-[:T]->(c)-[:T]->(a)" +
195-
", (a)-[:T]->(b)",
196-
UNDIRECTED
193+
", (a)-[:T]->(b)"
197194
);
198195

199196
TriangleCountResult result = compute(graph);
@@ -210,8 +207,7 @@ void parallelTriangles() {
210207
var graph = fromGdl(
211208
"CREATE" +
212209
" (a)-[:T]->(b)-[:T]->(c)-[:T]->(a)" +
213-
",(a)-[:T]->(b)-[:T]->(c)-[:T]->(a)",
214-
UNDIRECTED
210+
",(a)-[:T]->(b)-[:T]->(c)-[:T]->(a)"
215211
);
216212

217213
TriangleCountResult result = compute(graph);
@@ -230,8 +226,7 @@ void triangleNotOnFirstPathAndFirstNodeHasNoMoreNeighbours() {
230226
" (n0)-[:REL]->(n1)" +
231227
", (n1)-[:REL]->(n2)" +
232228
", (n0)-[:REL]->(n3)" +
233-
", (n1)-[:REL]->(n3)",
234-
UNDIRECTED
229+
", (n1)-[:REL]->(n3)"
235230
);
236231

237232
TriangleCountResult result = compute(graph);
@@ -252,8 +247,7 @@ void triangleNotOnFirstPathAndFirstNodeHasAnotherNeighbours() {
252247
", (n1)-[:REL]->(n2)" +
253248
", (n0)-[:REL]->(n3)" +
254249
", (n0)-[:REL]->(n4)" +
255-
", (n1)-[:REL]->(n3)",
256-
UNDIRECTED
250+
", (n1)-[:REL]->(n3)"
257251
);
258252

259253
TriangleCountResult result = compute(graph);
@@ -276,8 +270,7 @@ void triangleNotOnFirstPathAndFirstNodeHasTheMostNeighbours() {
276270
", (n0)-[:REL]->(n3)" +
277271
", (n0)-[:REL]->(n4)" +
278272
", (n0)-[:REL]->(n5)" +
279-
", (n1)-[:REL]->(n3)",
280-
UNDIRECTED
273+
", (n1)-[:REL]->(n3)"
281274
);
282275

283276
TriangleCountResult result = compute(graph);
@@ -300,8 +293,7 @@ void triangleWhenSecondMemberAtEndOfRelChain() {
300293
", (n1)-[:REL]->(n2)" +
301294
", (n0)-[:REL]->(n3)" +
302295
", (n0)-[:REL]->(n4)" +
303-
", (n1)-[:REL]->(n4)",
304-
UNDIRECTED
296+
", (n1)-[:REL]->(n4)"
305297
);
306298

307299
TriangleCountResult result = compute(graph);
@@ -325,8 +317,7 @@ void triangleWhenFirstMemberHasMoreNeighbours() {
325317
", (n0)-[:REL]->(n4)" +
326318
", (n0)-[:REL]->(n5)" +
327319
", (n1)-[:REL]->(n4)" +
328-
", (n1)-[:REL]->(n6)",
329-
UNDIRECTED
320+
", (n1)-[:REL]->(n6)"
330321
);
331322

332323
TriangleCountResult result = compute(graph);
@@ -354,8 +345,7 @@ void filterMaxDegreeFirstCNode() {
354345
", (n3)-[:REL]->(n4)" +
355346
", (n1)-[:REL]->(n6)" +
356347
", (n0)-[:REL]->(n2)" +
357-
", (n0)-[:REL]->(n6)",
358-
UNDIRECTED
348+
", (n0)-[:REL]->(n6)"
359349
);
360350

361351
TriangleCountBaseConfig config = ImmutableTriangleCountBaseConfig
@@ -387,8 +377,7 @@ void filterMaxDegreeSecondCNode() {
387377
", (n3)-[:REL]->(n0)" +
388378
", (n3)-[:REL]->(n4)" +
389379
", (n3)-[:REL]->(n5)" +
390-
", (n3)-[:REL]->(n6)",
391-
UNDIRECTED
380+
", (n3)-[:REL]->(n6)"
392381
);
393382

394383
TriangleCountBaseConfig config = ImmutableTriangleCountBaseConfig
@@ -420,8 +409,7 @@ void manyTrianglesAndOtherThings() {
420409
", (h)-[:T]->(i)-[:T]->(j)-[:T]->(k)-[:T]->(e)" +
421410
", (k)-[:T]->(l)" +
422411
", (k)-[:T]->(m)-[:T]->(n)-[:T]->(j)" +
423-
", (o)",
424-
UNDIRECTED
412+
", (o)"
425413
);
426414

427415
TriangleCountResult result = compute(graph);
@@ -449,16 +437,15 @@ void manyTrianglesAndOtherThings() {
449437
void testTriangleCountingWithMaxDegree() {
450438
var graph = fromGdl(
451439
"CREATE" +
452-
" (a)-[:T]->(b)"+
453-
" ,(a)-[:T]->(c)"+
454-
" ,(a)-[:T]->(d)"+
455-
" ,(b)-[:T]->(c)"+
456-
" ,(b)-[:T]->(d)"+
457-
458-
" ,(e)-[:T]->(f)"+
459-
" ,(f)-[:T]->(g)"+
460-
" ,(g)-[:T]->(e)",
461-
UNDIRECTED
440+
" (a)-[:T]->(b)" +
441+
" ,(a)-[:T]->(c)" +
442+
" ,(a)-[:T]->(d)" +
443+
" ,(b)-[:T]->(c)" +
444+
" ,(b)-[:T]->(d)" +
445+
446+
" ,(e)-[:T]->(f)" +
447+
" ,(f)-[:T]->(g)" +
448+
" ,(g)-[:T]->(e)"
462449
);
463450

464451
TriangleCountBaseConfig config = ImmutableTriangleCountBaseConfig
@@ -483,16 +470,15 @@ void testTriangleCountingWithMaxDegree() {
483470
void testTriangleCountingWithMaxDegreeOnUnionGraph() {
484471
var graph = fromGdl(
485472
"CREATE" +
486-
" (a)-[:T1]->(b)"+
487-
" ,(a)-[:T2]->(c)"+
488-
" ,(a)-[:T2]->(d)"+
489-
" ,(b)-[:T1]->(c)"+
490-
" ,(b)-[:T2]->(d)"+
491-
492-
" ,(e)-[:T1]->(f)"+
493-
" ,(f)-[:T1]->(g)"+
494-
" ,(g)-[:T1]->(e)",
495-
UNDIRECTED
473+
" (a)-[:T1]->(b)" +
474+
" ,(a)-[:T2]->(c)" +
475+
" ,(a)-[:T2]->(d)" +
476+
" ,(b)-[:T1]->(c)" +
477+
" ,(b)-[:T2]->(d)" +
478+
479+
" ,(e)-[:T1]->(f)" +
480+
" ,(f)-[:T1]->(g)" +
481+
" ,(g)-[:T1]->(e)"
496482
);
497483

498484
TriangleCountBaseConfig config = ImmutableTriangleCountBaseConfig
@@ -513,6 +499,33 @@ void testTriangleCountingWithMaxDegreeOnUnionGraph() {
513499
assertEquals(1, result.globalTriangles());
514500
}
515501

502+
@Test
503+
void testTriangleCountingOnUnionGraphWithIncompleteTriangles() {
504+
// triangle would be (a)-(b)-(c), but it is not complete, there is no (b)-(c)
505+
// (b) is connected to (x) and (y), both have smaller ids than (c)
506+
// TC tries to find (b)-(c) und the union graph has to exhaust all cursors during advance
507+
// to learn that there are only nodes that are smaller than (c)
508+
var testGraph = TestSupport.fromGdl(
509+
"CREATE" +
510+
" (a)-[:T]->(b)" +
511+
" ,(b)-[:X]->(x)" +
512+
" ,(b)-[:Y]->(y)" +
513+
" ,(a)-[:T]->(c)",
514+
UNDIRECTED
515+
);
516+
517+
var config = ImmutableTriangleCountBaseConfig.builder().build();
518+
var result = compute(testGraph.graph(), config);
519+
520+
assertThat(result.globalTriangles()).isEqualTo(0L);
521+
assertThat(result.localTriangles())
522+
.returns(0L, t -> t.get(testGraph.toMappedNodeId("a")))
523+
.returns(0L, t -> t.get(testGraph.toMappedNodeId("b")))
524+
.returns(0L, t -> t.get(testGraph.toMappedNodeId("c")))
525+
.returns(0L, t -> t.get(testGraph.toMappedNodeId("x")))
526+
.returns(0L, t -> t.get(testGraph.toMappedNodeId("y")));
527+
}
528+
516529
private TriangleCountResult compute(Graph graph) {
517530
TriangleCountStatsConfig config = ImmutableTriangleCountStatsConfig.builder().build();
518531
return compute(graph, config);
@@ -521,4 +534,8 @@ private TriangleCountResult compute(Graph graph) {
521534
private TriangleCountResult compute(Graph graph, TriangleCountBaseConfig config) {
522535
return IntersectingTriangleCount.create(graph, config, Pools.DEFAULT).compute();
523536
}
537+
538+
private static Graph fromGdl(String gdl) {
539+
return TestSupport.fromGdl(gdl, Orientation.UNDIRECTED).graph();
540+
}
524541
}

0 commit comments

Comments
 (0)