From a6e1438f50db8c212a3a406d0f256b70078b432f Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 20 Feb 2026 11:43:55 +0100 Subject: [PATCH 01/10] remove parallelize from sepal --- src/squidpy/gr/_sepal.py | 85 +++++++++++++--------------------------- 1 file changed, 27 insertions(+), 58 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 7e60085d1..b94c44ae5 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -1,20 +1,20 @@ from __future__ import annotations -from collections.abc import Callable, Sequence +from collections.abc import Sequence from typing import Literal import numpy as np import pandas as pd from anndata import AnnData -from numba import njit +from numba import njit, prange from scanpy import logging as logg -from scipy.sparse import csr_matrix, isspmatrix_csr, spmatrix +from scipy.sparse import csr_matrix, issparse, isspmatrix_csr, spmatrix from sklearn.metrics import pairwise_distances from spatialdata import SpatialData from squidpy._constants._pkg_constants import Key from squidpy._docs import d, inject_docs -from squidpy._utils import NDArrayA, Signal, SigQueue, _get_n_cores, parallelize +from squidpy._utils import NDArrayA from squidpy.gr._utils import ( _assert_connectivity_key, _assert_non_empty_sequence, @@ -108,8 +108,6 @@ def sepal( genes = genes[adata.var["highly_variable"].values] genes = _assert_non_empty_sequence(genes, name="genes") - n_jobs = _get_n_cores(n_jobs) - g = adata.obsp[connectivity_key] if not isspmatrix_csr(g): g = csr_matrix(g) @@ -124,27 +122,13 @@ def sepal( # get counts vals, genes = _extract_expression(adata, genes=genes, use_raw=use_raw, layer=layer) - start = logg.info(f"Calculating sepal score for `{len(genes)}` genes using `{n_jobs}` core(s)") - - score = parallelize( - _score_helper, - collection=np.arange(len(genes)).tolist(), - extractor=np.hstack, - use_ixs=False, - n_jobs=n_jobs, - backend=backend, - show_progress_bar=show_progress_bar, - )( - vals=vals, - max_neighs=max_neighs, - n_iter=n_iter, - sat=sat, - sat_idx=sat_idx, - unsat=unsat, - unsat_idx=unsat_idx, - dt=dt, - thresh=thresh, - ) + start = logg.info(f"Calculating sepal score for `{len(genes)}` genes") + + vals_dense = vals.toarray() if issparse(vals) else np.asarray(vals) + vals_dense = np.ascontiguousarray(vals_dense, dtype=np.float64) + + use_hex = max_neighs == 6 + score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) key_added = "sepal_score" sepal_score = pd.DataFrame(score, index=genes, columns=[key_added]) @@ -160,10 +144,10 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) -def _score_helper( - ixs: Sequence[int], - vals: spmatrix | NDArrayA, - max_neighs: int, +@njit(parallel=True) +def _diffusion_batch( + vals_dense: NDArrayA, + use_hex: bool, n_iter: int, sat: NDArrayA, sat_idx: NDArrayA, @@ -171,38 +155,20 @@ def _score_helper( unsat_idx: NDArrayA, dt: float, thresh: float, - queue: SigQueue | None = None, ) -> NDArrayA: - if max_neighs == 4: - fun = _laplacian_rect - elif max_neighs == 6: - fun = _laplacian_hex - else: - raise NotImplementedError(f"Laplacian for `{max_neighs}` neighbors is not yet implemented.") - - score = [] - for i in ixs: - if isinstance(vals, spmatrix): - conc = vals[:, i].toarray().flatten() # Safe to call toarray() - else: - conc = vals[:, i].copy() # vals is assumed to be a NumPy array here - - time_iter = _diffusion(conc, fun, n_iter, sat, sat_idx, unsat, unsat_idx, dt=dt, thresh=thresh) - score.append(dt * time_iter) - - if queue is not None: - queue.put(Signal.UPDATE) - - if queue is not None: - queue.put(Signal.FINISH) - - return np.array(score) + n_genes = vals_dense.shape[1] + scores = np.empty(n_genes) + for i in prange(n_genes): + conc = vals_dense[:, i].copy() + time_iter = _diffusion(conc, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) + scores[i] = dt * time_iter + return scores @njit(fastmath=True) def _diffusion( conc: NDArrayA, - laplacian: Callable[[NDArrayA, NDArrayA], float], + use_hex: bool, n_iter: int, sat: NDArrayA, sat_idx: NDArrayA, @@ -220,7 +186,10 @@ def _diffusion( for i in range(n_iter): for j in range(sat_shape): nhood[j] = np.sum(conc[sat_idx[j]]) - d2 = laplacian(conc[sat], nhood) + if use_hex: + d2 = _laplacian_hex(conc[sat], nhood) + else: + d2 = _laplacian_rect(conc[sat], nhood) dcdt = np.zeros(conc_shape) dcdt[sat] = d2 From e011ddcbb6abd29f89b5beebb0ea3646946c6565 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 20 Feb 2026 13:38:39 +0100 Subject: [PATCH 02/10] preallocated buffers --- src/squidpy/gr/_sepal.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index b94c44ae5..cf885ca75 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -6,7 +6,7 @@ import numpy as np import pandas as pd from anndata import AnnData -from numba import njit, prange +from numba import get_num_threads, get_thread_id, njit, prange from scanpy import logging as logg from scipy.sparse import csr_matrix, issparse, isspmatrix_csr, spmatrix from sklearn.metrics import pairwise_distances @@ -157,10 +157,25 @@ def _diffusion_batch( thresh: float, ) -> NDArrayA: n_genes = vals_dense.shape[1] + n_cells = vals_dense.shape[0] + sat_shape = sat.shape[0] + n_threads = get_num_threads() + + # Pre-allocate per-thread workspace to avoid allocator contention + conc_buf = np.empty((n_threads, n_cells)) + entropy_buf = np.empty((n_threads, n_iter)) + nhood_buf = np.empty((n_threads, sat_shape)) + dcdt_buf = np.empty((n_threads, n_cells)) + scores = np.empty(n_genes) for i in prange(n_genes): - conc = vals_dense[:, i].copy() - time_iter = _diffusion(conc, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) + tid = get_thread_id() + conc = conc_buf[tid] + conc[:] = vals_dense[:, i] + time_iter = _diffusion( + conc, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, + entropy_buf[tid], nhood_buf[tid], dcdt_buf[tid], + ) scores[i] = dt * time_iter return scores @@ -174,14 +189,17 @@ def _diffusion( sat_idx: NDArrayA, unsat: NDArrayA, unsat_idx: NDArrayA, - dt: float = 0.001, - thresh: float = 1e-8, + dt: float, + thresh: float, + entropy_arr: NDArrayA, + nhood: NDArrayA, + dcdt: NDArrayA, ) -> float: """Simulate diffusion process on a regular graph.""" - sat_shape, conc_shape = sat.shape[0], conc.shape[0] - entropy_arr = np.zeros(n_iter) + sat_shape = sat.shape[0] + entropy_arr[:] = 0.0 + nhood[:] = 0.0 prev_ent = 1.0 - nhood = np.zeros(sat_shape) for i in range(n_iter): for j in range(sat_shape): @@ -191,7 +209,7 @@ def _diffusion( else: d2 = _laplacian_rect(conc[sat], nhood) - dcdt = np.zeros(conc_shape) + dcdt[:] = 0.0 dcdt[sat] = d2 conc[sat] += dcdt[sat] * dt conc[unsat] += dcdt[unsat_idx] * dt From d9e3438b62231c2812291174714f1ad9ac9ddb7e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 12:56:27 +0000 Subject: [PATCH 03/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_sepal.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index cf885ca75..7343efcea 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -173,8 +173,18 @@ def _diffusion_batch( conc = conc_buf[tid] conc[:] = vals_dense[:, i] time_iter = _diffusion( - conc, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, - entropy_buf[tid], nhood_buf[tid], dcdt_buf[tid], + conc, + use_hex, + n_iter, + sat, + sat_idx, + unsat, + unsat_idx, + dt, + thresh, + entropy_buf[tid], + nhood_buf[tid], + dcdt_buf[tid], ) scores[i] = dt * time_iter return scores From 8c6df254d0b093c8c8456c851e9fee2750b83f5d Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Tue, 24 Feb 2026 15:48:14 +0100 Subject: [PATCH 04/10] add sparse batch support --- src/squidpy/gr/_sepal.py | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 7343efcea..b70751ba4 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -40,9 +40,7 @@ def sepal( layer: str | None = None, use_raw: bool = False, copy: bool = False, - n_jobs: int | None = None, - backend: str = "loky", - show_progress_bar: bool = True, + sparse_batch_size: int = 4096, ) -> pd.DataFrame | None: """ Identify spatially variable genes with *Sepal*. @@ -78,7 +76,11 @@ def sepal( use_raw Whether to access :attr:`anndata.AnnData.raw`. %(copy)s - %(parallelize)s + sparse_batch_size + Number of genes to process in each batch when using sparse inputs. + Used to keep memory bounded while using parallel diffusion. + If your dataset can fit in memory, set this to a large value to use dense inputs for optimal performance. + Default is 4096. Returns ------- @@ -124,11 +126,13 @@ def sepal( vals, genes = _extract_expression(adata, genes=genes, use_raw=use_raw, layer=layer) start = logg.info(f"Calculating sepal score for `{len(genes)}` genes") - vals_dense = vals.toarray() if issparse(vals) else np.asarray(vals) - vals_dense = np.ascontiguousarray(vals_dense, dtype=np.float64) - use_hex = max_neighs == 6 - score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) + + if issparse(vals): + score = _diffusion_batch_sparse(vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size) + else: + vals_dense = np.ascontiguousarray(vals, dtype=np.float64) + score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) key_added = "sepal_score" sepal_score = pd.DataFrame(score, index=genes, columns=[key_added]) @@ -144,6 +148,31 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) + +def _diffusion_batch_sparse( + vals: spmatrix, + use_hex: bool, + n_iter: int, + sat: NDArrayA, + sat_idx: NDArrayA, + unsat: NDArrayA, + unsat_idx: NDArrayA, + dt: float, + thresh: float, + sparse_batch_size: int, +) -> NDArrayA: + """Densify in small batches to keep memory bounded while using parallel diffusion.""" + n_genes = vals.shape[1] + scores = np.empty(n_genes) + for start in range(0, n_genes, sparse_batch_size): + end = min(start + sparse_batch_size, n_genes) + chunk = np.ascontiguousarray(vals[:, start:end].toarray(), dtype=np.float64) + scores[start:end] = _diffusion_batch( + chunk, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, + ) + return scores + + @njit(parallel=True) def _diffusion_batch( vals_dense: NDArrayA, From b0d079259604c3de05c14f760241e5a04583bc62 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:48:55 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_sepal.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index b70751ba4..03adc6303 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -129,7 +129,9 @@ def sepal( use_hex = max_neighs == 6 if issparse(vals): - score = _diffusion_batch_sparse(vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size) + score = _diffusion_batch_sparse( + vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size + ) else: vals_dense = np.ascontiguousarray(vals, dtype=np.float64) score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) @@ -148,7 +150,6 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) - def _diffusion_batch_sparse( vals: spmatrix, use_hex: bool, @@ -168,7 +169,15 @@ def _diffusion_batch_sparse( end = min(start + sparse_batch_size, n_genes) chunk = np.ascontiguousarray(vals[:, start:end].toarray(), dtype=np.float64) scores[start:end] = _diffusion_batch( - chunk, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, + chunk, + use_hex, + n_iter, + sat, + sat_idx, + unsat, + unsat_idx, + dt, + thresh, ) return scores From b9605dd3ecbd137a7735578c8ee3998b9418bfe6 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Tue, 24 Feb 2026 15:53:02 +0100 Subject: [PATCH 06/10] better default --- src/squidpy/gr/_sepal.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 03adc6303..cbd055e77 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -40,7 +40,7 @@ def sepal( layer: str | None = None, use_raw: bool = False, copy: bool = False, - sparse_batch_size: int = 4096, + sparse_batch_size: int = 128, ) -> pd.DataFrame | None: """ Identify spatially variable genes with *Sepal*. @@ -80,7 +80,6 @@ def sepal( Number of genes to process in each batch when using sparse inputs. Used to keep memory bounded while using parallel diffusion. If your dataset can fit in memory, set this to a large value to use dense inputs for optimal performance. - Default is 4096. Returns ------- @@ -129,9 +128,7 @@ def sepal( use_hex = max_neighs == 6 if issparse(vals): - score = _diffusion_batch_sparse( - vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size - ) + score = _diffusion_batch_sparse(vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size) else: vals_dense = np.ascontiguousarray(vals, dtype=np.float64) score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) @@ -150,6 +147,7 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) + def _diffusion_batch_sparse( vals: spmatrix, use_hex: bool, @@ -169,15 +167,7 @@ def _diffusion_batch_sparse( end = min(start + sparse_batch_size, n_genes) chunk = np.ascontiguousarray(vals[:, start:end].toarray(), dtype=np.float64) scores[start:end] = _diffusion_batch( - chunk, - use_hex, - n_iter, - sat, - sat_idx, - unsat, - unsat_idx, - dt, - thresh, + chunk, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, ) return scores From 2df55b25388abd3cc73a948e331bcfee8a3357ed Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:53:17 +0000 Subject: [PATCH 07/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_sepal.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index cbd055e77..2f5a57fe7 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -128,7 +128,9 @@ def sepal( use_hex = max_neighs == 6 if issparse(vals): - score = _diffusion_batch_sparse(vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size) + score = _diffusion_batch_sparse( + vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size + ) else: vals_dense = np.ascontiguousarray(vals, dtype=np.float64) score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) @@ -147,7 +149,6 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) - def _diffusion_batch_sparse( vals: spmatrix, use_hex: bool, @@ -167,7 +168,15 @@ def _diffusion_batch_sparse( end = min(start + sparse_batch_size, n_genes) chunk = np.ascontiguousarray(vals[:, start:end].toarray(), dtype=np.float64) scores[start:end] = _diffusion_batch( - chunk, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, + chunk, + use_hex, + n_iter, + sat, + sat_idx, + unsat, + unsat_idx, + dt, + thresh, ) return scores From e856524a1a61649a0860315dfbb152a4d73172c2 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 27 Feb 2026 22:03:40 +0100 Subject: [PATCH 08/10] lazy densify --- src/squidpy/gr/_sepal.py | 70 +++++++++++++++++++++++++-------------- tests/graph/test_sepal.py | 20 ++++++++--- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 2f5a57fe7..f2c582add 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -3,12 +3,13 @@ from collections.abc import Sequence from typing import Literal +import fast_array_utils import numpy as np import pandas as pd from anndata import AnnData from numba import get_num_threads, get_thread_id, njit, prange from scanpy import logging as logg -from scipy.sparse import csr_matrix, issparse, isspmatrix_csr, spmatrix +from scipy.sparse import csc_matrix, csr_matrix, issparse, isspmatrix_csr, spmatrix from sklearn.metrics import pairwise_distances from spatialdata import SpatialData @@ -40,7 +41,6 @@ def sepal( layer: str | None = None, use_raw: bool = False, copy: bool = False, - sparse_batch_size: int = 128, ) -> pd.DataFrame | None: """ Identify spatially variable genes with *Sepal*. @@ -76,10 +76,6 @@ def sepal( use_raw Whether to access :attr:`anndata.AnnData.raw`. %(copy)s - sparse_batch_size - Number of genes to process in each batch when using sparse inputs. - Used to keep memory bounded while using parallel diffusion. - If your dataset can fit in memory, set this to a large value to use dense inputs for optimal performance. Returns ------- @@ -128,12 +124,21 @@ def sepal( use_hex = max_neighs == 6 if issparse(vals): - score = _diffusion_batch_sparse( - vals, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh, sparse_batch_size + vals = csc_matrix(vals) + score = _diffusion_batch_csc( + vals, + use_hex, + n_iter, + sat, + sat_idx, + unsat, + unsat_idx, + dt, + thresh, ) else: vals_dense = np.ascontiguousarray(vals, dtype=np.float64) - score = _diffusion_batch(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) + score = _diffusion_batch_dense(vals_dense, use_hex, n_iter, sat, sat_idx, unsat, unsat_idx, dt, thresh) key_added = "sepal_score" sepal_score = pd.DataFrame(score, index=genes, columns=[key_added]) @@ -149,8 +154,9 @@ def sepal( _save_data(adata, attr="uns", key=key_added, data=sepal_score, time=start) -def _diffusion_batch_sparse( - vals: spmatrix, +@njit(parallel=True) +def _diffusion_batch_csc( + vals: csc_matrix, use_hex: bool, n_iter: int, sat: NDArrayA, @@ -159,16 +165,28 @@ def _diffusion_batch_sparse( unsat_idx: NDArrayA, dt: float, thresh: float, - sparse_batch_size: int, ) -> NDArrayA: - """Densify in small batches to keep memory bounded while using parallel diffusion.""" - n_genes = vals.shape[1] + indptr = vals.indptr + indices = vals.indices + data = vals.data + n_cells, n_genes = vals.shape + sat_shape = sat.shape[0] + n_threads = get_num_threads() + + conc_buf = np.empty((n_threads, n_cells)) + entropy_buf = np.empty((n_threads, n_iter)) + nhood_buf = np.empty((n_threads, sat_shape)) + dcdt_buf = np.empty((n_threads, n_cells)) + scores = np.empty(n_genes) - for start in range(0, n_genes, sparse_batch_size): - end = min(start + sparse_batch_size, n_genes) - chunk = np.ascontiguousarray(vals[:, start:end].toarray(), dtype=np.float64) - scores[start:end] = _diffusion_batch( - chunk, + for i in prange(n_genes): + tid = get_thread_id() + conc = conc_buf[tid] + conc[:] = 0.0 + for j in range(indptr[i], indptr[i + 1]): + conc[indices[j]] = data[j] + time_iter = _diffusion( + conc, use_hex, n_iter, sat, @@ -177,13 +195,17 @@ def _diffusion_batch_sparse( unsat_idx, dt, thresh, + entropy_buf[tid], + nhood_buf[tid], + dcdt_buf[tid], ) + scores[i] = dt * time_iter return scores @njit(parallel=True) -def _diffusion_batch( - vals_dense: NDArrayA, +def _diffusion_batch_dense( + vals: NDArrayA, use_hex: bool, n_iter: int, sat: NDArrayA, @@ -193,8 +215,8 @@ def _diffusion_batch( dt: float, thresh: float, ) -> NDArrayA: - n_genes = vals_dense.shape[1] - n_cells = vals_dense.shape[0] + n_genes = vals.shape[1] + n_cells = vals.shape[0] sat_shape = sat.shape[0] n_threads = get_num_threads() @@ -208,7 +230,7 @@ def _diffusion_batch( for i in prange(n_genes): tid = get_thread_id() conc = conc_buf[tid] - conc[:] = vals_dense[:, i] + conc[:] = vals[:, i] time_iter = _diffusion( conc, use_hex, diff --git a/tests/graph/test_sepal.py b/tests/graph/test_sepal.py index 8fb711f5e..98c7ba0ce 100644 --- a/tests/graph/test_sepal.py +++ b/tests/graph/test_sepal.py @@ -5,6 +5,7 @@ from pandas.testing import assert_frame_equal from squidpy.gr import sepal, spatial_neighbors +import numba UNS_KEY = "sepal_score" @@ -16,8 +17,14 @@ def test_sepal_seq_par(adata: AnnData): adata.var["highly_variable"] = rng.choice([True, False], size=adata.var_names.shape, p=[0.005, 0.995]) sepal(adata, max_neighs=6) - df = sepal(adata, max_neighs=6, copy=True, n_jobs=1) - df_parallel = sepal(adata, max_neighs=6, copy=True, n_jobs=2) + + prev_threads = numba.get_num_threads() + try: + numba.set_num_threads(1) + df = sepal(adata, max_neighs=6, copy=True) + finally: + numba.set_num_threads(prev_threads) + df_parallel = sepal(adata, max_neighs=6, copy=True) idx_df = df.index.values idx_adata = adata[:, adata.var.highly_variable.values].var_names.values @@ -40,8 +47,13 @@ def test_sepal_square_seq_par(adata_squaregrid: AnnData): rng = np.random.default_rng(42) adata.var["highly_variable"] = rng.choice([True, False], size=adata.var_names.shape) - sepal(adata, max_neighs=4) - df_parallel = sepal(adata, copy=True, n_jobs=2, max_neighs=4) + prev_threads = numba.get_num_threads() + try: + numba.set_num_threads(1) + sepal(adata, max_neighs=4) + finally: + numba.set_num_threads(prev_threads) + df_parallel = sepal(adata, copy=True, max_neighs=4) idx_df = df_parallel.index.values idx_adata = adata[:, adata.var.highly_variable.values].var_names.values From d81b098d938bf2cd36d3d8d1ff8de9603ca2cf84 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 21:03:34 +0000 Subject: [PATCH 09/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/graph/test_sepal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/graph/test_sepal.py b/tests/graph/test_sepal.py index 98c7ba0ce..a54f8d7ac 100644 --- a/tests/graph/test_sepal.py +++ b/tests/graph/test_sepal.py @@ -1,11 +1,11 @@ from __future__ import annotations +import numba import numpy as np from anndata import AnnData from pandas.testing import assert_frame_equal from squidpy.gr import sepal, spatial_neighbors -import numba UNS_KEY = "sepal_score" From 542df635a9dac18f00da2c8fa5a868df980f92f8 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 27 Feb 2026 22:12:31 +0100 Subject: [PATCH 10/10] ignore comment for fau --- src/squidpy/gr/_sepal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index f2c582add..1197a6a6a 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -3,7 +3,7 @@ from collections.abc import Sequence from typing import Literal -import fast_array_utils +import fast_array_utils # noqa: F401 import numpy as np import pandas as pd from anndata import AnnData