Chapter 24 · Graph Algorithms: Centrality, Community Detection, Similarity, Paths, and Topology Analytics

Louvain/Leiden/Label Propagation and Community Detection: Resolution, Stability, and Validation

Run Louvain, Leiden, and Label Propagation on a bounded AtlasMart product graph, test resolution/randomness/stability, and treat community IDs as analytical partition labels rather than business truth.

Advanced240–350 minutesCommunity detection · stabilityNeo4j 2026.07.1 · Community mandatoryGDS Community 2026.07.0 · Cypher 25Algorithms · estimates · stream first · concurrency ≤4 CEJava 21/25 · GDS plugin requiredLast reviewed: September 2026

Learning outcomes

01

Distinguish Louvain, Leiden, and Label Propagation by objective/mechanism rather than by name recognition.

02

Explain modularity, resolution, hierarchy, randomness, seed properties, convergence, and community identifiers.

03

Run bounded community algorithms with reproducible parameters and inspect distributions instead of only one partition.

04

Measure partition stability across algorithm/parameter choices without treating community IDs as semantic labels.

05

Document when a community result is exploratory structure versus evidence safe enough for downstream decisions.

1. AtlasMart problem: merchandising segments appear in the graph, but are they stable?

AtlasMart wants product “communities” for catalog navigation. A dense group of products may be useful, but community detection does not discover an objective business taxonomy. Louvain and Leiden optimize modularity-style structure; Label Propagation spreads neighbor labels until a stable labeling or iteration limit. Their outputs depend on graph scope, orientation, weights, parameters, and sometimes randomness.

The safe question is not “what are the true communities?” It is “does this projection contain stable, interpretable structural groups that improve a measured decision?”

Dimension Chapter 24 reproducible assumption
Neo4j 2026.07.1 Community, disposable local/container deployment; database neo4j.
Cypher Cypher 25 for course examples; Cypher 5 differences are not needed for the GDS procedures used here.
Java Java 21/25 supported by the 2026 line; container image supplies the runtime.
Auth/TLS User neo4j, password atlasmart-course-2026; loopback Bolt without TLS only for this isolated lab.
Driver No application driver is required for mandatory GDS procedure labs; cypher-shell/Browser is sufficient.
GDS GDS Community 2026.07.0. Community includes all algorithms; maximum GDS concurrency is four CPU cores.
APOC Not required.
Graph scope Only identifiers prefixed CH24- plus named in-memory projections beginning atlas-ch24- are created.
Runtime evidence This artifact does not execute Neo4j/GDS. Exact fixture counts and hand calculations are deterministic; timings, memory estimates, and algorithm scores must be measured locally.
Verify the Chapter 24 lab baseline
// Run in the disposable atlasmart-gds lab from Chapter 23.
RETURN gds.version() AS gdsVersion;
CALL dbms.components() YIELD name, versions, edition
RETURN name, versions, edition;

// Defensive cleanup of Chapter 24 projections if rerunning.
CALL gds.graph.list()
YIELD graphName
WITH graphName WHERE graphName STARTS WITH 'atlas-ch24-'
CALL gds.graph.drop(graphName) YIELD graphName AS dropped
RETURN dropped;

2. Community identifiers are opaque partition labels

Concept Precise meaning
community ID An algorithm-produced identifier grouping nodes in one run; do not attach permanent business semantics to its numeric value.
modularity A quality measure comparing within-community density against a random-network expectation. Higher is not automatically “better business segmentation.”
resolution Controls the granularity of detected groups. In Leiden, gamma is the explicit resolution parameter; higher values tend toward more communities.
randomness Leiden uses randomized refinement and exposes randomSeed; Louvain and LPA have different control surfaces, so do not invent one universal seed parameter.
seedProperty Optional existing node property used as initial communities/labels in supported algorithms; seeding changes the optimization start state.
stability Similarity of partitions under reruns, parameter changes, refreshed data, or algorithm family changes—not merely “did the procedure finish.”

3. Create two dense product groups with one weak bridge

Create CH24 community fixture
// Remove only this lesson's synthetic nodes if rerunning after Lesson 1 cleanup.
MATCH (n:Product) WHERE n.productId STARTS WITH 'CH24-COM-' DETACH DELETE n;

UNWIND range(1,8) AS i
CREATE (:Product:CH24CommunityProduct {
  productId:'CH24-COM-' + toString(i),
  name:'Community Product ' + toString(i),
  chapter24:true
});

MATCH (p1:Product {productId:'CH24-COM-1'}), (p2:Product {productId:'CH24-COM-2'}),
      (p3:Product {productId:'CH24-COM-3'}), (p4:Product {productId:'CH24-COM-4'}),
      (p5:Product {productId:'CH24-COM-5'}), (p6:Product {productId:'CH24-COM-6'}),
      (p7:Product {productId:'CH24-COM-7'}), (p8:Product {productId:'CH24-COM-8'})
CREATE (p1)-[:RELATED_TO {strength:5.0}]->(p2),
       (p2)-[:RELATED_TO {strength:5.0}]->(p3),
       (p3)-[:RELATED_TO {strength:5.0}]->(p4),
       (p4)-[:RELATED_TO {strength:5.0}]->(p1),
       (p1)-[:RELATED_TO {strength:4.0}]->(p3),
       (p5)-[:RELATED_TO {strength:5.0}]->(p6),
       (p6)-[:RELATED_TO {strength:5.0}]->(p7),
       (p7)-[:RELATED_TO {strength:5.0}]->(p8),
       (p8)-[:RELATED_TO {strength:5.0}]->(p5),
       (p5)-[:RELATED_TO {strength:4.0}]->(p7),
       (p4)-[:RELATED_TO {strength:0.5}]->(p5);

CALL gds.graph.project(
  'atlas-ch24-community',
  'CH24CommunityProduct',
  {RELATED_TO:{orientation:'UNDIRECTED', properties:['strength']}}
)
YIELD graphName, nodeCount, relationshipCount
RETURN graphName, nodeCount, relationshipCount;
Projection-scope check

If Lesson 1 Product nodes still exist, the label-only projection will include them too. Either run the Chapter cleanup before this lesson, or replace the simple native projection with a filtered Cypher projection. A bounded analytical question requires a bounded projection.

4. Run Louvain, Leiden, and Label Propagation as competing hypotheses

Community algorithms in stream/stats modes
CALL gds.louvain.stats('atlas-ch24-community', {
  relationshipWeightProperty:'strength',
  maxLevels:10,
  maxIterations:10,
  tolerance:0.0001,
  concurrency:2
}) YIELD communityCount, modularity, ranLevels, communityDistribution
RETURN *;

CALL gds.louvain.stream('atlas-ch24-community', {
  relationshipWeightProperty:'strength', concurrency:2
}) YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).productId AS productId, communityId
ORDER BY communityId, productId;

CALL gds.leiden.stream('atlas-ch24-community', {
  relationshipWeightProperty:'strength',
  gamma:1.0,
  theta:0.01,
  randomSeed:24,
  concurrency:2
}) YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).productId AS productId, communityId
ORDER BY communityId, productId;

CALL gds.labelPropagation.stream('atlas-ch24-community', {
  relationshipWeightProperty:'strength',
  maxIterations:10,
  concurrency:2
}) YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).productId AS productId, communityId
ORDER BY communityId, productId;

The fixture is designed with two dense four-node blocks and one weak bridge, so “two groups” is a reasonable structural hypothesis to check. Do not hard-code numeric community IDs in an acceptance test; IDs are labels, not stable domain keys.

5. Resolution and randomness need explicit experiments

Leiden sensitivity experiment
UNWIND [0.5, 1.0, 2.0] AS gamma
CALL gds.leiden.stats('atlas-ch24-community', {
  relationshipWeightProperty:'strength',
  gamma:gamma,
  theta:0.01,
  randomSeed:24,
  concurrency:2
})
YIELD communityCount, modularity
RETURN gamma, communityCount, modularity
ORDER BY gamma;

UNWIND [11,24,37] AS seed
CALL gds.leiden.stats('atlas-ch24-community', {
  relationshipWeightProperty:'strength',
  gamma:1.0,
  randomSeed:seed,
  concurrency:2
})
YIELD communityCount, modularity
RETURN seed, communityCount, modularity
ORDER BY seed;

Do not assume higher modularity or more communities is automatically a better product taxonomy. Record whether the partition is stable enough for the intended downstream use and compare it with human judgments or a known fixture.

6. Compare partitions without comparing raw IDs

A simple hand-checkable stability measure is pair agreement: for every pair of products, ask whether each run puts the pair in the same community. Compare that Boolean relation across runs. This avoids pretending that community ID 17 from one run must equal community ID 17 from another.

Pair-agreement idea in Python
from itertools import combinations

def pair_signature(assignments):
    # assignments: {product_id: opaque_community_id}
    return {
        tuple(sorted((a, b))): assignments[a] == assignments[b]
        for a, b in combinations(assignments, 2)
    }

def agreement(a, b):
    sa, sb = pair_signature(a), pair_signature(b)
    keys = sa.keys() & sb.keys()
    return sum(sa[k] == sb[k] for k in keys) / len(keys)

# Feed this with actual streamed assignments from two runs.
# Do not paste invented community IDs into the course evidence.

7. Deliberately wrong: rename community IDs as “customer segments”

A common failure is to persist communityId, display “Segment 42,” and let business workflows depend on it. A refreshed graph may split or merge groups; a parameter change may relabel IDs; a different algorithm may optimize another objective.

Repair by treating the partition as a versioned analytical artifact: store algorithm, projection fingerprint, parameter set, source-data cutoff, evaluation metrics, and a separate governed business-segment key only after validation. If the product needs stable segmentation, stability itself is a requirement to measure.

Production judgment and bridge

Question Required evidence
Does the graph have meaningful community structure? Compare against random/baseline partitions, known fixtures, modularity/distribution, and domain judgments.
Is the partition stable? Rerun/refit under data refreshes and parameter/seed changes; compare pair membership, not numeric IDs.
Do weights mean what the algorithm assumes? Document source and scale of non-negative relationship weights; run unweighted baseline.
Can the system afford refresh? Projection + estimate + measured compute time/memory/concurrency at expected cadence.
Can a community drive a decision? Only after external validation; communities are structural association, not causal or protected-class truth.

Next, AtlasMart shifts from grouping a whole graph to generating pairwise candidates: “which customers or products are similar?” That exposes the difference between structural Node Similarity and property-based KNN.

Check your understanding

  1. Why should community IDs not be compared directly across runs?
  2. Which of these three algorithms exposes an explicit randomSeed in current GDS?
  3. What does Leiden gamma control?
  4. Why run an unweighted baseline?
  5. What business claim follows automatically from two dense graph groups?
Review the answers

1. They are opaque partition labels; compare node co-membership or other partition metrics instead.

2. Leiden. Do not invent a shared randomness parameter model for Louvain and Label Propagation.

3. Resolution/granularity of modularity optimization; changing it can change how many communities appear.

4. To show whether the chosen relationship weights materially drive the partition and to catch bad weight semantics.

5. None. They are structural groups that require separate interpretation and validation.

Chapter 24 cleanup/reset
// Drop all Chapter 24 in-memory projections first.
CALL gds.graph.list()
YIELD graphName
WITH graphName WHERE graphName STARTS WITH 'atlas-ch24-'
CALL gds.graph.drop(graphName) YIELD graphName AS dropped
RETURN dropped;

// Then delete only synthetic Chapter 24 persisted fixtures.
MATCH (n)
WHERE n.chapter24 = true
DETACH DELETE n;

MATCH (n) WHERE n.chapter24 IS NOT NULL REMOVE n.chapter24;
RETURN 'Chapter 24 reset complete' AS status;

Summary and next step

Louvain/Leiden/Label Propagation and Community Detection: Resolution, Stability, and Validation is useful only when its assumptions and observed evidence stay attached to the decision. The examples above establish a reproducible mechanism and boundary; they do not turn one lab result into a universal production rule.

Next, continue to Node Similarity, KNN, Jaccard/Overlap/Cosine Concepts, and Candidate Generation. Carry forward the verified assumptions, fixture state, version/edition boundaries, and measurements from this lesson instead of treating the next topic as an isolated recipe.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.