Skip to content

Estimators

EVoC runs the whole pipeline on the CPU. EVoCGpu moves the kNN stage onto the GPU and is only present in a build carrying the gpu feature.

estimator

The EVoC estimators.

EVoC runs the whole pipeline on the CPU. EVoCGpu moves the kNN stage onto the GPU and leaves everything else where it was, which is the only part of EVoC that is worth the transfer.

EVoC

EVoC(
    n_neighbours: int = 15,
    *,
    noise_level: float = 0.5,
    n_epochs: int = 50,
    embedding_dim: int | None = None,
    neighbour_scale: float = 1.0,
    symmetrise: bool = True,
    min_samples: int = 5,
    base_min_cluster_size: int = 5,
    approx_n_clusters: int | None = None,
    min_similarity_threshold: float = 0.2,
    max_layers: int = 10,
    ann_algorithm: str = "nndescent",
    metric: str = "euclidean",
    n_tree: int = 50,
    search_budget: int | None = None,
    m: int = 16,
    ef_construction: int = 200,
    ef_search: int = 100,
    diversify_prob: float = 1.0,
    delta: float = 0.001,
    ef_budget: int | None = None,
    extract_knn: bool = True,
    bt_budget: float = 0.05,
    n_list: int | None = None,
    n_probes: int | None = None,
    seed: int = 42,
    verbose: int = 0,
)

Bases: BaseEstimator

Cluster high-dimensional embeddings with EVoC.

Six stages: an approximate kNN graph, a fuzzy simplicial set over it, a label-propagation initialisation, a UMAP-like node embedding, an MST over mutual reachability distances, then a hierarchy of clusterings pulled out of that tree by persistence.

The hierarchy is the point. cluster_layers_ holds one labelling per granularity, finest first, and labels_ picks the layer with the highest persistence score. Set approx_n_clusters instead if you know how many clusters you want, and the finest layer is binary-searched for it.

Parameters:

Name Type Description Default
n_neighbours int

Neighbours per point in the kNN graph.

15
noise_level float

Repulsion strength in the embedding gradient. 0.0 is aggressive, 1.0 conservative.

0.5
n_epochs int

Embedding optimisation epochs.

50
embedding_dim int | None

Embedding dimensionality. None derives it from n_neighbours as clamp(n_neighbours // 4, 4, 16).

None
neighbour_scale float

Multiplier on the effective neighbour count when building the fuzzy graph.

1.0
symmetrise bool

Whether to symmetrise the fuzzy graph.

True
min_samples int

Points used for the core distance in the MST density estimate.

5
base_min_cluster_size int

Minimum cluster size at the finest layer.

5
approx_n_clusters int | None

Binary-search base_min_cluster_size for roughly this many clusters and return a single layer. None returns the full hierarchy.

None
min_similarity_threshold float

Jaccard similarity above which two layers count as redundant and the coarser one is dropped.

0.2
max_layers int

Cap on the layers returned.

10
ann_algorithm str

kNN backend. One of nndescent, hnsw, annoy, ivf, kmknn, balltree, exhaustive.

'nndescent'
metric str

Distance metric, euclidean or cosine.

'euclidean'
n_tree int

Annoy trees.

50
search_budget int | None

Annoy candidates per query. None uses the crate default.

None
m int

HNSW connections per layer.

16
ef_construction int

HNSW construction budget.

200
ef_search int

HNSW search budget.

100
diversify_prob float

NN-Descent diversification probability.

1.0
delta float

NN-Descent convergence threshold.

0.001
ef_budget int | None

NN-Descent beam budget when querying. Ignored when extract_knn is True, since no search runs.

None
extract_knn bool

For nndescent, return the graph the descent already built rather than beam-searching it. On by default: a self-kNN query re-does the work the descent just did.

True
bt_budget float

BallTree search budget, as a fraction of the sample count.

0.05
n_list int | None

IVF cells. None uses sqrt(n).

None
n_probes int | None

IVF cells probed per query. None uses sqrt(n_list).

None
seed int

Random seed.

42
verbose int

0 silent, 1 normal, 2 detailed.

0

Attributes:

Name Type Description
labels_ ndarray | None

(n_samples,) int64 labels from the highest-persistence layer. -1 is noise.

membership_strengths_ ndarray | None

(n_samples,) strengths in [0, 1] for labels_.

cluster_layers_ ndarray | None

(n_layers, n_samples) int64, every layer, finest first.

layer_strengths_ ndarray | None

(n_layers, n_samples) strengths for every layer.

persistence_scores_ ndarray | None

(n_layers,) float64, higher is more stable.

neighbour_graph_ tuple[ndarray, ndarray] | None

(distances, indices) from the kNN stage, self excluded.

n_clusters_ int

Non-noise clusters in labels_.

n_features_in_ int | None

Columns seen during fit.

Example

import numpy as np from evoc_rs import EVoC rng = np.random.default_rng(0) X = np.vstack([rng.normal(c * 20, 1, (200, 32)) for c in range(3)]) labels = EVoC(n_neighbours=15).fit_predict(X.astype(np.float32))

n_clusters_ property

n_clusters_: int

Non-noise clusters in labels_.

Returns:

Type Description
int

The count, 0 when everything is noise.

Raises:

Type Description
NotFittedError

If fit has not run.

fit

fit(
    X: ndarray,
    y: None = None,
    *,
    precomputed_knn: tuple[ndarray, ndarray] | None = None,
) -> EVoC

Cluster X.

Parameters:

Name Type Description Default
X ndarray

(n_samples, n_features) design matrix.

required
y None

Ignored. Present so the estimator fits in a scikit-learn pipeline.

None
precomputed_knn tuple[ndarray, ndarray] | None

(indices, distances), both (n_samples, k) and excluding self. Skips the kNN stage entirely.

None

Returns:

Type Description
EVoC

The fitted estimator, so calls chain.

fit_predict

fit_predict(
    X: ndarray,
    y: None = None,
    *,
    precomputed_knn: tuple[ndarray, ndarray] | None = None,
) -> ndarray

Cluster X and return the labels.

Parameters:

Name Type Description Default
X ndarray

(n_samples, n_features) design matrix.

required
y None

Ignored.

None
precomputed_knn tuple[ndarray, ndarray] | None

As for fit.

None

Returns:

Type Description
ndarray

(n_samples,) int64 labels, -1 for noise.

EVoCGpu

EVoCGpu(
    n_neighbours: int = 15,
    *,
    noise_level: float = 0.5,
    n_epochs: int = 50,
    embedding_dim: int | None = None,
    neighbour_scale: float = 1.0,
    symmetrise: bool = True,
    min_samples: int = 5,
    base_min_cluster_size: int = 5,
    approx_n_clusters: int | None = None,
    min_similarity_threshold: float = 0.2,
    max_layers: int = 10,
    ann_algorithm: str = "ivf_gpu",
    metric: str = "euclidean",
    n_list: int | None = None,
    n_probes: int | None = None,
    k: int | None = None,
    k_build: int | None = None,
    n_tree: int | None = None,
    delta: float = 0.001,
    rho: float | None = None,
    beam_width: int | None = None,
    max_beam_iters: int | None = None,
    n_entry_points: int | None = None,
    extract_knn: bool = True,
    seed: int = 42,
    verbose: int = 0,
)

Bases: EVoC

EVoC with the kNN stage on the GPU.

Everything downstream of the graph stays on the CPU, so this only pays off when the kNN search dominates: many points, high dimension, or an exhaustive search. float32 only, because WGSL has no f64.

Args and attributes are those of EVoC, with these differences:

Parameters:

Name Type Description Default
ann_algorithm str

One of exhaustive_gpu, ivf_gpu, nndescent_gpu.

'ivf_gpu'
k int | None

CAGRA graph degree after pruning, for nndescent_gpu. None backfills from n_neighbours.

None
k_build int | None

Build degree before pruning. None backfills to 2 * n_neighbours.

None
n_tree int | None

Trees initialising the kNN graph. None uses the crate default.

None
rho float | None

NN-Descent sampling rate. None auto-picks.

None
beam_width int | None

Beam width when querying. None auto-picks.

None
max_beam_iters int | None

Beam iterations when querying. None auto-picks.

None
n_entry_points int | None

Entry points when querying. None auto-picks.

None
extract_knn bool

For nndescent_gpu, return the CAGRA graph the build already produced rather than beam-searching it. On by default, and worth keeping: it is roughly 5x faster for about 0.4% recall.

True