From 3d5035ab8b44ed56d9affb7e00ae918b5ab855e4 Mon Sep 17 00:00:00 2001 From: Intron7 Date: Mon, 6 Jul 2026 12:59:38 +0200 Subject: [PATCH 1/3] fix dense int input Signed-off-by: Intron7 --- docs/release-notes/0.16.0.md | 1 + src/rapids_singlecell/preprocessing/_normalize.py | 4 +++- src/rapids_singlecell/preprocessing/_scale.py | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/0.16.0.md b/docs/release-notes/0.16.0.md index 9bff7f8d..5de83dff 100644 --- a/docs/release-notes/0.16.0.md +++ b/docs/release-notes/0.16.0.md @@ -15,6 +15,7 @@ ``` * {func}`~rapids_singlecell.pp.neighbors` with ``method="jaccard"`` no longer crashes with ``CUDA_ERROR_ILLEGAL_ADDRESS`` on large datasets where ``n_obs * (n_neighbors - 1)**2`` exceeds ``2**31`` {pr}`700` {smaller}`S Dicks` * {func}`~rapids_singlecell.pp.pca` no longer imports a removed cuML internal, fixing ``ImportError`` on recent cuML builds {pr}`708` {smaller}`S Dicks` +* Promote dense integer inputs in {func}`~rapids_singlecell.pp.normalize_total` and {func}`~rapids_singlecell.pp.scale` to floating point {pr}`718` {smaller}`S Dicks` ```{rubric} Misc ``` diff --git a/src/rapids_singlecell/preprocessing/_normalize.py b/src/rapids_singlecell/preprocessing/_normalize.py index e89a564e..de88b17f 100644 --- a/src/rapids_singlecell/preprocessing/_normalize.py +++ b/src/rapids_singlecell/preprocessing/_normalize.py @@ -80,7 +80,9 @@ def normalize_total( _check_gpu_X(X, allow_dask=True) - if not inplace: + if X.dtype.kind in "iu": + X = X.astype(cp.float32) + elif not inplace: X = X.copy() if sparse.isspmatrix_csc(X): diff --git a/src/rapids_singlecell/preprocessing/_scale.py b/src/rapids_singlecell/preprocessing/_scale.py index 38551fed..464d1b68 100644 --- a/src/rapids_singlecell/preprocessing/_scale.py +++ b/src/rapids_singlecell/preprocessing/_scale.py @@ -136,6 +136,8 @@ def scale( def _scale_dispatch(X, *, mask_obs, zero_center, inplace, max_value): + if X.dtype.kind in "iu": + X = X.astype(cp.float64) if isinstance(X, DaskArray): return _scale_dask( X, From 383b2395766a3747a8f04d0f145db90b46a28403 Mon Sep 17 00:00:00 2001 From: Intron7 Date: Mon, 6 Jul 2026 13:03:54 +0200 Subject: [PATCH 2/3] add tests Signed-off-by: Intron7 --- tests/test_normalization.py | 12 ++++++++++++ tests/test_scaling.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/test_normalization.py b/tests/test_normalization.py index 75932358..34e1ee84 100644 --- a/tests/test_normalization.py +++ b/tests/test_normalization.py @@ -27,6 +27,18 @@ def test_normalize_total(dtype, sparse): ) +@pytest.mark.parametrize("dtype", [np.int32, np.int64]) +def test_normalize_total_promotes_dense_integers(dtype): + cudata = AnnData(cp.array([[1, 1], [2, 4]], dtype=dtype)) + + rsc.pp.normalize_total(cudata, target_sum=10) + + assert cudata.X.dtype == cp.float32 + cp.testing.assert_allclose( + cudata.X.sum(axis=1), cp.full(cudata.n_obs, 10, dtype=cp.float32) + ) + + @pytest.mark.parametrize("dtype", [np.float32, np.float64]) def test_normalize_total_layers(dtype): cudata = AnnData(csr_matrix(X_total, dtype=dtype)) diff --git a/tests/test_scaling.py b/tests/test_scaling.py index 9abe4306..e9d6b2a3 100644 --- a/tests/test_scaling.py +++ b/tests/test_scaling.py @@ -113,6 +113,16 @@ def test_scale_simple(dtype): ) +@pytest.mark.parametrize("dtype", [np.int32, np.int64]) +def test_scale_promotes_dense_integers(dtype): + adata = AnnData(cp.array(X_original, dtype=dtype)) + + rsc.pp.scale(adata) + + assert adata.X.dtype == cp.float64 + cp.testing.assert_allclose(adata.X, X_centered_original) + + @pytest.mark.parametrize( "typ", [np.array, csr_matrix, csc_matrix], ids=lambda x: x.__name__ ) From b931f0df9a71c5776a69e6dcabaf11c254e5871c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:19:55 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_scaling.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_scaling.py b/tests/test_scaling.py index a7f4c568..305c32ea 100644 --- a/tests/test_scaling.py +++ b/tests/test_scaling.py @@ -122,6 +122,7 @@ def test_scale_promotes_dense_integers(dtype): assert adata.X.dtype == cp.float64 cp.testing.assert_allclose(adata.X, X_centered_original) + def test_scale_obsm_does_not_write_var_statistics(): adata = AnnData(cp.ones((3, 4), dtype=cp.float32)) adata.obsm["X_embedding"] = cp.array([[1, 2], [2, 4], [3, 6]], dtype=cp.float32)