Skip to content

Say plainly that to_scipy_sparse() omits centering, and pin the identity - #57

Draft
fishidaho wants to merge 1 commit into
mainfrom
docs/to-scipy-sparse-contract
Draft

fishidaho wants to merge 1 commit into
mainfrom
docs/to-scipy-sparse-contract

Conversation

@fishidaho

Copy link
Copy Markdown
Contributor

Problem

to_scipy_sparse() (#47) returns the uncentered Delta term and leaves means for the caller to subtract. For the two recipes that do not center that is the normalized matrix; for the three that do (parafac2, scanpy, pearson) it is not.

The docstring mentioned means only in passing, after describing the return value in terms that read as complete. The documented use is handing arrays to parafac2's CuPy/MLX backends, which is exactly where a silently uncentered matrix is hard to trace back.

Fix

Lead the docstring with what it omits, name the recipes it bites, and give the identity for recovering the whole matrix.

Pin both halves in tests: to_scipy_sparse().toarray() - means == toarray() for every recipe, and the fact that dropping means really does change the answer for the centering ones — so the warning cannot quietly stop being true.

No API change.

Measurements

On a 40x6 integer matrix, to_scipy_sparse() differs from the true normalized matrix by more than 0.1 for every centering recipe, and is exact for the other two.

Merge order

Any time, independent of the others.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rvu3cf8ZL7F5EPX22eo6Je

@fishidaho
fishidaho marked this pull request as draft September 18, 2026 03:36
`to_scipy_sparse()` (#47) returns the uncentered `Delta` term and leaves
`means` for the caller to subtract. For the two recipes that do not center
that is the normalized matrix; for the three that do (`parafac2`, `scanpy`,
`pearson`) it is not, and the old docstring mentioned `means` only in
passing, after describing the return value in terms that read as complete.
Measured on a 40x6 integer matrix, what comes back differs from the real
normalized matrix by more than 0.1 for every centering recipe.

Lead with that instead, and pin both halves in tests: the identity
`to_scipy_sparse().toarray() - means == toarray()` for every recipe, and the
fact that dropping `means` really does change the answer for the centering
ones -- so the warning cannot quietly stop being true.

No API change. An earlier draft added `is_sparse` plus guarded
`to_scipy()`/`to_csr()`/`to_csc()`, but `is_sparse` only restated the
already-public `recipe.center`, the format helpers only restated scipy's own
`.tocsr()`/`.tocsc()`, and a second materializer differing from the first
only in whether it raises is more API to understand rather than less. The
sparse decomposition itself landed in #47 and #51: `to_scipy_sparse()` is the
`sparse_delta()` that work proposed, and `means` is its `baseline` negated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fishidaho
fishidaho force-pushed the docs/to-scipy-sparse-contract branch from 3de005c to 7c21a4e Compare September 18, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant