Skip to content

Reconstruction

Counts to posteriors, posteriors to likelihood parameters, and the search. bonsai_from_counts is the first three in one call.

core

Reconstruction: Sanity, the S5 conversion, Bonsai and backbone mode.

bonsai_from_counts

bonsai_from_counts(
    counts: Any,
    *,
    cell_totals: ndarray | None = None,
    variance_rule: VarianceRule = "marginalise",
    fixed_variance: float | None = None,
    dtype: type[float32] | type[float64] = float32,
    gpu: bool = False,
    start: Start = "linkage",
    search: Search = "approximate",
    min_signal_to_noise: float | None = None,
    max_amplification: float | None = None,
    reroot: bool = True,
) -> BonsaiResult

Raw counts to tree: sanity, from_sanity, then bonsai.

The whole chain runs in Rust, so the dense posteriors never cross into Python.

Parameters:

Name Type Description Default
counts Any

As sanity.

required
cell_totals ndarray | None

As sanity.

None
variance_rule VarianceRule

As sanity.

'marginalise'
fixed_variance float | None

As sanity.

None
dtype type[float32] | type[float64]

As sanity.

float32
gpu bool

As sanity. Only Sanity runs on the GPU; the tree search is CPU either way.

False
start Start

As bonsai.

'linkage'
search Search

As bonsai.

'approximate'
min_signal_to_noise float | None

As bonsai.

None
max_amplification float | None

As from_sanity.

None
reroot bool

As bonsai.

True

Returns:

Type Description
BonsaiResult

As bonsai, with features and dropped indexing the genes of

BonsaiResult

counts.

sanity

sanity(
    counts: Any,
    *,
    cell_totals: ndarray | None = None,
    variance_rule: VarianceRule = "marginalise",
    fixed_variance: float | None = None,
    dtype: type[float32] | type[float64] = float32,
    gpu: bool = False,
) -> SanityResult

Posterior log expression and error bars from raw UMI counts.

Runs Sanity (Breda et al., Nat. Biotechnol. 2021) via sanity-sc-rs.

Parameters:

Name Type Description Default
counts Any

(n_cells, n_genes) raw UMI counts, a numpy integer array or any scipy sparse matrix. Log-normalised input is rejected.

required
cell_totals ndarray | None

Total UMIs of each cell over all genes. Defaults to the row sums of counts, which is only right if counts holds every gene. Subset genes before calling and you must pass this.

None
variance_rule VarianceRule

How the per-gene variance enters the estimates. "marginalise" integrates over its posterior and is the one to quote; the others are faster approximations.

'marginalise'
fixed_variance float | None

The variance used for every gene when variance_rule="fixed".

None
dtype type[float32] | type[float64]

Storage type of the output. Reductions are float64 either way.

float32
gpu bool

Run Sanity on the GPU through wgpu. The device path is float32 whatever dtype says, since wgpu has no float64. Check gpu_available first; asking for it where that is False raises rather than falling back to the CPU.

False

Returns:

Type Description
SanityResult

The posteriors, cells x genes.

Raises:

Type Description
ValueError

On malformed or non-integer counts.

BonsaiError

If gpu=True and no GPU can be reached.

from_sanity

from_sanity(
    posterior_means: ndarray,
    posterior_sds: ndarray,
    variances: ndarray,
    *,
    max_amplification: float | None = None,
) -> Likelihood

Recover likelihood means and SDs from Sanity posteriors (SI eq. S5).

Sanity shrinks each cell towards the gene mean under a N(0, v) prior. Bonsai wants the measurement before that shrinkage, so this undoes it: mu = x * v / (v - eps^2) and sig^2 = eps^2 * v / (v - eps^2).

Pass the zero-centred log fold changes, not the log transcription quotients. With the gene mean added, the per-cell amplification scales it differently in every cell and invents structure. On simulated counts that took Robinson-Foulds from 0 to 100 of a possible 122.

Parameters:

Name Type Description Default
posterior_means ndarray

Log fold changes, (n_cells, n_genes).

required
posterior_sds ndarray

Their error bars, same shape.

required
variances ndarray

Sanity's per-gene variance v.

required
max_amplification float | None

Largest v / (v - eps^2) accepted. A gene with any cell above it is dropped, not clamped. None for the default of 1000.

None

Returns:

Type Description
Likelihood

Means, SDs and variances for the kept genes, plus which genes were kept

Likelihood

and dropped.

Raises:

Type Description
BonsaiError

If every gene is ill-conditioned.

bonsai

bonsai(
    means: ndarray,
    sds: ndarray,
    *,
    variances: ndarray | None = None,
    start: Start = "linkage",
    search: Search = "approximate",
    min_signal_to_noise: float | None = None,
    reroot: bool = True,
) -> BonsaiResult

Reconstruct a tree from per-cell means and error bars.

Storage follows the input: float32 in, float32 all the way down. Same input gives the same tree whatever the thread count.

Parameters:

Name Type Description Default
means ndarray

(n_cells, n_features).

required
sds ndarray

Standard deviations on means, same shape, strictly positive.

required
variances ndarray | None

Per-feature variance. None estimates it from the data (SI eq. S9). From Sanity, pass Likelihood.variances.

None
start Start

"linkage" (Ward over a neighbour graph, the default) or "greedy" (the paper's greedy merge, for like-for-like reproduction).

'linkage'
search Search

"approximate" (the default) or "exact". Exact runs SPR and NNI as the paper specifies them; the approximate search revisits only what the last moves touched and landed within a few nats of the exact one on every dataset measured, several times faster. Worth an exact run to check on data of your own.

'approximate'
min_signal_to_noise float | None

Features below this signal-to-noise are dropped before the search. None for the default of 1, the paper's.

None
reroot bool

Reroot for display once the search is done. Changes the picture, not the likelihood.

True

Returns:

Type Description
BonsaiResult

The tree, its loglikelihood and the posterior over every node.

Raises:

Type Description
ValueError

On shape mismatches, non-positive SDs or non-finite means.

BonsaiError

If no feature survives selection.

backbone

backbone(
    means: ndarray,
    sds: ndarray,
    *,
    variances: ndarray | None = None,
    backbone_cells: int | None = None,
    seed: int = 0,
    start: Start = "linkage",
    search: Search = "approximate",
    min_signal_to_noise: float | None = None,
    reroot: bool = True,
) -> BonsaiResult

Backbone mode for datasets too large to search directly.

Reconstructs on a random subset, places every other cell onto it one at a time, then refines the whole tree. This is the paper's route to large datasets.

Parameters:

Name Type Description Default
means ndarray

As bonsai.

required
sds ndarray

As bonsai.

required
variances ndarray | None

As bonsai.

None
backbone_cells int | None

Cells in the initial backbone. None for the default of 2048.

None
seed int

Seed for choosing the backbone subset.

0
start Start

As bonsai, for the backbone.

'linkage'
search Search

As bonsai.

'approximate'
min_signal_to_noise float | None

As bonsai.

None
reroot bool

As bonsai.

True

Returns:

Type Description
BonsaiResult

As bonsai.

BonsaiError

Bases: Exception

The data was well formed but the method could not use it.

gpu_available builtin

gpu_available() -> bool

Whether Sanity can run on the GPU here.

Params
  • py - Attached interpreter token
Returns

True only when this build has the gpu feature and wgpu resolves an adapter. Safe to call on any machine.