UltipaDocs
Try Playground
  • Introduction
  • Managing HDC Graphs
  • Managing Distributed Projections
  • Installing Algorithms
  • Running Algorithms
    • Degree Centrality
    • Closeness Centrality
    • Harmonic Centrality
    • Graph Centrality
    • Betweenness Centrality
    • Eigenvector Centrality
    • Katz Centrality
    • CELF
    • PageRank
    • ArticleRank
    • TextRank
    • HITS
    • SybilRank
    • Jaccard Similarity
    • Overlap Similarity
    • Cosine Similarity
    • Pearson Correlation Coefficient
    • Euclidean Distance
    • K-Hop All
    • Bipartite Graph
    • HyperANF
    • Connected Component
    • Triangle Counting
    • Induced Subgraph
    • k-Core
    • k-Truss
    • p-Cohesion
    • k-Edge Connected Components
    • Local Clustering Coefficient
    • Topological Sort
    • Schema Overview
    • Dijkstra's Single-Source Shortest Path
    • Delta-Stepping Single-Source Shortest Path
    • Shortest Path Faster Algorithm (SPFA)
    • Minimum Spanning Tree
    • Breadth-First Search (BFS)
    • Depth-First Search (DFS)
    • Adamic-Adar Index
    • Common Neighbors
    • Preferential Attachment
    • Resource Allocation
    • Total Neighbors
    • Louvain
    • Leiden
    • Label Propagation
    • HANP
    • k-Means
    • kNN (k-Nearest Neighbors)
    • K-1 Coloring
    • Conductance
      • Random Walk
      • Node2Vec Walk
      • Node2Vec
      • Struc2Vec Walk
      • Struc2Vec
      • LINE
      • Fast Random Projection
      • Summary of Graph Embedding
      • Gradient Descent
      • Backpropagation
      • Skip-gram
      • Skip-gram Optimization
  1. Docs
  2. /
  3. Graph Analytics & Algorithms
  4. /
  5. Community Detection & Classification

Leiden

HDC

Overview

The Leiden algorithm is a community detection method designed to optimize modularity while addressing some of the limitations of the widely used Louvain algorithm. Unlike Louvain, which may produce poorly connected or even disconnected communities, Leiden guarantees well-connected communities. Additionally, the Leiden algorithm is faster. It is named after the city of Leiden, where it was developed.

References:

  • V.A. Traag, L. Waltman, N.J. van Eck, From Louvain to Leiden: guaranteeing well-connected communities (2019)
  • V.A. Traag, P. Van Dooren, Y. Nesterov, Narrow scope for resolution-limit-free community detection (2011)

Concepts

Modularity

The concept of modularity, as explained in the Louvain algorithm, is also used in the Leiden algorithm. However, Leiden introduces a new resolution parameter γ (gamma) into the modularity formula:

The parameter γ controls the granularity of the detected communities by modulating the balance between intra-community and inter-community connections:

  • When γ > 1, the algorithm favors more and smaller communities that are tightly connected internally.
  • When 0 < γ < 1, it favors fewer and larger communities that may be less densely connected internally.

Leiden

When the Leiden algorithm starts, each node is placed in its own community. The algorithm then iteratively proceeds through passes, each consisting of three phases:

First Phase: Fast Modularity Optimization

In the first phase of Louvain, the algorithm repeatedly visits all nodes in the graph until no further node movements can increase the modularity. The Leiden algorithm improves efficiency by only visiting all nodes once initially, and afterwards, only revisiting nodes whose neighborhoods have changed.

To achieve this, the Leiden algorithm maintains a queue, initializes it with all nodes in the graph in a random order, then repeats the following steps until the queue is empty:

  • Remove the first node v from the front of the queue.
  • Reassign node v to a different community C that provides the maximum gain of modularity (ΔQ), or keep v in its current community if there is no positive gain.
  • If v is moved to a new community C, add to the rear of the queue all neighbors of v that do not belong to C and that are not already in the queue.

Second Phase: Refinement

This phase produces a refined partition Prefined based on the partition P obtained from the first phase. Initially, Prefined is set as a singleton partition, where each node—either from the original graph or the aggregated graph—is placed in its own community. Then, each community C ∈ P is refined individually as follows:

1. Find each node v ∈ C that is well-connected within C by this formula:

where,

  • W(v,C-v) is the sum of edge weights between node v and the other nodes in C.
  • kv is the total edge weights between node v and the other nodes in the graph.
  • ∑totc is the sum of k of all nodes in C.
  • m is the sum of all edge weights in the graph.

Take community C1 in the graph above as an example, where

  • m = 18.1
  • ∑totC1 = ka + kb + kc + kd = 6 + 2.7 + 2.8 + 3 = 14.5

Set γ as 1.2, then:

  • W(a, C1) - γ/m ⋅ ka ⋅ (∑totC1 - ka) = 4.5 - 1.2/18.1*6*(14.5 - 6) = 1.12
  • W(b, C1) - γ/m ⋅ kb ⋅ (∑totC1 - kb) = 1 - 1.2/18.1*2.7*(14.5 - 2.7) = -1.11
  • W(c, C1) - γ/m ⋅ kc ⋅ (∑totC1 - kc) = 0.5 - 1.2/18.1*2.8*(14.5 - 2.8) = -1.67
  • W(d, C1) - γ/m ⋅ kd ⋅ (∑totC1 - kd) = 3 - 1.2/18.1*3*(14.5 - 3) = 0.71

Therefore, nodes a and d are considered well-connected in C1.

2. Visit each node v. If it remains in its own singleton community in Prefined, randomly merge it into a community C' ∈ Prefined that increases the modularity. The merge is allowed only if C' is well-connected with C, determined by the following condition:

Note that each node v is not necessarily merged greedily with the community that yields the maximum gain of modularity. Instead, the larger the modularity gain, the more likely that community is to be selected. The degree of randomness in selecting a community C' is determined by a parameter θ (theta) as:

Randomness in the selection of a community allows the partition space to be explored more broadly.

Third Phase: Community Aggregation

The aggregate graph is constructed based on the Prefined obtained from the previous phase. This aggregation process is the same as in Louvain. Note that each node is a single community in the aggregate graph in Louvain. However, the aggregate graph in Leiden is partitioned based on P, so multiple nodes may belong to the same community.

P is denoted by color blocks, Prefined is denoted by node colors

Once this third phase is completed, another pass is applied to the aggregate graph. These passes are iterated until no further changes occur in the node communities, and the modularity reaches its maximum.

Considerations

  • If node v has any self-loop, when calculating kv, the weight of self-loop is counted only once.
  • The Leiden algorithm treats all edges as undirected, ignoring their original direction.

Example Graph

Run the following statements on an empty graph to define its structure and insert data:

ALTER EDGE default ADD PROPERTY {
    weight float
};
INSERT (A:default {_id: "A"}),
       (B:default {_id: "B"}),
       (C:default {_id: "C"}),
       (D:default {_id: "D"}),
       (E:default {_id: "E"}),
       (F:default {_id: "F"}),
       (G:default {_id: "G"}),
       (H:default {_id: "H"}),
       (I:default {_id: "I"}),
       (J:default {_id: "J"}),
       (K:default {_id: "K"}),
       (L:default {_id: "L"}),
       (M:default {_id: "M"}),
       (N:default {_id: "N"}),
       (A)-[:default {weight: 1}]->(B),
       (A)-[:default {weight: 1.7}]->(C),
       (A)-[:default {weight: 0.6}]->(D),
       (A)-[:default {weight: 1}]->(E),
       (B)-[:default {weight: 3}]->(G),
       (F)-[:default {weight: 1.6}]->(A),
       (F)-[:default {weight: 0.3}]->(H),
       (F)-[:default {weight: 2}]->(J),
       (F)-[:default {weight: 0.5}]->(K),
       (G)-[:default {weight: 2}]->(F),
       (I)-[:default {weight: 1}]->(F),
       (K)-[:default {weight: 0.3}]->(A),
       (K)-[:default {weight: 0.8}]->(L),
       (K)-[:default {weight: 1.2}]->(M),
       (K)-[:default {weight: 2}]->(N);

Creating HDC Graph

To load the entire graph to the HDC server hdc-server-1 as my_hdc_graph:

CREATE HDC GRAPH my_hdc_graph ON "hdc-server-1" OPTIONS {
  nodes: {"*": ["*"]},
  edges: {"*": ["*"]},
  direction: "undirected",
  load_id: true,
  update: "static"
}

Parameters

Algorithm name: leiden

Name
Type
Spec
Default
Optional
Description
phase1_loop_numInteger≥15YesThe maximum number of loops in the first phase during each pass.
min_modularity_increaseFloat[0,1]0.01YesThe minimum gain of modularity (ΔQ) to move a node to another community.
edge_schema_property[]"<@schema.?><property>"//YesSpecifies numeric edge properties used as weights by summing their values. Only properties of numeric type are considered, and edges without these properties are ignored.
gammaFloat>01YesThe resolution parameter γ.
thetaFloat≥00.01YesThe parameter θ which controls the degree of randomness during community merging in the second phase; sets to 0 to disable randomness to acquire the maximum gain of modularity (ΔQ).
return_id_uuidStringuuid, id, bothuuidYesIncludes _uuid, _id, or both to represent nodes in the results.
limitInteger≥-1-1YesLimits the number of results returned. Set to -1 to include all results.
orderStringasc, desc/YesSorts the results by count; this option is only valid in Stream Return when mode is set to 2.

File Writeback

This algorithm can generate three files:

Spec
Content
filename_community_id
  • _id/_uuid: The node.
  • community_id: ID of the community the node belongs to.
filename_ids
  • community_id: ID of the community.
  • _ids/_uuids: Nodes belonging to the community.
filename_num
  • community_id: ID of the community.
  • count: Number of nodes in the community.
CALL algo.leiden.write("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 5, 
  min_modularity_increase: 0.1,
  edge_schema_property: 'weight'
}, {
  file: {
    filename_community_id: "f1",
    filename_ids: "f2",
    filename_num: "f3"
  }
})

Result:

community_id,_ids
5,I;J;F;H;
7,G;B;
9,D;A;E;C;
11,N;L;K;M;

DB Writeback

Writes the community_id values from the results to the specified node property. The property type is uint32.

CALL algo.leiden.write("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 5, 
  min_modularity_increase: 0.1,
  edge_schema_property: 'weight'
}, {
  db: {
    property: "communityID"
  }
})

Stats Writeback

CALL algo.leiden.write("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 5, 
  min_modularity_increase: 0.1,
  edge_schema_property: 'weight'
}, {
  stats: {}
})

Result:

community_countmodularity
40.548490

Full Return

CALL algo.leiden.run("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 5, 
  min_modularity_increase: 0.1
}) YIELD r
RETURN r

Result:

_idcommunity_id
I5
G7
J5
D9
N11
F5
H5
B7
L11
A9
E9
K11
M11
C9

Stream Return

This Stream Return supports two modes:

ItemSpecColumns
mode1 (Default)
  • _id/_uuid: The node.
  • community_id: ID of the community the node belongs to.
2
  • community_id: ID of the community.
  • count: Number of nodes in the community.
CALL algo.leiden.stream("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 6, 
  min_modularity_increase: 0.1
}) YIELD r
RETURN r

Result:

_idcommunity_id
I5
G7
J5
D9
N11
F5
H5
B7
L11
A9
E9
K11
M11
C9
CALL algo.leiden.stream("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 6, 
  min_modularity_increase: 0.1
}, {
  mode: 2
}) YIELD r
RETURN r

Result:

community_idcount
72
54
94
114

Stats Return

CALL algo.leiden.stats("my_hdc_graph", {
  return_id_uuid: "id",
  phase1_loop_num: 6, 
  min_modularity_increase: 0.1
}) YIELD s
RETURN s

Result:

community_countmodularity
40.397778