Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions algo/src/function/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_library(lbug_algo_function
page_rank.cpp
k_core_decomposition.cpp
louvain.cpp
leiden.cpp
spanning_forest.cpp
)

Expand Down
844 changes: 844 additions & 0 deletions algo/src/function/leiden.cpp

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions algo/src/include/function/leiden.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "function/function.h"

namespace lbug {
namespace algo_extension {

struct LeidenFunction {
static constexpr const char* name = "LEIDEN";

static function::function_set getFunctionSet();
};

struct LeidenAliasFunction {
using alias = LeidenFunction;

static constexpr const char* name = "LE";
};

} // namespace algo_extension
} // namespace lbug
3 changes: 3 additions & 0 deletions algo/src/main/algo_extension.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "main/algo_extension.h"

#include "function/algo_function.h"
#include "function/leiden.h"
#include "main/client_context.h"

namespace lbug {
Expand All @@ -21,6 +22,8 @@ void AlgoExtension::load(main::ClientContext* context) {
ExtensionUtils::addTableFunc<KCoreDecompositionFunction>(db);
ExtensionUtils::addTableFuncAlias<KCoreDecompositionAliasFunction>(db);
ExtensionUtils::addTableFunc<LouvainFunction>(db);
ExtensionUtils::addTableFunc<LeidenFunction>(db);
ExtensionUtils::addTableFuncAlias<LeidenAliasFunction>(db);
ExtensionUtils::addTableFunc<SpanningForest>(db);
ExtensionUtils::addTableFuncAlias<SpanningForestAliasFunction>(db);
}
Expand Down
89 changes: 89 additions & 0 deletions algo/test/test_files/leiden.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-DATASET CSV empty

--

-CASE LeidenBasic
-LOAD_DYNAMIC_EXTENSION algo
-STATEMENT CREATE NODE TABLE Node(id INT64 PRIMARY KEY);
---- ok
-STATEMENT CREATE REL TABLE Edge(FROM Node to Node);
---- ok
-STATEMENT CREATE (u0:Node {id: 0}),
(u1:Node {id: 1}),
(u2:Node {id: 2}),
(u3:Node {id: 3}),
(u4:Node {id: 4}),
(u5:Node {id: 5}),
(u6:Node {id: 6}),
(u7:Node {id: 7}),
(u8:Node {id: 8}),
(u9:Node {id: 9}),
(u0)-[:Edge]->(u1),
(u0)-[:Edge]->(u2),
(u1)-[:Edge]->(u2),
(u2)-[:Edge]->(u3),
(u3)-[:Edge]->(u4),
(u5)-[:Edge]->(u6),
(u5)-[:Edge]->(u7),
(u6)-[:Edge]->(u7),
(u7)-[:Edge]->(u8),
(u8)-[:Edge]->(u9),
(u2)-[:Edge]->(u5),
(u4)-[:Edge]->(u9);
---- ok
-STATEMENT CALL PROJECT_GRAPH('Graph', ['Node'], ['Edge'])
---- ok

# RED test: Leiden should return communities (same graph as Louvain test)
# All 10 nodes must get a community assignment.
-STATEMENT CALL LEIDEN('Graph') RETURN count(*) AS c;
---- 1
10

-CASE LeidenAlias
-LOAD_DYNAMIC_EXTENSION algo
-STATEMENT CREATE NODE TABLE Node(id INT64 PRIMARY KEY);
---- ok
-STATEMENT CREATE REL TABLE Edge(FROM Node to Node);
---- ok
-STATEMENT CREATE (u0:Node {id: 0}),
(u1:Node {id: 1}),
(u2:Node {id: 2}),
(u0)-[:Edge]->(u1),
(u1)-[:Edge]->(u2),
(u0)-[:Edge]->(u2);
---- ok
-STATEMENT CALL PROJECT_GRAPH('Graph', ['Node'], ['Edge'])
---- ok

-STATEMENT CALL LE('Graph') RETURN count(*) AS c;
---- 1
3

-CASE LeidenDisconnected
-LOAD_DYNAMIC_EXTENSION algo
-STATEMENT CREATE NODE TABLE Node(id INT64 PRIMARY KEY);
---- ok
-STATEMENT CREATE REL TABLE Edge(FROM Node TO Node);
---- ok
# Component 1
-STATEMENT CREATE (:Node {id: 0}), (:Node {id: 1}), (:Node {id: 2});
---- ok
-STATEMENT MATCH (a:Node {id: 0}), (b:Node {id: 1}) CREATE (a)-[:Edge]->(b);
---- ok
-STATEMENT MATCH (a:Node {id: 1}), (b:Node {id: 2}) CREATE (a)-[:Edge]->(b);
---- ok
# Component 2 — isolated
-STATEMENT CREATE (:Node {id: 10}), (:Node {id: 11}), (:Node {id: 12});
---- ok
-STATEMENT MATCH (a:Node {id: 10}), (b:Node {id: 11}) CREATE (a)-[:Edge]->(b);
---- ok
-STATEMENT MATCH (a:Node {id: 11}), (b:Node {id: 12}) CREATE (a)-[:Edge]->(b);
---- ok
-STATEMENT CALL PROJECT_GRAPH('Graph', ['Node'], ['Edge'])
---- ok

# Two disconnected components → 2 different community IDs
-STATEMENT CALL LEIDEN('Graph') WITH community_id, count(*) as cnt RETURN count(distinct community_id) as num_communities;
---- 1
2
43 changes: 43 additions & 0 deletions algo/test/test_files/leiden_heterogeneous.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-DATASET CSV empty

--

# Leiden on a heterogeneous projected graph (two node tables + one edge table).
# Regression test for the array out-of-bounds assertion that occurred when
# PhaseState arrays were sized for only the first node table.

-CASE LeidenHeterogeneous
-LOAD_DYNAMIC_EXTENSION algo
-STATEMENT CREATE NODE TABLE T(id INT64 PRIMARY KEY, name STRING);
---- ok
-STATEMENT CREATE NODE TABLE M(id INT64 PRIMARY KEY, name STRING);
---- ok
-STATEMENT CREATE REL TABLE E(FROM T TO M);
---- ok
-STATEMENT CREATE (:T {id: 0, name: 't0'}),
(:T {id: 1, name: 't1'}),
(:T {id: 2, name: 't2'});
---- ok
-STATEMENT CREATE (:M {id: 0, name: 'm0'}),
(:M {id: 1, name: 'm1'}),
(:M {id: 2, name: 'm2'});
---- ok
-STATEMENT MATCH (t:T {id: 0}), (m:M {id: 0}), (m2:M {id: 1})
CREATE (t)-[:E]->(m), (t)-[:E]->(m2);
---- ok
-STATEMENT MATCH (t:T {id: 1}), (m:M {id: 1}), (m2:M {id: 2})
CREATE (t)-[:E]->(m), (t)-[:E]->(m2);
---- ok
-STATEMENT MATCH (t:T {id: 2}), (m:M {id: 0}), (m2:M {id: 2})
CREATE (t)-[:E]->(m), (t)-[:E]->(m2);
---- ok
-STATEMENT CALL PROJECT_GRAPH('hg', ['T', 'M'], ['E']);
---- ok
# All 6 nodes (3 T + 3 M) must get a community assignment.
-STATEMENT CALL leiden('hg') RETURN count(*) AS c;
---- 1
6
# Communities must be valid community ids (0 <= id < numNodes).
-STATEMENT CALL leiden('hg') RETURN max(community_id) < 6 AS valid;
---- 1
True
Loading
Loading