Metadata-Version: 2.4
Name: perturbresidual
Version: 0.1.0
Summary: Auditable functional-coupling scores from measured combinatorial perturbation panels
Author: Ziran Peng
Maintainer: Ziran Peng
License-Expression: MIT
Project-URL: Homepage, https://pypi.org/project/perturbresidual/
Project-URL: Documentation, https://pypi.org/project/perturbresidual/#description
Project-URL: Source, https://pypi.org/project/perturbresidual/#files
Keywords: combinatorial perturbation,functional coupling,genetic interaction,single-cell,causal discovery
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: <3.13,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy<3,>=1.26
Requires-Dist: scipy<2,>=1.10
Requires-Dist: pandas<3,>=2.0
Provides-Extra: validation
Requires-Dist: scikit-learn<2,>=1.3; extra == "validation"
Dynamic: license-file

# PerturbResidual 0.1.0

PerturbResidual converts matched control, single-perturbation, and measured
double-perturbation means into auditable pair-level functional-coupling scores.
Score construction does not read STRING, KEGG, dataset names, or other
functional labels.

The scientific object is the higher-order residual

```text
R_AB = d_AB - d_A - d_B
```

where each effect is measured relative to the same control. PerturbResidual
then separates recurrent source means from gene-scale heteroscedasticity and
chooses a frozen nuisance-control branch using only the measured panel's median
endpoint degree.

## Scientific scope

PerturbResidual:

- ranks combinations that have already been measured;
- tests whether higher-order residual structure carries functional coupling;
- reports whether its selected branch contains a transductive component.

It is not a directed-edge estimator, a full-transcriptome response predictor,
or a method for predicting arbitrary unmeasured combinations.

## Frozen algorithm

For each measured pair, the software constructs:

1. the original pair-specific top-10 residual score;
2. a strict leave-one-pair-out source-mean correction;
3. an independently estimated gene-scale correction;
4. a sparse OPG-A score;
5. when required, a strict-LOPO spectral score and a bounded transductive PC5
   tail.

The label-blind router is:

```text
median endpoint degree < 5   -> sparse OPG-A
median endpoint degree >= 5  -> dense spectral-tail
```

The strict-LOPO description applies to the spectral component. The PC5 tail is
unsupervised but transductive because its basis contains the scored pair; this
fact is written to every dense-run audit. Dense scoring requires at least ten
genes after the frozen HVG and perturbation-identity masks; smaller spaces stop
with an explicit error instead of silently changing the low-rank formula.

## Install the release artifact

PerturbResidual 0.1.0 supports Python 3.12. Compatibility will be broadened
only after the corresponding runtime CI gates pass.

```bash
python -m pip install perturbresidual-0.1.0-py3-none-any.whl
```

After an eventual PyPI publication, the intended command is:

```bash
python -m pip install perturbresidual==0.1.0
```

The present release bundle prepares the wheel and source distribution but does
not claim that either has already been uploaded to PyPI.

## Python API

```python
from perturbresidual import PerturbResidual

model = PerturbResidual(route="auto")
result = model.fit_score(
    control=control,          # shape: (genes,)
    singles=singles,          # dict[str, array of shape (genes,)]
    doubles=double_means,     # shape: (pairs, genes)
    pairs=pairs,              # list[tuple[str, str]]
    gene_names=gene_names,
)

scores = result.scores
audit = result.audit
frame = result.to_dataframe()
```

`route="sparse"` and `route="dense"` are available for diagnostics. Scientific
use should normally retain `route="auto"`. Changing route, `hvg_quantile`,
`top_k`, or `source_ridge` marks the output `NONCANONICAL_VARIANT`, records the
exact parameter differences, and removes the frozen-paper evidence claim. Set
`compute_all_components=True` to compute dense diagnostics without changing the
selected score or canonical status.

## Command line

```bash
perturbresidual score \
  --control control.csv \
  --singles singles.csv \
  --doubles doubles.csv \
  --output results
```

Input files use a wide format:

- `control.csv`: exactly one row; every column is a gene;
- `singles.csv`: first column `perturbation`, followed by the same gene columns;
- `doubles.csv`: first columns `source_a,source_b`, followed by the same genes.

Perturbation names and pair endpoints must be nonempty. Duplicate single names
are rejected rather than silently overwritten.

The output directory contains:

- `pair_scores.csv`;
- `route_audit.json`;
- `nuisance_diagnostics.csv`;
- `run_config.json`.

## Reproducibility

Run fast tests from the source tree:

```bash
PYTHONPATH=src python -m unittest discover -s tests -v
```

Run the frozen Norman/Wessels regression when `PerturbResidual_repo_v2` is
available alongside the release:

```bash
PYTHONPATH=src python validation/run_frozen_regression.py \
  --repo-root ../PerturbResidual_repo_v2 \
  --out-dir validation/results
```

The regression finalizes both label-blind score vectors before evaluating the
four STRING/KEGG metrics per panel.

## Evidence boundary

The paper reports the strongest point estimates in the symmetrically evaluated
external comparator fields across eight Norman/Wessels metrics. Both panels
contributed to method development, and seven of eight source-endpoint intervals
cross zero. This package therefore supports reproducibility and reuse; it does
not turn the developmental result into confirmatory two-panel SOTA.

## License

MIT.

Maintainer: Ziran Peng.
