API reference

class vsparse.VCSCArray(shape, major_ptr, values, value_ptr, indices)[source]

Bases: _VCSBase

Value-Compressed Sparse Column array. Values are deduplicated per column.

Parameters:
  • shape (tuple[int, int])

  • major_ptr (np.ndarray)

  • values (np.ndarray)

  • value_ptr (np.ndarray)

  • indices (np.ndarray)

property T: _VCSBase

shares buffers and swaps the VCSC/VCSR dual class.

Type:

Transpose. Free

astype(dtype, copy=True)

Cast the stored values to dtype. Structural zeros stay zero implicitly.

Return type:

_VCSBase

Parameters:
copy()
count_nonzero()

Count of stored elements that are actually nonzero (unlike nnz/getnnz).

Return type:

int

property dtype: dtype
classmethod from_scipy(mat)

Build from any scipy sparse array/matrix (converted internally).

Return type:

_VCSBase

getnnz(axis=None)

Count of stored elements along axis, or overall if None.

Return type:

ndarray | int

Parameters:

axis (int | None)

indices
log1p()

Elementwise log1p. Structural zeros stay zero implicitly.

Return type:

_VCSBase

major_ptr
max(axis=None)

Maximum value (including implicit zeros) along axis, or overall if None.

Return type:

ndarray | Any

Parameters:

axis (int | None)

mean(axis=None)

Mean of (structural + implicit-zero) values along axis, or overall if None.

Return type:

ndarray | float

Parameters:

axis (int | None)

min(axis=None)

Minimum value (including implicit zeros) along axis, or overall if None.

Return type:

ndarray | Any

Parameters:

axis (int | None)

multiply(other)

Elementwise multiplication (matches scipy’s sparse-array .multiply).

Return type:

_VCSBase | ndarray

property n_major: int
property n_minor: int
property n_unique: int

Number of stored (major-slice, unique-value) entries.

property nnz: int
normalized(view='parafac2', *, recalculate=True)

A normalized view of this array – see vsparse._vcs_norm/vsparse._norm_common.

Parameters:
  • view (str | Recipe) – Which normalization recipe to apply: the name of one of vsparse._norm_common.RECIPES ("raw", "cp10k_log1p", "parafac2" (the default), "scanpy", "pearson"), or a Recipe built by the caller.

  • recalculate (bool) – If True (the default), (re)compute the recipe’s statistics fresh from this array. If False, reuse a previously computed view for view on this same array, if one exists (from an earlier .normalized(view, ...) call, with any value of recalculate) – this is how switching between recipes avoids recomputing each one every time. If no such view has been computed yet, one is still computed (there is nothing to reuse). The cache holds at most NORM_CACHE_MAXSIZE recipes, and holds each view weakly – see _NormCache.

Return type:

Any

shape
property stats: VCSStats

Per-row/per-column totals and nonzero counts – see vsparse._stats.

Computed on first access (O(nnz)) and kept, or restored from the sidecar of a file written by write_h5ad()/ write_zarr(). Normalization reads its per-cell/per-gene scales (a/b) from here.

sum(axis=None)

Sum of (structural) values along axis (0=rows, 1=columns), or overall if None.

Return type:

ndarray | float

Parameters:

axis (int | None)

to_csc()
Return type:

csc_array

to_csr()
Return type:

csr_array

to_scipy()

Decompress to the equivalent scipy csc_array/csr_array.

toarray()
Return type:

ndarray

transpose()
Return type:

_VCSBase

value_ptr
values
class vsparse.VCSRArray(shape, major_ptr, values, value_ptr, indices)[source]

Bases: _VCSBase

Value-Compressed Sparse Row array. Values are deduplicated per row.

Parameters:
  • shape (tuple[int, int])

  • major_ptr (np.ndarray)

  • values (np.ndarray)

  • value_ptr (np.ndarray)

  • indices (np.ndarray)

property T: _VCSBase

shares buffers and swaps the VCSC/VCSR dual class.

Type:

Transpose. Free

astype(dtype, copy=True)

Cast the stored values to dtype. Structural zeros stay zero implicitly.

Return type:

_VCSBase

Parameters:
copy()
count_nonzero()

Count of stored elements that are actually nonzero (unlike nnz/getnnz).

Return type:

int

property dtype: dtype
classmethod from_scipy(mat)

Build from any scipy sparse array/matrix (converted internally).

Return type:

_VCSBase

getnnz(axis=None)

Count of stored elements along axis, or overall if None.

Return type:

ndarray | int

Parameters:

axis (int | None)

indices
log1p()

Elementwise log1p. Structural zeros stay zero implicitly.

Return type:

_VCSBase

major_ptr
max(axis=None)

Maximum value (including implicit zeros) along axis, or overall if None.

Return type:

ndarray | Any

Parameters:

axis (int | None)

mean(axis=None)

Mean of (structural + implicit-zero) values along axis, or overall if None.

Return type:

ndarray | float

Parameters:

axis (int | None)

min(axis=None)

Minimum value (including implicit zeros) along axis, or overall if None.

Return type:

ndarray | Any

Parameters:

axis (int | None)

multiply(other)

Elementwise multiplication (matches scipy’s sparse-array .multiply).

Return type:

_VCSBase | ndarray

property n_major: int
property n_minor: int
property n_unique: int

Number of stored (major-slice, unique-value) entries.

property nnz: int
normalized(view='parafac2', *, recalculate=True)

A normalized view of this array – see vsparse._vcs_norm/vsparse._norm_common.

Parameters:
  • view (str | Recipe) – Which normalization recipe to apply: the name of one of vsparse._norm_common.RECIPES ("raw", "cp10k_log1p", "parafac2" (the default), "scanpy", "pearson"), or a Recipe built by the caller.

  • recalculate (bool) – If True (the default), (re)compute the recipe’s statistics fresh from this array. If False, reuse a previously computed view for view on this same array, if one exists (from an earlier .normalized(view, ...) call, with any value of recalculate) – this is how switching between recipes avoids recomputing each one every time. If no such view has been computed yet, one is still computed (there is nothing to reuse). The cache holds at most NORM_CACHE_MAXSIZE recipes, and holds each view weakly – see _NormCache.

Return type:

Any

shape
property stats: VCSStats

Per-row/per-column totals and nonzero counts – see vsparse._stats.

Computed on first access (O(nnz)) and kept, or restored from the sidecar of a file written by write_h5ad()/ write_zarr(). Normalization reads its per-cell/per-gene scales (a/b) from here.

sum(axis=None)

Sum of (structural) values along axis (0=rows, 1=columns), or overall if None.

Return type:

ndarray | float

Parameters:

axis (int | None)

to_csc()
Return type:

csc_array

to_csr()
Return type:

csr_array

to_scipy()

Decompress to the equivalent scipy csc_array/csr_array.

toarray()
Return type:

ndarray

transpose()
Return type:

_VCSBase

value_ptr
values
class vsparse.VCSStats(row_sums, row_nnz, col_sums, col_nnz, col_depth_sums)[source]

Per-row/per-column statistics of one VCS array – see vsparse._stats.

The arrays are read-only, since a VCSStats is shared between an array and its copies.

Parameters:
row_sums: ndarray

Per-row sum of stored values (scanpy’s obs["total_counts"]).

row_nnz: ndarray

Per-row count of stored values that are nonzero (obs["n_genes_by_counts"]).

col_sums: ndarray

Per-column sum of stored values (var["total_counts"]).

col_nnz: ndarray

Per-column count of stored values that are nonzero (var["n_cells_by_counts"]).

col_depth_sums: ndarray

Per-column sum of depth-normalized values, sum_i(x[i, j] / row_sums[i]) (rows summing to zero divide by 1 instead).

vsparse.from_anndata(adata, layer=None, use_raw=False, format='csc')[source]

Convert adata.X (or a layer / raw.X) into a VCSC/VCSR array.

Parameters:
  • adata (AnnData) – Source anndata.AnnData object.

  • layer (str | None) – If given, read adata.layers[layer] instead of adata.X.

  • use_raw (bool) – If True, read adata.raw.X instead of adata.X.

  • format (str) – Either "csc" (default) or "csr", selecting the returned type.

Return type:

_VCSBase

Returns:

  • A VCSCArray or VCSRArray. The source

  • matrix may already be CSC, CSR, or dense; it is converted as needed.

vsparse.to_layer(adata, arr, key)[source]

Decompress arr and store it as adata.layers[key].

AnnData does not natively understand the VCSC/VCSR layout, so this stores the equivalent scipy sparse array.

Return type:

None

Parameters:
class vsparse.CudaNormalizedView(view)[source]

Bases: object

A NormalizedViewBase resident on a CUDA device.

Built by to_gpu(), not directly. Holds the wrapped array’s value-compressed structure and the recipe’s statistics as CuPy arrays, and computes self @ B / B @ self with the kernels in this module. All floating-point state is DEVICE_DTYPE (float32).

The object pins its device memory for as long as it is alive; drop the reference (and, if you need the memory back immediately, cupy.get_default_memory_pool().free_all_blocks()) to release it.

Parameters:

view (NormalizedViewBase)

shape
recipe
major_ptr
value_ptr
indices
values
row_scale
gene_scale
col_mean
col_post_scale
property dtype: dtype
property nnz: int
property nbytes: int

Device bytes held, structure plus statistics.

property means: Any

Per-gene mean-correction vector, col_post_scale * col_mean.

to_cupy_sparse(format=None, *, sort_indices=True)[source]

The uncentered Delta term as a CuPy CSR/CSC matrix, one float per nonzero.

The device-side counterpart of to_scipy_sparse(), and the baseline the kernels are benchmarked against: it hands the product to cuSPARSE, at the cost of expanding every value-compressed run and so giving up the layout’s whole memory advantage. means is left to subtract externally, exactly as on the host.

Parameters:
  • format (str | None) – Sparse format to build. Defaults to whichever one matches the wrapped array’s own major axis, which is the one that costs nothing to assemble. Worth overriding: cuSPARSE is markedly faster with a CSR operand in both directions – measurably so for dense @ sparse, where CSC ran ~5x slower in benchmarks/cases.py – so a caller multiplying repeatedly should pay the one-time conversion and pass "csr".

  • sort_indices (bool) – Whether to sort each slice’s indices ascending before returning. A VCS slice stores its indices grouped by the value they share, never in index order, so the expanded matrix is not canonical unless this sorts it – and cuSPARSE measured ~6x slower on the unsorted form. That penalty is why this defaults to True where the host’s to_scipy_sparse(), whose result usually goes somewhere less picky, returns the unsorted order. Pass False to skip the sort when the result is used once, or by something that does not care.

Return type:

Any

vsparse.cuda_is_available()[source]

Whether CuPy is importable and a CUDA device is actually present.

Return type:

bool

class vsparse.VCSCAnnData(X=None, *, raw_X=None, **kwargs)[source]

Bases: AnnData

An AnnData whose X is a VCSCArray/VCSRArray.

Standard AnnData validates every array assigned to X/layers/etc. against a fixed allowlist of types (dense/sparse/ dask), so a plain AnnData cannot hold a VCSCArray directly. This subclass overrides the X property to store one without going through that validation. A “raw” VCSC/VCSR matrix, if any, is kept as .raw_X – a plain attribute, not wired into anndata’s own .raw/Raw machinery, which has the same restriction.

Because of this, most operations that need anndata’s normal per-element type dispatch on X – concatenation, most of scanpy/anndata’s ecosystem – are not supported while X is VCSC/VCSR-backed. Call to_anndata() first to get a fully-featured, ordinary AnnData. Indexing (adata[obs_idx, var_idx]) is supported (see __getitem__()), but always as an eager copy, not a lazy view – anndata’s view machinery bypasses the X/raw_X overrides here.

Persist with write_h5ad()/write_zarr() and read_h5ad()/read_zarr() (not the top-level anndata.read_h5ad/read_zarr, which always reconstruct a plain AnnData and would fail validating a VCSC-typed X).

Parameters:
  • X (_AnyVCS | None)

  • raw_X (_AnyVCS | None)

  • kwargs (Any)

property X: _VCSBase | None

Data matrix of shape n_obs × n_vars.

property raw_X: _VCSBase | None

The raw/X matrix, as a VCSCArray/VCSRArray (see class docstring).

copy()[source]

A deep copy, preserving the VCSC/VCSR-backed X/raw_X.

The inherited anndata.AnnData.copy() only knows how to copy the standard private _X attribute, which this class never sets (see the class docstring: X/raw_X live in _vcs_X/_vcs_raw_X instead) – so it silently drops them (X comes back None) and returns a plain AnnData rather than this class. This copies every field explicitly instead, including a real VCSCArray/ VCSRArray copy of X/raw_X, and returns type(self) so a subclass (e.g. one overriding the X property) round-trips too.

Return type:

VCSCAnnData

to_memory(*, copy=False)[source]

Return this object with its data loaded into memory.

The inherited anndata.AnnData.to_memory() has the same problem as the inherited copy() (see above): it iterates the object’s standard attributes, which never includes this class’s X/ raw_X (held in _vcs_X/_vcs_raw_X instead), and reconstructs a plain AnnData – so X silently comes back None.

This class never actually supports a lazily backed X/raw_X (they’re always eagerly-held VCSCArray/VCSRArray instances), so there is never anything to load – this always returns a full copy() instead, regardless of copy (unlike plain AnnData, where copy=False can skip copying arrays already in memory; here everything already is, so the distinction doesn’t apply).

Return type:

VCSCAnnData

Parameters:

copy (bool)

normalized(view='parafac2', *, recalculate=True)[source]

A normalized view of X – see vsparse._base._VCSBase.normalized().

Also records the recipe’s statistics – per-cell a in obs["vsparse_a"], per-gene b/c/s in varm["vsparse_b"]/["vsparse_c"]/["vsparse_s"], and the active recipe name plus a stale flag in uns["vsparse"] – so they persist across write_h5ad()/write_zarr() and can be reused instead of recomputed.

recalculate=False reuses whatever is already recorded for view: first an in-memory view already built this session (however it was built), else what’s stored in obs/varm/uns (built fresh, but skipping the O(nnz) statistics passes) if it matches view and the current shape. uns["vsparse"]["stale"] is set to True after indexing (see __getitem__()), since population statistics like a depth median or a per-gene mean no longer reflect the subset – recalculate=False reuses them anyway; the default recalculate=True always recomputes fresh (and clears stale).

Return type:

Any

Parameters:
  • view (str | Recipe)

  • recalculate (bool)

classmethod from_anndata(adata, format='csc', raw_format=None, *, include_raw=True)[source]

Build from a regular AnnData, compressing X (and raw.X).

Return type:

VCSCAnnData

Parameters:
to_anndata()[source]

Decompress to a regular, fully-featured AnnData.

Return type:

AnnData

write_h5ad(filename, *, convert_strings_to_categoricals=True, dataset_kwargs=None, **_kwargs)[source]

Write to .h5ad. Read back with read_h5ad().

X/raw_X are always stored as IVCSC (from VCSC) or IVCSR (from VCSR): the minor-axis indices are byte-packed (delta + varint encoding), at the cost of extra work on write/read. They come back from read_h5ad() as ordinary VCSCArray/VCSRArray objects – IVCSC/IVCSR is purely an on-disk storage format.

Parameters:
  • convert_strings_to_categoricals (bool) – Convert obs/var string columns to categorical in place before writing, as anndata’s own writers do. Only columns with fewer categories than rows are converted.

  • dataset_kwargs (Mapping[str, Any] | None) – Passed to h5py.Group.create_dataset for every array written. Defaults to zstd (level 1) compression, which read_h5ad() decodes in parallel; pass {} to store uncompressed. Either way, compression is only ever applied to numeric arrays – see vsparse._compression.numeric_only_compression().

  • filename (str | PathLike[str])

  • _kwargs (Any)

Return type:

None

classmethod read_h5ad(filename)[source]

Read a file written by write_h5ad().

Reads files compressed with the current zstd default and with the Blosc2 default of earlier versions alike.

Return type:

VCSCAnnData

Parameters:

filename (str | PathLike[str])

write_zarr(store, *, convert_strings_to_categoricals=True, dataset_kwargs=None, **_kwargs)[source]

Write to a zarr store. Read back with read_zarr().

See write_h5ad() for the IVCSC/IVCSR storage format and convert_strings_to_categoricals/dataset_kwargs (including the numeric-only compression behavior); the default compression here is zstd (level 1) via numcodecs.

Return type:

None

Parameters:
classmethod read_zarr(store)[source]

Read a store written by write_zarr().

Return type:

VCSCAnnData

Parameters:

store (Any)