From 31cee350e1ee0533da3215cf645a65b5cf15d0f1 Mon Sep 17 00:00:00 2001 From: Net Zhang Date: Thu, 18 Jun 2026 09:20:37 -0400 Subject: [PATCH 1/2] Make PCA projection reproducible given seed sklearn auto-selects the randomized SVD solver for large inputs, calling PCA(n_components=2) without `random_state` produced a different projection on every run. Now fixed by passing the seed to the API call. cuML PCA is left unchanged: it has no random_state parameter (passing one raises TypeError and silently falls back to sklearn), and its full-SVD solver is already deterministic. Verified on a V100: cuML PCA stays on GPU path and is reproducible run-to-run. --- shared/utils/clustering.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shared/utils/clustering.py b/shared/utils/clustering.py index 4f85e83..4de3857 100644 --- a/shared/utils/clustering.py +++ b/shared/utils/clustering.py @@ -199,7 +199,9 @@ def _reduce_dim_sklearn(embeddings: np.ndarray, method: str, seed: Optional[int] effective_workers = -1 if n_workers > 1 else n_workers if method.upper() == "PCA": - reducer = PCA(n_components=2) + # Pass random_state so the randomized SVD solver (auto-selected for + # large inputs) is reproducible when a seed is set; None keeps it random. + reducer = PCA(n_components=2, random_state=seed) elif method.upper() == "TSNE": # Adjust perplexity to be valid for the sample size n_samples = embeddings.shape[0] @@ -244,6 +246,8 @@ def _reduce_dim_cuml(embeddings: np.ndarray, method: str, seed: Optional[int], n if method.upper() == "PCA": from cuml.decomposition import PCA as cuPCA + # cuML PCA takes no random_state and needs none: its full-SVD solver + # is deterministic, so results are already reproducible run-to-run. reducer = cuPCA(n_components=2) elif method.upper() == "TSNE": from cuml.manifold import TSNE as cuTSNE From 6f9f844468f94e538fc0b442894615d359d359fd Mon Sep 17 00:00:00 2001 From: Net Zhang Date: Thu, 16 Jul 2026 17:09:27 -0400 Subject: [PATCH 2/2] Document reproducibility guarantees in the README Verified empirically on both backends (sklearnex-patched and vanilla sklearn on CPU; cuML 26.4 on an H100): with a fixed seed and a pinned backend, PCA/UMAP/KMeans/t-SNE reproduce bit-identically across process restarts, except cuML t-SNE, which RAPIDS documents as not completely deterministic even with random_state (rapidsai/cuml#2980). Co-Authored-By: Claude Fable 5 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 2cff7ad..9bcb32d 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,15 @@ uv pip install -e ".[gpu-cu13]" The app auto-detects GPU availability at runtime and falls back to CPU if anything goes wrong — no configuration needed. The CPU sklearn path is auto-accelerated by [scikit-learn-intelex](https://github.com/uxlfoundation/scikit-learn-intelex)[^1]. You can also manually select backends (`cuML`, `sklearn`) in the sidebar. +### Reproducibility + +To get reproducible projections and clusters, enable **Use fixed seed** in the sidebar and pin the backend instead of `auto`: the GPU backend is `cuML`, the CPU backend is `sklearn` (auto-accelerated by `scikit-learn-intelex` on x86 CPUs). With a seed and a pinned backend, results are identical across app restarts on both backends, with one exception: cuML t-SNE, which never reproduces exactly. + +- **PCA** is a deterministic decomposition, with no stochastic optimization involved. cuML PCA uses a full eigendecomposition and always returns the same result, seed or no seed. sklearn can auto-select a randomized SVD solver, so the app passes the seed to make it reproducible. +- **UMAP** and **KMeans** reproduce exactly on both backends when a seed is set. (Seeded UMAP trades some speed for determinism.) +- **t-SNE** reproduces on `sklearn` when a seed is set. cuML's implementation is highly parallelized and documented as [not completely deterministic between runs, even with the same `random_state`](https://docs.rapids.ai/api/cuml/stable/api/generated/cuml.manifold.tsne/) (see [rapidsai/cuml#2980](https://github.com/rapidsai/cuml/issues/2980)). Select `sklearn` when t-SNE results need to be reproducible. +- `auto` chooses a backend from data size and hardware, so the same seed can run different algorithms on different machines. Exact coordinates may also differ across library versions and hardware; a seed guarantees repeatability within one environment, not across environments. + ## Usage ### Standalone Apps