Skip to content

betula-cluster v0.1.5

Latest

Choose a tag to compare

@github-actions github-actions released this 04 Jul 19:22

Added

  • 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.