Repository navigation
fix(svd): keep nonneg/L1 constraints on deflation factors after the first - #83
Conversation
…irst The deflation solver re-orthogonalized every factor against the previous ones with two-pass Gram-Schmidt. For factors with projection constraints (nonneg, L1, upper_bound) this re-introduced negative entries and removed L1 zeros on all components after the first, so `pca(..., nonneg = TRUE)` with k > 1 returned mixed-sign loadings. - Skip post-factor Gram-Schmidt on any side with projection constraints (CPU and both GPU deflation paths); unconstrained SVD is unchanged. - Start constrained factors k > 1 from a non-negative random vector rather than the orthogonalized power-step warm start, which could collapse to zero after projection. - method = "auto": choose deflation whenever nonneg/L1/upper_bound is set; warn when krylov is requested with these constraints and k > 1 (its block solve-then-project can return redundant or all-zero components). - Tests: assert non-negativity on all deflation factors, L1 zeros on later factors, auto-method choice, and the krylov warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014zFkHdSUAE1RSwGtTqz9Fi
|
CI note: The job fails in
No fix exists on Suggests:
...
SeuratData,
...
+Remotes:
+ satijalab/seurat-dataAlternatively, drop Generated by Claude Code |
fix(svd): keep nonneg/L1 constraints on deflation factors after the first
Problem
svd()/pca()withmethod = "deflation"re-orthogonalizes each new factor against the previous ones (two-pass Gram-Schmidt). For factors with projection constraints this undoes the projection: withnonneg = c(TRUE, FALSE)andk = 3, components 2–3 came back with 35–180 negative loadings (of 50–200 features), and L1 zeros were lost on every factor after the first. The existing test acknowledged this as a "known limitation".method = "auto"also routed constrained problems withk >= 8to"krylov", whose block solve-then-project step can return redundant (non-deflated) or all-zero components whenk > 1.Changes
deflation.hpp, GPU sparse + densedeflation_gpu.cuh): skip post-factor Gram-Schmidt on any side withnonneg,L1, orupper_bound. Constrained factors are returned as fitted (non-negative/sparse, not mutually orthogonal), as in other constrained/sparse PCA methods. Unconstrained sides are still re-orthogonalized.k > 1start from a non-negative random vector instead of the orthogonalized power-step warm start, which can point into the negative orthant and collapse to zero after projection.R/svd.R:method = "auto"selects"deflation"whenevernonneg/L1/upper_boundis set; explicitly requesting"krylov"with these constraints andk > 1emits a warning.test_svd.R): non-negativity asserted on all deflation factors (replacing the "known limitation" note); new tests for mixed-sign data, L1 zeros on later factors, auto-method choice, and the Krylov warning. Existing Krylov constraint tests wrap the call insuppressWarnings().Verification (CPU, single thread, OpenBLAS; simulated data)
Non-negative PCA (
pca(t(X), k = 3, nonneg = c(TRUE, FALSE), method = "deflation")) vs CRANnsprcomp0.5.1.2 (nneg = TRUE, 5 restarts):L1 = c(0.5 | 2 | 5, 0)): exact zeros preserved on every factor (13–98 per column).base::svdto 4e-4 (float32).Not covered here (follow-ups)
krylov_constrained_svd) still needs a proper deflation scheme fork > 1; this PR only steersautoaway from it and warns.configureandcleanupare tracked without the executable bit, soR CMD INSTALLfrom a fresh clone fails until they arechmod +x'd (git update-index --chmod=+x configure cleanup).Generated by Claude Code