A standardized library for structural entropy — compute it, optimize it, and benchmark SE methods against classical baselines under one API.
GitHub repo · SE gallery · SE tutorial · recursive structures · periodic table · survey maps · raw results JSON · reproduction benchmark · survey paper
Every SE paper ships its own loader, metric, and (often unmaintained) code, so
"does structural entropy actually help?" is hard to answer on equal footing.
selib fixes the interface: one Method.fit_predict(G, k, seed)
contract, a native 2D-structural-entropy encoding-tree core, shared datasets and
metrics, and a one-call benchmark — so an SE method and a Louvain baseline are
scored exactly the same way. The tables below are produced by that benchmark; the
headline takeaway matches the survey's regime-dependent verdict.
import selib
recs = selib.benchmark(["louvain", "leiden", "se_agglomerative"],
["Karate", "SBM-Clean", "SBM-Noisy"])
selib.summarize(recs, "nmi")
The shipped se_agglomerative only ever merges, so an early bad merge can never be undone — it gets stuck in poor local optima. se_louvain is this library's stronger minimizer of the same 2D structural-entropy objective: Louvain-style local node moves + community aggregation + multistart, with an O(degree) exact move delta. Validated against the canonical metric and against brute-force exhaustive optima on small graphs (gap 0.000).
2D structural entropy reached on each graph (lower is better); Δ is the relative reduction of se_louvain over the naive merger. se_louvain wins on 8/9 graphs.
| se_agglomerative | se_louvain | Δ (lower=better) | |
|---|---|---|---|
| Karate | 3.731 | 3.731 | +0.0% |
| SBM-Clean | 6.035 | 6.031 | +0.1% |
| SBM-Noisy | 6.419 | 6.407 | +0.2% |
| LFR-mu0.1 | 6.175 | 6.164 | +0.2% |
| LFR-mu0.2 | 6.673 | 6.662 | +0.2% |
| LFR-mu0.3 | 7.204 | 7.103 | +1.4% |
| LFR-mu0.4 | 7.750 | 7.551 | +2.6% |
| LFR-mu0.5 | 8.064 | 7.895 | +2.1% |
| LFR-mu0.6 | 8.129 | 8.055 | +0.9% |
Structural entropy is defined over an encoding tree, not just a flat partition. se_hier builds a multilevel tree (binary se_agglomerative dendrogram + recursive se_louvain inits) and refines it with exact-guarded local moves — collapse a redundant level, relocate a subtree — accepting a move only when it strictly lowers the exact tree structural entropy HT. Because refinement starts from the naive dendrogram and only takes improving moves, the result is ≤ the naive dendrogram by construction; it is strictly lower on 6/6 graphs.
| se_agglomerative | louvain_2level | se_hier | Δ vs naive | |
|---|---|---|---|---|
| Karate | 2.6932 | 3.3275 | 2.4856 | +7.7% |
| SBM-Clean | 5.3071 | 6.0304 | 5.0286 | +5.2% |
| SBM-Noisy | 5.4496 | 6.1747 | 5.3495 | +1.8% |
| LFR-mu0.1 | 4.3636 | 5.0671 | 4.1184 | +5.6% |
| LFR-mu0.3 | 5.0990 | 5.7360 | 5.0343 | +1.3% |
| LFR-mu0.5 | 5.4456 | 6.1653 | 5.3532 | +1.7% |
| se_agglomerative | louvain_2level | se_hier | Δ vs naive | |
|---|---|---|---|---|
| Karate | 2304.0 | 3746.0 | 2625.0 | -13.9% |
| SBM-Clean | 81858.0 | 108050.0 | 85440.0 | -4.4% |
| SBM-Noisy | 93208.0 | 127962.0 | 93041.0 | +0.2% |
| LFR-mu0.1 | 39778.0 | 56416.0 | 40673.0 | -2.2% |
| LFR-mu0.3 | 80040.0 | 104988.0 | 84418.0 | -5.5% |
| LFR-mu0.5 | 99638.0 | 131146.0 | 100689.0 | -1.1% |
Validated in selib.htree: a 2-level tree's HT equals the canonical 2D structural entropy exactly, and refinement is monotone. louvain_2level is a flat (2-level) hierarchy baseline.
All methods run on the identical graphs, all at free k (so the structural-entropy objective is compared fairly). The published SE community detectors CoDeSEG (original C++) and deDoc (original Java, the founding SE algorithm) run their real implementations through selib's wrappers. For the hierarchical comparison the original HCSE and BBM code (github.com/Hardict/HCSE) build the trees, and classical hierarchies (Paris, average / Ward linkage) are included — all scored on the same encoding-tree objective as se_hier. (* = structural-entropy method.)
| louvain | leiden | infomap | codeseg * | dedoc * | se_louvain * | |
|---|---|---|---|---|---|---|
| Karate | 3.311 | 3.279 | 3.496 | 3.428 | 4.483 | 3.229 |
| SBM-Clean | 6.030 | 6.030 | 6.030 | 6.316 | 6.035 | 5.978 |
| SBM-Noisy | 6.196 | 6.200 | 7.183 | 6.545 | 7.166 | 6.178 |
| LFR-mu0.1 | 5.067 | 5.067 | 5.067 | 5.533 | 7.154 | 5.067 |
| LFR-mu0.3 | 5.736 | 5.744 | 5.823 | 6.107 | 7.162 | 5.728 |
| LFR-mu0.5 | 6.163 | 6.122 | 7.141 | 6.424 | 7.141 | 6.129 |
| louvain | leiden | infomap | codeseg * | dedoc * | se_louvain * | |
|---|---|---|---|---|---|---|
| Karate | 0.596 | 0.588 | 0.568 | 0.441 | 0.340 | 0.499 |
| SBM-Clean | 1.000 | 1.000 | 1.000 | 0.418 | 0.985 | 0.766 |
| SBM-Noisy | 0.107 | 0.089 | 0.000 | 0.227 | 0.362 | 0.134 |
| LFR-mu0.1 | 1.000 | 1.000 | 1.000 | 0.830 | 0.541 | 1.000 |
| LFR-mu0.3 | 0.939 | 0.944 | 0.903 | 0.645 | 0.541 | 0.877 |
| LFR-mu0.5 | 0.268 | 0.284 | 0.000 | 0.394 | 0.540 | 0.308 |
On its own objective, se_louvain wins: it reaches the lowest 2D structural entropy on every graph — below the modularity methods and the published CoDeSEG. NMI tells a subtler, honest story: minimizing 2D-SE at free k tends to over-segment relative to the planted communities, so on clean graphs the modularity methods (Louvain/Leiden) recover ground truth better even though their structural entropy is higher. Run with a target k (§1) se_louvain recovers ground truth strongly. It beats the published SE detector CoDeSEG on NMI on 4/6 graphs; CoDeSEG's finer segmentation helps only on the hardest (noisy SBM, μ=0.5). Lesson: lower structural entropy ≠ better label recovery — a point the survey makes. deDoc (the founding SE algorithm, original Java) recovers the clean SBM almost perfectly (NMI 0.985) but degenerates to near-singletons on sparse Karate/LFR — it was designed for dense weighted Hi-C matrices; this exactly reproduces the survey campaign's numbers. (Its successor deDoc2 is excluded here: it only forms contiguous TAD-like domains over genomically-ordered bins, so on a general graph it groups consecutive node IDs and ignores the topology — a Hi-C tool, not a community detector.)
| se_hier * | se_agglomerative * | bbm * | hcse * | paris | average | ward | |
|---|---|---|---|---|---|---|---|
| Karate | 2.486 | 2.693 | 3.234 | 2.684 | 2.486 | 2.795 | 2.791 |
| SBM-Clean | 5.029 | 5.307 | 5.350 | 6.656 | 5.032 | 5.130 | 5.288 |
| SBM-Noisy | 5.349 | 5.450 | 5.795 | 6.671 | 5.349 | 5.474 | 5.473 |
| LFR-mu0.1 | 4.118 | 4.364 | 4.309 | 4.742 | 4.124 | 4.345 | 4.273 |
| LFR-mu0.3 | 5.034 | 5.099 | 5.521 | 6.560 | 5.059 | 5.283 | 5.290 |
| LFR-mu0.5 | 5.353 | 5.446 | 5.697 | 6.583 | 5.353 | 5.459 | 5.503 |
| se_hier * | se_agglomerative * | bbm * | hcse * | paris | average | ward | |
|---|---|---|---|---|---|---|---|
| Karate | 2625 | 2304 | 3479 | 2796 | 2625 | 2802 | 2829 |
| SBM-Clean | 85440 | 81858 | 86112 | 171938 | 85409 | 89480 | 98563 |
| SBM-Noisy | 93041 | 93208 | 97293 | 138163 | 93041 | 93134 | 95586 |
| LFR-mu0.1 | 40673 | 39778 | 41443 | 52376 | 40600 | 46512 | 46067 |
| LFR-mu0.3 | 84418 | 80040 | 96605 | 144122 | 86852 | 91787 | 94429 |
| LFR-mu0.5 | 100689 | 99638 | 104408 | 144936 | 100689 | 100106 | 104012 |
se_hier reaches the lowest HT on every graph — below the original BBM and HCSE code, Paris, and the linkage baselines. It warm-starts from several constructions (binary SE dendrogram, recursive se_louvain, Paris) and refines the best with exact-guarded moves, so it is ≤ each by construction. BBM (deep binary merge) is the strongest original SE tree; HCSE targets a height-constrained hierarchy, so its full-depth HT is higher here. Paris is the best non-SE classical tree.
se_gnn is selib's attribute-aware method (ported from the author's glass-jax prototype): a tiny GCN trained end-to-end to minimize a differentiable soft 2D structural entropy — validated so that the soft objective at a hard assignment equals the canonical 2D-SE exactly. DeSE / LSENet are the published feature-aware SE methods (reproduction-campaign numbers); the rest are run fresh by selib on the same standard Planetoid data.
| se_gnn * | DeSE * | LSENet * | se_louvain * | louvain | leiden | infomap | spectral | |
|---|---|---|---|---|---|---|---|---|
| Cora | 0.487 | 0.579 | 0.495 | 0.120 | 0.449 | 0.466 | 0.453 | 0.039 |
| Citeseer | 0.262 | 0.443 | 0.094 | 0.246 | 0.327 | 0.328 | 0.319 | 0.014 |
| Photo | — | — | 0.667 | — | — | — | — | — |
| se_gnn * | DeSE * | LSENet * | se_louvain * | louvain | leiden | infomap | spectral | |
|---|---|---|---|---|---|---|---|---|
| Cora | 0.387 | 0.522 | 0.418 | -0.004 | 0.246 | 0.259 | 0.265 | -0.004 |
| Citeseer | 0.213 | 0.448 | 0.113 | 0.022 | 0.089 | 0.086 | 0.158 | 0.005 |
| Photo | — | — | 0.574 | — | — | — | — | — |
Reading: feature-aware beats topology-only. se_gnn (2-layer GCN over the normalized adjacency + a balanced Sinkhorn assignment head, best-of-starts selected by the SE objective) finds the full number of communities (k = 6–7, no collapse) and beats every topology method on NMI, ARI and ACC on both datasets — on Cora it matches LSENet (NMI 0.487 vs 0.495) with a far smaller model. The Sinkhorn head is what prevents the cluster collapse that pure SE minimization induces with a plain softmax (ablation in segnn_head_eval.json). DeSE still leads NMI — the remaining gap.
What the optimizers actually produce — node-link layouts coloured by se_louvain communities, and the se_hier encoding tree. Rendered as inline SVG from the run.
Every synthetic comparison repeated over 3 independent graph realizations (means shown). se_louvain has the lowest mean 2D-SE on 5/5 generators; se_hier ≤ Paris (the strongest classical tree) on 5/5.
| se_louvain 2D-SE | best other | lowest? | se_hier HT | Paris HT | ≤ Paris? | |
|---|---|---|---|---|---|---|
| SBM-Clean | 5.977 | 6.026 | ✓ | 5.038 | 5.042 | ✓ |
| SBM-Noisy | 6.173 | 6.184 | ✓ | 5.348 | 5.348 | ✓ |
| LFR-mu0.1 | 4.902 | 4.902 | ✓ | 3.982 | 3.984 | ✓ |
| LFR-mu0.3 | 5.643 | 5.655 | ✓ | 4.951 | 4.996 | ✓ |
| LFR-mu0.5 | 6.117 | 6.138 | ✓ | 5.363 | 5.363 | ✓ |
The first-improvement refinement (exact-guarded, O(m log h) scoring) extends se_hier beyond small graphs: it reaches the lowest HT at every size, with growing margins over Paris and the original BBM.
| se_hier | se_agglomerative | bbm | paris | se_hier time | |
|---|---|---|---|---|---|
| LFR-mu0.3-n500 (n=500) | 6.115 | 6.463 | 6.772 | 6.559 | 535s |
| SBM-n1000 (n=1000) | 5.752 | 6.286 | 6.263 | 5.899 | 1175s |
| LFR-mu0.3-n1000 (n=1000) | 6.594 | 6.757 | 7.495 | 6.938 | 1626s |
| louvain | leiden | infomap | codeseg * | dedoc * | se_louvain * | |
|---|---|---|---|---|---|---|
| Karate | 0.596 | 0.588 | 0.568 | 0.441 | 0.340 | 0.499 |
| SBM-Clean | 1.000 | 1.000 | 1.000 | 0.418 | 0.985 | 0.766 |
| SBM-Noisy | 0.107 | 0.089 | 0.000 | 0.227 | 0.362 | 0.134 |
| LFR-mu0.1 | 1.000 | 1.000 | 1.000 | 0.830 | 0.541 | 1.000 |
| LFR-mu0.3 | 0.939 | 0.944 | 0.903 | 0.645 | 0.541 | 0.877 |
| LFR-mu0.5 | 0.268 | 0.284 | 0.000 | 0.394 | 0.540 | 0.308 |
Higher is better.
| louvain | leiden | infomap | spectral | se_agglomerative | se_louvain | |
|---|---|---|---|---|---|---|
| Karate | 0.535 | 0.465 | 0.591 | 0.882 | 0.882 | 0.882 |
| SBM-Clean | 1.000 | 1.000 | 1.000 | 1.000 | 0.980 | 0.996 |
| SBM-Noisy | 0.037 | 0.049 | 0.000 | 0.053 | 0.015 | 0.022 |
Higher = more modular partition.
| louvain | leiden | infomap | spectral | se_agglomerative | se_louvain | |
|---|---|---|---|---|---|---|
| Karate | 0.442 | 0.445 | 0.432 | 0.404 | 0.404 | 0.404 |
| SBM-Clean | 0.405 | 0.405 | 0.405 | 0.405 | 0.402 | 0.404 |
| SBM-Noisy | 0.206 | 0.093 | 0.000 | 0.171 | 0.152 | 0.165 |
| louvain | leiden | infomap | codeseg * | dedoc * | se_louvain * | |
|---|---|---|---|---|---|---|
| Karate | 3.311 | 3.279 | 3.496 | 3.428 | 4.483 | 3.229 |
| SBM-Clean | 6.030 | 6.030 | 6.030 | 6.316 | 6.035 | 5.978 |
| SBM-Noisy | 6.196 | 6.200 | 7.183 | 6.545 | 7.166 | 6.178 |
| LFR-mu0.1 | 5.067 | 5.067 | 5.067 | 5.533 | 7.154 | 5.067 |
| LFR-mu0.3 | 5.736 | 5.744 | 5.823 | 6.107 | 7.162 | 5.728 |
| LFR-mu0.5 | 6.163 | 6.122 | 7.141 | 6.424 | 7.141 | 6.129 |
SE (se_agglomerative, the native greedy 2D-SE encoding tree) matches the baselines on clean, well-separated structure and on Karate, but does not rescue performance once the planted signal is weak — the same fragility the survey reports for unsupervised community detection.
LFR graphs (n=500), mixing parameter μ on the x-axis: higher μ = more inter-community edges = harder. Mean NMI over 5 seeds.
All methods — SE and baselines alike — degrade together as μ rises and collapse past the detectability threshold; no method's curve is rescued by the SE objective. This is the core empirical message, now reproducible in one call.
SBM with per-node community signal held constant as N grows (k=10 blocks). Left: wall-clock per run; right table: NMI stays high, so runtime is measured on a task every method can solve.
Per-node community signal held constant as N grows.
| louvain | leiden | infomap | spectral | se_agglomerative | se_louvain | |
|---|---|---|---|---|---|---|
| SBM-scale-N200 | 1.000 | 1.000 | 1.000 | 1.000 | 1.000 | 1.000 |
| SBM-scale-N500 | 1.000 | 1.000 | 1.000 | 1.000 | 0.984 | 1.000 |
| SBM-scale-N1000 | 1.000 | 1.000 | 1.000 | 1.000 | 0.995 | 1.000 |
| SBM-scale-N2000 | 1.000 | 1.000 | 1.000 | 1.000 | 0.997 | 1.000 |
The native se_agglomerative core is a pure-Python greedy merge; the optimized C/igraph baselines (Louvain/Leiden/Infomap) are faster, while spectral clustering's dense eigendecomposition scales worst. These are wall-clock facts from the run, not claims.
| dataset | nodes | edges | k (true) |
|---|---|---|---|
| Karate | 34 | 78 | 2 |
| LFR-mu0.1 | 500 | 7017 | 11 |
| LFR-mu0.2 | 500 | 7162 | 11 |
| LFR-mu0.3 | 500 | 7332 | 11 |
| LFR-mu0.4 | 500 | 7514 | 11 |
| LFR-mu0.5 | 500 | 7505 | 11 |
| LFR-mu0.6 | 500 | 7550 | 11 |
| SBM-Clean | 150 | 1419 | 3 |
| SBM-Noisy | 150 | 1185 | 3 |
| SBM-scale-N1000 | 1000 | 8999 | 10 |
| SBM-scale-N200 | 200 | 1793 | 10 |
| SBM-scale-N2000 | 2000 | 18202 | 10 |
| SBM-scale-N500 | 500 | 4498 | 10 |
git clone https://github.com/SuuTTT/selib && cd selib
pip install -e ".[extra]"
python3 scripts/run_full_benchmark.py # writes results/benchmark_results.json
python3 scripts/build_site.py # regenerates this page from that JSON
All numbers on this page are read directly from
results/benchmark_results.json
produced by the run above on an RTX 3070 box. Methods that wrap a paper's original
code (deDoc, CoDeSEG) are available through the same API once their upstream artifact
is configured; the runs shown here use the native methods + classical baselines.