From 31cee350e1ee0533da3215cf645a65b5cf15d0f1 Mon Sep 17 00:00:00 2001 From: Net Zhang Date: Thu, 18 Jun 2026 09:20:37 -0400 Subject: [PATCH] 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