Metadata-Version: 2.2
Name: pyglmGamPoi
Version: 0.2.1
Summary: Pure-Python/C++ glmGamPoi offset fitting for SCTransform v2
Author: seqyuan
License: MIT
Requires-Python: >=3.10
Requires-Dist: numpy<2.0,>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: scipy>=1.10
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Description-Content-Type: text/markdown

# pyglmGamPoi

Pure Python/C++ reimplementation of `sctransform::fit_glmGamPoi_offset` for
[trackcell](https://github.com/seqyuan/trackcell) SCTransform v2, without an R
runtime.

## Architecture

```
pyglmGamPoi/offset.py          Python API (AnnData-friendly DataFrame output)
        │
pyglmGamPoi/_core.so           pybind11 thin binding (numpy ↔ std::vector)
        │
src/glm_gp_offset.cpp          glm_gp loop per gene (no Rcpp::List)
src/offset_fit.cpp             Newton-Raphson + Brent fallback
src/overdispersion.cpp         Cox-Reid overdispersion MLE (intercept-only)
```

The C++ core is **rewritten** from the algorithms in
[const-ae/glmGamPoi](https://github.com/const-ae/glmGamPoi); R headers and
Rtatami are not used.

## Install (editable)

```bash
git clone https://github.com/seqyuan/pyglmGamPoi.git
cd pyglmGamPoi
pip install -e ".[test]"
```

Requires a C++17 compiler and CMake.

## Usage

```python
import numpy as np
from pyglmGamPoi import fit_glmGamPoi_offset

umi = np.random.poisson(3, size=(100, 50)).astype(float)
log10_umi = np.log10(umi.sum(axis=0) + 1e-9)
model_pars = fit_glmGamPoi_offset(umi, log10_umi)
```

## trackcell integration

Replace `trackcell.tl._r_sctransform.fit_glmGamPoi_offset_r` with:

```python
from pyglmGamPoi import fit_offset_model
```

See [docs](https://pyglmgampoi.readthedocs.io/en/latest/integration.html).

## Status

- [x] Per-gene offset NB fit (design ~ 1 + offset)
- [x] Overdispersion MLE (Cox-Reid; local search + Newton)
- [x] `allow_inf_theta` / Poisson-boundary `theta = Inf`
- [x] Cross-gene `overdispersion_shrinkage` (R `glm_gp` default)
- [x] R parity on GSE288946 SCT step1 (3 samples, ~2000×2000 each)

| Metric (vs R live, pooled) | Value |
|----------------------------|-------|
| Samples | GSM8779707–709 |
| Theta med \|diff\| (finite) | ~2×10⁻⁷ |
| Intercept med \|diff\| | ~2.5×10⁻⁹ |
| Genes with \|Δ Intercept\| < 0.01 | 100% |
| Inf theta match | 463/463 |
| Speed vs R (8 threads) | **~6.4×** (7.1 s vs 46.5 s) |

Latest release: **v0.2.1** — see [changelog](https://pyglmgampoi.readthedocs.io/en/latest/changelog.html).

See [evaluation docs](https://pyglmgampoi.readthedocs.io/en/latest/evaluation.html) for full benchmark report.

## Docs & benchmark

```bash
python scripts/run_all_benchmarks.py    # real + synthetic → docs/_generated/
pytest tests/test_theta_od_parity.py -q
```

## License

MIT (this package). Algorithm reference: glmGamPoi (MIT in recent releases).
