Metadata-Version: 2.4
Name: sm-rust
Version: 0.0.3
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Dist: numpy>=1.22
Requires-Dist: anndata>=0.10 ; extra == 'anndata'
Requires-Dist: pandas>=2.0 ; extra == 'anndata'
Requires-Dist: scipy>=1.10 ; extra == 'anndata'
Requires-Dist: pytest ; extra == 'test'
Requires-Dist: anndata>=0.10 ; extra == 'test'
Requires-Dist: pandas>=2.0 ; extra == 'test'
Requires-Dist: scipy>=1.10 ; extra == 'test'
Provides-Extra: anndata
Provides-Extra: test
License-File: LICENSE
Summary: Spatial multiomics primitives (neighbour graphs, Moran's I, nhood enrichment, Ripley, co-occurrence) — Python bindings.
Keywords: spatial,multiomics,bioinformatics,squidpy,anndata
Author-email: Francisco Couzo <franciscouzo@gmail.com>
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# sm-rust — Python bindings

Spatial multiomics primitives from the [sm-rust](https://github.com/nadeemlab/sm-rust)
crate (neighbour graphs, Moran's I, Geary's C, local LISA statistics — Local
Moran's I, Local Getis-Ord G_i/G_i*, Local Geary's C — neighbourhood enrichment,
Ripley F/G/L, co-occurrence, ligand-receptor, sepal, niche detection, per-cell
neighbourhood metrics — local density, distance to nearest cell of a type,
diversity bundle (Shannon / Simpson / inverse-Simpson / richness / Pielou) and
homophily / mixing fraction),
exposed to Python via [PyO3](https://pyo3.rs/) + [maturin](https://www.maturin.rs/).

## Layout

* `sm_rust._native` — the compiled extension module: `Cells`, `Neighbors`,
  every `compute_*` function returning result dataclass-like objects with
  numpy arrays. Mirrors the Node/wasm binding surface.
* `sm_rust.gr` — squidpy-shaped wrappers that consume `AnnData` directly:
  `spatial_neighbors`, `spatial_autocorr`, `nhood_enrichment`,
  `interaction_matrix`, `centrality_scores`, `co_occurrence`, `ripley`,
  `ligrec`, `sepal`. The output shape (column names, `.uns` keys) matches
  `squidpy.gr` so existing notebooks can drop this in.

## Build (host)

```bash
pip install maturin
maturin develop --release --features python-bindings,parallel
```

## Build (docker, recommended)

The repo's `docker-compose.yml` ships a `pyo3` service that builds a wheel
into `python/dist/`:

```bash
docker compose run --rm pyo3
```

## Quick start

Low-level API:

```python
import numpy as np
from sm_rust import _native as sm

cells = sm.cells_from_coords_py(np.random.rand(1000), np.random.rand(1000))
labels = np.random.randint(0, 3, 1000).astype(np.uint8)
neighbors = sm.Neighbors.knn(6)

res = sm.compute_nhood_enrichment_py(cells, labels, neighbors, n_clusters=3)
print(res.count.reshape(3, 3))
print(res.zscore.reshape(3, 3))
```

Local (LISA) statistics — one score per cell, returned as parallel numpy arrays
(mirroring `esda.Moran_Local` / `Geary_Local` / `G_Local`). Pass
`permutations=0` for the observed map only (conditional-null fields become
`NaN`):

```python
values = np.random.rand(1000)   # a continuous attribute / marker intensity

lm = sm.compute_local_moran_i_py(cells, values, neighbors, permutations=999, seed=42)
print(lm.Is, lm.q, lm.p_sim)         # local I, HH/LH/LL/HL quadrant, pseudo p

lg = sm.compute_local_geary_c_py(cells, values, neighbors, permutations=999)
print(lg.local_g, lg.p_sim)

# Getis-Ord hot-/cold-spot map; star=True selects G_i* (includes the focal cell).
go = sm.compute_local_getis_ord_py(cells, values, neighbors, star=True)
print(go.z, go.p_norm)               # standardised z-score + normal p-value
```

Per-cell neighbourhood metrics — one summary per cell over the supplied
`Neighbors` graph, returned as parallel numpy arrays (cells with no neighbours
hold `NaN`):

```python
# Local cell density: neighbour count + area-normalised density (count / πr²).
den = sm.compute_local_density_py(cells, neighbors)
print(den.count, den.density)

# Diversity of the neighbour cell-type composition (vegan conventions:
# simpson = 1 - Σp², inverse_simpson = 1/Σp², pielou = H / ln(richness)).
div = sm.compute_local_diversity_py(cells, labels, n_clusters=3, neighbors=neighbors)
print(div.richness, div.shannon, div.simpson, div.inverse_simpson, div.pielou)

# Homophily: fraction of neighbours sharing the focal cell's label (+ mixing).
hom = sm.compute_homophily_py(cells, labels, neighbors)
print(hom.homophily, hom.mixing)

# Distance from every cell to the nearest *other* cell of a target type (NaN
# where none exists; None if the target label is absent).
d = sm.compute_distance_to_nearest_type_py(cells, labels, target=1)
print(d)
```

squidpy-style API:

```python
import sm_rust as smr
import anndata as ad

smr.gr.spatial_neighbors(adata, coord_type="generic", n_neighs=6)
smr.gr.spatial_autocorr(adata, mode="moran", genes=adata.var_names[:50])
smr.gr.nhood_enrichment(adata, cluster_key="leiden")
```

