You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
method="leiden" / method="leiden-cpm" — graph clustering / community detection over the
microcluster affinity graph via the full Leiden algorithm (Traag, Waltman & van Eck 2019):
local moving → refinement (sub-communities grown from singletons along edges, so each is
connected by construction — Leiden's guarantee over Louvain) → aggregation seeded from the
pre-refinement partition. It discovers the community count — no k (like the density head).
A resolution (γ) knob trades community count against size; the modularity objective
("leiden", γ = 1 default) has a resolution limit, the CPM objective ("leiden-cpm") is
resolution-limit-free (γ on a smaller, density scale). Pure Rust — no eigensolver, NumPy-only.
Best for community/blob structure at a moderate threshold; use method="spectral" for elongated
manifolds. The self-tuning k-NN affinity graph is shared between the spectral and Leiden heads.
betula_cluster.consensus(X, n_clusters, n_runs=…) — clusters X under several random
insertion-order permutations and votes, turning the CF-tree's insertion-order sensitivity
(Known Limitation #1) into a measurable quantity: a consensus labelling plus a per-point
stability score in [0, 1] (ConsensusResult.confidence — low on unstable boundaries, high
where every order agrees). NumPy-only; for the partitional heads at a fixed n_clusters. The
independent runs parallelize across threads with n_jobs (the Rust core releases the GIL).
method="spectral" — spectral clustering over the CF-tree leaf microclusters for non-convex /
manifold clusters (moons, rings, spirals) that the centroid heads cannot separate. Self-tuning
symmetric k-NN affinity (Zelnik-Manor & Perona local scaling), the normalized Laplacian embedding
(Ng-Jordan-Weiss) via the in-house Jacobi eigensolver — no LAPACK/ARPACK, the crate stays
NumPy-only — with a k-means landmark reduction above 256 microclusters so the O(m³) solve stays
bounded. Dense input only; pair it with a small threshold (many leaves) so the microclusters
resolve the manifold. No built-in cluster-count selection: n_clusters=0 defaults to 2.
threshold="auto" for the Betula estimator — removes the one hyperparameter users most often
have to guess. A bounded-subsample pilot fits a threshold=0 tree at the same max_leaves and
reads the threshold it converges to, warm-starting the full fit near-converged instead of growing
it from zero (fewer rebuild passes, lower peak leaf count on large n). Cached across refits /
streaming batches; below the pilot cap it is a no-op (growing from zero is already cheap), and it
is dense-only (raises on sparse input).
Changed
Benchmarks now cover every head (spectral, Leiden added to bench/comprehensive.py) and the
compression heads run at max_leaves = 4000: betula-kmeans is at exact parity with scikit-learn
(blobs 0.861 = 0.861) and Ward beats raw Ward while running the full N. Docs / README / docs site
surface the spectral, Leiden and consensus additions; test counts reconciled (190 Python, 158
Rust). The docs site now renders the CHANGELOG and redeploys on every published release.