Fix/mdl metric benchmark#1429
Open
popchanovska wants to merge 10 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MDL Metric Benchmark: Results and Assessment
This documents an actual run of
py3plex/algorithms/community_detection/mdl_benchmark.pyand
tests/test_mdl_benchmark.py, on this branch (with the resolution-limitfix in
_block_parameter_cost— seeMDL_METRIC_CHANGES.md§8 — alreadyapplied). All numbers below come from running the code, not from reasoning
about it in the abstract.
1. What was run
plus a few ad-hoc extensions (larger
nssweep for scaling, per-partition-typescore breakdowns, and a community-composition analysis) using the same
run_benchmark/_louvain_partitionfunctions, to explain why theheadline numbers look the way they do rather than just report them.
1a.
tests/test_mdl_benchmark.py— smoke testsAll four pass:
run_benchmarkproduces all three partition types(
ground_truth,corrupted,louvain) for every(mu, n, seed)group,every score is finite and in its valid range,
ranking_agreement_reportand
scaling_reporthave the expected shape. This is scaffoldingverification only — it does not check that
mdl_scoreactually behaveswell, by design (see the module docstring, which I updated during this
session: it used to document the pre-fix bug as a known, accepted
limitation; it now points here instead).
1b.
mdl_benchmark.py— the real benchmark, default parametersDefault sweep:
mu ∈ {0.05, 0.1, 0.2, 0.3, 0.4, 0.5},n=200nodes,3 layers,
avg_degree=10,min_community=20,seeds ∈ {0, 1, 2}(18(mu, seed)groups × 3 partition types = 54 scored partitions):2. What this shows is working well
2a.
mdl_scorenow correctly ranks ground truth best — 100% of the timeGround truth wins on every single
(mu, seed)group in the default sweep,across the full easy-to-hard range (
mu=0.05tomu=0.5). This is thedirect, empirical payoff of the resolution-limit fix from the previous
session: before it,
mdl_scorehad no cost for specifying block-densityparameters, so a maximally-fragmented/near-clique partition could win by
construction regardless of the graph.
tests/test_mdl_benchmark.py's owndocstring documented this as a known, accepted limitation before the fix
— it now no longer needs to.
Breaking this down by partition type (mean over 3 seeds, default sweep):
mdl_score(ground_truth) < mdl_score(corrupted) < mdl_score(louvain)holdsat every
mushown (and in all but the one flagged disagreement across thewhole sweep) — a corrupted partition (30% of node-layer assignments
randomly reassigned) is consistently and correctly penalized relative to
the true structure, and gets worse as
mugrows (more genuine mixing meansless signal for
mdl_scoreto reward truth over noise, which is theexpected, not concerning, direction).
2b. Agreement with modularity is strong once structure is clear
Spearman rank correlation between MDL's and modularity's ranking of the
three partitions per group is
+1.000(perfect agreement) formu ∈ {0.3, 0.4, 0.5}, and still strongly positive (+0.833,+0.5) atlower
mu. The two metrics are largely measuring the same thing, which isa reasonable sanity check for a metric that is supposed to be an
alternative/complement to modularity, not a replacement measuring something
unrelated.
2c. The one MDL/modularity disagreement is informative, not alarming
At
mu=0.05, seed=1, MDL picksground_truthas best while modularitypicks
louvain. This is the interesting case the benchmark was built tosurface:
replica_consistencyfor both ground truth and louvain is1.0in that regime (§2a's low-mu row), so this isn't
mdl_scorebeing fooled —it's a case where Louvain's flat-graph optimization found a partition with
marginally higher raw modularity than the true generative one, which can
happen with noisy finite samples. MDL siding with the true generative
structure here is defensible behavior, not a red flag.
2d. Performance holds up in practice, matching the earlier unit-test guarantees
Mean
mdl_scorecompute time is0.0087–0.0108s at n≈600 — comparableto
multilayer_modularity(0.0053–0.0075s) and both dominatereplica_consistency(~0.0002s), which is expected since MDL andmodularity both do real graph-structure work while replica consistency is
a cheap per-node lookup. Extending the sweep to larger graphs (
n ∈ {100, 300, 600, 1200}nodes per layer, 3 layers, so up to 3,600 node-layerpairs) confirms this scales as expected, not quadratically:
mdl_scoretracksmultilayer_modularity's growth rate closely across a12x increase in graph size (~18x time increase, roughly in line with
O(N + |E|)for a graph whose edge count also grows withN), consistentwith the
O(N + |E| + D²)complexity guarantee from the resolution-limitfix (
D, the number of distinct community sizes, stays small here sincethese are real, non-adversarial partitions).
3. What needs further improvement
3a.
louvain_multilayer/leiden_multilayerare not what the benchmark uses — and that gap is itself a findingThe benchmark deliberately does not use this package's own
leiden_multilayer/louvain_multilayeras the "real algorithm" baseline.As established in the previous session, both are single-level local search
with no hierarchical coarsening/aggregation phase (no
induced_graphequivalent), which gets stuck in poor local optima past toy-sized graphs.
This is a separate, pre-existing bug this benchmark does not fix — it just
routes around it by using the vendored
community_louvain.best_partition(real multi-level Louvain) instead. This means the "louvain" column
above is not exercising this package's own multilayer algorithms at all.
Until that's fixed, there is no benchmark evidence for how well
py3plex's own community-detection algorithms perform againstmdl_score— only for how well a generic, non-multilayer-aware Louvain does.
3b. Even the working Louvain baseline breaks down at high
mu— and not for a subtle reasonLouvain's
replica_consistencycollapses from1.0000atmu=0.05to0.1507atmu=0.5(§2a table), and its modularity drops far below eventhe corrupted ground-truth partition's at high
mu(0.0287vs0.0926atmu=0.5). Inspecting the actual community compositionexplains why concretely: at
mu=0.5, Louvain's partition consists ofthree giant communities, each 100% pure by layer — i.e., it has
partitioned the graph by layer identity, not by the true cross-layer
community structure:
At
mu=0.05the same partition is layer-balanced and perfectlygt-aligned instead (
layer_purity=0.33,gt_purity=1.00— each bigcommunity spans all 3 layers evenly, matching the true communities). The
transition is visible already at
mu=0.2, where one community is stillgt-aligned and two have already flipped to pure-by-layer. Mechanically:
generate_multilayer_lfrcouples layers with a fixed,mu-independent setof identity edges (1,800 coupling edges regardless of
mu, vs. 4,059–5,460intra-layer edges that do vary with
mu). Asmugrows, truewithin-layer community signal weakens, but each layer's internal
density stays much higher than what the sparse, fixed-weight coupling
edges can hold together — so once the true-community signal degrades
below the "same layer" signal, real multi-level Louvain (working correctly
on the graph it's given) rationally re-discovers "same layer" as the
higher-modularity partition instead. This is a benchmark/coupling-design
limitation, not a Louvain bug: coupling edges most likely need weight
scaling (relative to intra-layer edge density) to remain competitive as
mugrows, or the benchmark should report/flag this regime explicitlyrather than only via the resulting metric scores.
3c. The benchmark has only run at one graph size for the ranking/agreement report
The headline "100% ground truth best" result and the Spearman-agreement
table are from
n=200nodes only (ns=(200,)is therun_benchmarkdefault). The
nsscaling sweep in §2d only measured timing, using adifferent (
mu=0.1, 0.3, single seed) configuration — it did not re-checkranking correctness at those larger sizes. It would be worth confirming
the 100%-best-ground-truth result holds (or degrades gracefully) at larger
nand with more seeds, especially since §3b shows the couplingdynamics change qualitatively with graph parameters — a similar
size-dependent surprise for
mdl_scoreitself can't be ruled out withoutrunning it.
3d. Corruption model is one specific kind of "bad partition"
_corrupt_partitiononly tests one failure mode: uniformly-randomrelabeling of a fixed fraction of node-layer pairs. It does not exercise
the specific resolution-limit exploit pattern (deterministic,
maximally-fragmented block structure) that motivated the metric fix in the
first place, nor does it test partial partitions (the comment #1 exploit)
or parallel/weighted-edge inputs (the comment #3 fix) at benchmark scale —
those are only covered by the unit tests in
tests/test_mdl_score.py. Abenchmark-scale adversarial partition generator (not just random
corruption) would give more direct evidence that the fixes made in this
session hold up outside of hand-built toy graphs.
3e.
resolutionparameter is fixed at 1.0 throughoutrun_benchmark'sresolutionparameter (passed tocommunity_louvain.best_partition) defaults to1.0and is never swept.Given §3b shows Louvain's behavior is sensitive to the relative strength of
intra- vs. inter-layer signal, it's plausible that tuning resolution could
recover better cross-layer community detection at high
mu— worthchecking before concluding the coupling-weight explanation in §3b is the
only lever available.
4. Summary
mdl_scoreranks ground truth best across the fullmusweepmdl_scoreagrees with modularity when structure is clearmdl_scorecompute cost at realistic scaleleiden_multilayer/louvain_multilayer(separate known bug)mu