Worked, byte-checked examples — some with integer answers. ← benchmark · gallery · repo
For a graph with degrees dv, total degree 2m = Σv dv, and a partition into communities j with volume Vj = Σv∈j dv and cut gj (edge weight leaving j), the 2D structural entropy is
The 1D entropy H1 = −Σv(dv/2m)log2(dv/2m)
is the same quantity with no partition (every node its own "module" sharing the whole graph as parent);
it is an upper bound that H2 improves on whenever the graph has community structure. Below,
the module and within columns are the per-community contributions to each sum;
the displayed totals are reproduced exactly by selib.
Two nodes, one edge. Whatever the partition, both H¹ and H² equal exactly 1 bit: one bit to locate a random walker on one of two symmetric endpoints.
| j | members | Vj | gj | module | within |
|---|---|---|---|---|---|
| 0 | {0, 1} | 2 | 0 | 0.0000 | 1.0000 |
| totals | 0.0000 | 1.0000 | |||
H1 = 1.0000 · H2 = 0.0000 + 1.0000 = 1.0000 = 1 bit exactly
selib.structural_entropy(G, [0, 0]) # -> 1.0000Eight nodes in four independent pairs. The partition-free 1D entropy is log₂8 = 3 bits; describing each pair as its own community drives it down to exactly 1 bit — the cut term vanishes (no edges leave a pair, g=0) and only the within-pair bit remains.
| j | members | Vj | gj | module | within |
|---|---|---|---|---|---|
| 0 | {0, 1} | 2 | 0 | 0.0000 | 0.2500 |
| 1 | {2, 3} | 2 | 0 | 0.0000 | 0.2500 |
| 2 | {4, 5} | 2 | 0 | 0.0000 | 0.2500 |
| 3 | {6, 7} | 2 | 0 | 0.0000 | 0.2500 |
| totals | 0.0000 | 1.0000 | |||
H1 = 3.0000 · H2 = 0.0000 + 1.0000 = 1.0000 = 1 bit exactly
selib.structural_entropy(G, [0, 0, 1, 1, 2, 2, 3, 3]) # -> 1.0000A 4-cycle has 1D entropy log₂4 = 2 bits. Cutting it into two adjacent pairs costs a half-bit of cut entropy but saves a full bit inside the communities → H² = 1.5 bits.
| j | members | Vj | gj | module | within |
|---|---|---|---|---|---|
| 0 | {0, 1} | 4 | 2 | 0.2500 | 0.5000 |
| 1 | {2, 3} | 4 | 2 | 0.2500 | 0.5000 |
| totals | 0.5000 | 1.0000 | |||
H1 = 2.0000 · H2 = 0.5000 + 1.0000 = 1.5000
selib.structural_entropy(G, [0, 0, 1, 1]) # -> 1.5000Two triangles joined by a single bridge edge. The SE-optimal partition is the two triangles; the lone bridge is the only cut, so the module term is tiny and H² ≈ 1.70 bits — the textbook case where SE recovers the obvious two communities.
| j | members | Vj | gj | module | within |
|---|---|---|---|---|---|
| 0 | {0, 1, 2} | 7 | 1 | 0.0714 | 0.7783 |
| 1 | {3, 4, 5} | 7 | 1 | 0.0714 | 0.7783 |
| totals | 0.1429 | 1.5567 | |||
H1 = 2.5567 · H2 = 0.1429 + 1.5567 = 1.6995
selib.structural_entropy(G, [0, 0, 0, 1, 1, 1]) # -> 1.6995Structural entropy is usually motivated by a random walk: on a connected graph the stationary distribution is πv = dv/2m, and the formulas above are its expected description length under a partition. But the quantities they use — degrees, community volumes Vj, and cuts gj — are purely combinatorial, so the entropy is well-defined for any graph, connected or not. selib computes it directly from those quantities. Concretely:
weight (default 1); the degree
dv is the weighted degree and 2m = Σv dv.The 2D-SE numbers here are validated against code that implements the formula independently. Full per-run report: se_crosscheck.json.
selib is 1.8e-15 bits, i.e. floating-point machine precision. The definitions are identical.tree_entropy, the Angsheng-Li-group formula) to 3.6e-15 bits, scoring the trees built by selib's se_hier and the original BBM and HCSE code (github.com/Hardict/HCSE), over connected, disconnected, weighted and isolated-node graphs. (HCSE/BBM's own code is only defined for connected graphs; selib handles all cases.)selib's H2 = 4.483 of the same partition — the definition is confirmed via glass-jax above, while the normalization differs.selib.calc and asserted equal to the
hand-derived breakdown in tutorial.json — if the arithmetic and the library
ever disagreed, the build would fail.