scperteval 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. scperteval-0.1.0/.github/workflows/build.yaml +26 -0
  2. scperteval-0.1.0/.github/workflows/lint.yaml +32 -0
  3. scperteval-0.1.0/.github/workflows/notebooks.yaml +41 -0
  4. scperteval-0.1.0/.github/workflows/release.yaml +30 -0
  5. scperteval-0.1.0/.github/workflows/test.yaml +150 -0
  6. scperteval-0.1.0/.gitignore +30 -0
  7. scperteval-0.1.0/.pre-commit-config.yaml +25 -0
  8. scperteval-0.1.0/.readthedocs.yaml +16 -0
  9. scperteval-0.1.0/CHANGELOG.md +62 -0
  10. scperteval-0.1.0/CITATION.cff +66 -0
  11. scperteval-0.1.0/CONTRIBUTORS.md +31 -0
  12. scperteval-0.1.0/LICENSE +21 -0
  13. scperteval-0.1.0/PKG-INFO +192 -0
  14. scperteval-0.1.0/README.md +134 -0
  15. scperteval-0.1.0/RELEASE.md +179 -0
  16. scperteval-0.1.0/docs/_mermaid/architecture.mmd +89 -0
  17. scperteval-0.1.0/docs/_mermaid/user-guide-overview.mmd +19 -0
  18. scperteval-0.1.0/docs/_static/css/custom.css +145 -0
  19. scperteval-0.1.0/docs/_static/logo/scPertEval-dark-logo.svg +136 -0
  20. scperteval-0.1.0/docs/_static/logo/scPertEval-dark-mascot-only.svg +121 -0
  21. scperteval-0.1.0/docs/_static/logo/scPertEval-dark-text-only.svg +57 -0
  22. scperteval-0.1.0/docs/_static/logo/scPertEval-favicon.svg +194 -0
  23. scperteval-0.1.0/docs/_static/logo/scPertEval-logo.svg +136 -0
  24. scperteval-0.1.0/docs/_static/logo/scPertEval-mascot-only.svg +121 -0
  25. scperteval-0.1.0/docs/_static/logo/scPertEval-text-only.svg +57 -0
  26. scperteval-0.1.0/docs/_templates/autosummary/class.rst +25 -0
  27. scperteval-0.1.0/docs/api/api.md +33 -0
  28. scperteval-0.1.0/docs/api/architecture.md +68 -0
  29. scperteval-0.1.0/docs/api/extensions.md +102 -0
  30. scperteval-0.1.0/docs/api/io.md +12 -0
  31. scperteval-0.1.0/docs/api/protocols.md +43 -0
  32. scperteval-0.1.0/docs/api/types.md +46 -0
  33. scperteval-0.1.0/docs/api.md +71 -0
  34. scperteval-0.1.0/docs/changelog.md +3 -0
  35. scperteval-0.1.0/docs/conf.py +148 -0
  36. scperteval-0.1.0/docs/contributing.md +6 -0
  37. scperteval-0.1.0/docs/extensions/notebook_cell_tabs.py +75 -0
  38. scperteval-0.1.0/docs/extensions/notebook_shell_lexer.py +39 -0
  39. scperteval-0.1.0/docs/extensions/protocol_table.py +61 -0
  40. scperteval-0.1.0/docs/extensions/typed_returns.py +31 -0
  41. scperteval-0.1.0/docs/index.md +122 -0
  42. scperteval-0.1.0/docs/installation.md +59 -0
  43. scperteval-0.1.0/docs/notebooks/01_cli_walkthrough.ipynb +648 -0
  44. scperteval-0.1.0/docs/notebooks/02_preparing_a_dataset.ipynb +551 -0
  45. scperteval-0.1.0/docs/notebooks/03_python_api.ipynb +926 -0
  46. scperteval-0.1.0/docs/references.bib +99 -0
  47. scperteval-0.1.0/docs/references.md +5 -0
  48. scperteval-0.1.0/docs/tutorials.md +22 -0
  49. scperteval-0.1.0/docs/user-guide/building-blocks.md +79 -0
  50. scperteval-0.1.0/docs/user-guide/calibration.md +98 -0
  51. scperteval-0.1.0/docs/user-guide/datasets.md +134 -0
  52. scperteval-0.1.0/docs/user-guide/index.md +38 -0
  53. scperteval-0.1.0/docs/user-guide/protocols.md +240 -0
  54. scperteval-0.1.0/docs/user-guide/python-api.md +135 -0
  55. scperteval-0.1.0/docs/user-guide/scoring.md +57 -0
  56. scperteval-0.1.0/docs/user-guide/usage.md +159 -0
  57. scperteval-0.1.0/pyproject.toml +221 -0
  58. scperteval-0.1.0/src/scperteval/__init__.py +64 -0
  59. scperteval-0.1.0/src/scperteval/__main__.py +3 -0
  60. scperteval-0.1.0/src/scperteval/api.py +486 -0
  61. scperteval-0.1.0/src/scperteval/blocks/__init__.py +3 -0
  62. scperteval-0.1.0/src/scperteval/blocks/de.py +262 -0
  63. scperteval-0.1.0/src/scperteval/blocks/spaces.py +205 -0
  64. scperteval-0.1.0/src/scperteval/calibrators.py +55 -0
  65. scperteval-0.1.0/src/scperteval/cli.py +249 -0
  66. scperteval-0.1.0/src/scperteval/context.py +357 -0
  67. scperteval-0.1.0/src/scperteval/dataset.py +106 -0
  68. scperteval-0.1.0/src/scperteval/io.py +120 -0
  69. scperteval-0.1.0/src/scperteval/predictions.py +65 -0
  70. scperteval-0.1.0/src/scperteval/protocols/__init__.py +3 -0
  71. scperteval-0.1.0/src/scperteval/protocols/metrics.py +423 -0
  72. scperteval-0.1.0/src/scperteval/protocols/resolve.py +99 -0
  73. scperteval-0.1.0/src/scperteval/protocols/table.py +111 -0
  74. scperteval-0.1.0/src/scperteval/py.typed +0 -0
  75. scperteval-0.1.0/src/scperteval/reference.py +50 -0
  76. scperteval-0.1.0/src/scperteval/registry.py +63 -0
  77. scperteval-0.1.0/src/scperteval/runner.py +239 -0
  78. scperteval-0.1.0/src/scperteval/sources.py +132 -0
  79. scperteval-0.1.0/src/scperteval/types.py +181 -0
  80. scperteval-0.1.0/tests/conftest.py +90 -0
  81. scperteval-0.1.0/tests/test_api.py +250 -0
  82. scperteval-0.1.0/tests/test_calibrate.py +53 -0
  83. scperteval-0.1.0/tests/test_cli.py +67 -0
  84. scperteval-0.1.0/tests/test_controls.py +95 -0
  85. scperteval-0.1.0/tests/test_de.py +179 -0
  86. scperteval-0.1.0/tests/test_defaults.py +40 -0
  87. scperteval-0.1.0/tests/test_optional_extras.py +137 -0
  88. scperteval-0.1.0/tests/test_score.py +82 -0
  89. scperteval-0.1.0/tests/test_user_sources.py +167 -0
  90. scperteval-0.1.0/tests/test_warm_pca.py +101 -0
@@ -0,0 +1,26 @@
1
+ name: Check Build
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ package:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ with:
19
+ filter: blob:none
20
+ fetch-depth: 0
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v7
23
+ - name: Build package
24
+ run: uv build
25
+ - name: Check package
26
+ run: uvx twine check --strict dist/*.whl
@@ -0,0 +1,32 @@
1
+ name: Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ lint:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ with:
19
+ filter: blob:none
20
+ fetch-depth: 0
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v7
23
+ - name: Install lint deps
24
+ run: uv sync --group lint
25
+ - name: Ruff lint
26
+ run: uv run ruff check .
27
+ - name: Ruff format check
28
+ run: uv run ruff format --check .
29
+ - name: mypy
30
+ run: uv run mypy src/scperteval
31
+ - name: pyright
32
+ run: uv run pyright src/scperteval
@@ -0,0 +1,41 @@
1
+ name: Notebooks
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ execute:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ with:
19
+ # hatch-vcs derives the package version from git tags; fetch full history + tags
20
+ # so `uv sync` (which builds/installs the package) resolves a real version.
21
+ fetch-depth: 0
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v7
24
+ - name: Install docs deps
25
+ run: uv sync --group docs
26
+ - name: Execute tutorial notebooks (guard against stale/broken output)
27
+ # Docs render the committed (already-executed) notebooks, so re-run them here to
28
+ # catch a tutorial that no longer runs against the current API. Output is discarded:
29
+ # this only checks that the notebook still executes, not that its values match.
30
+ #
31
+ # Notebooks that download large real datasets (hundreds of MB to GBs) are skipped here
32
+ # via SKIP — they are executed by hand and committed with their outputs.
33
+ run: |
34
+ SKIP="02_preparing_a_dataset.ipynb"
35
+ for nb in docs/notebooks/*.ipynb; do
36
+ case " $SKIP " in
37
+ *" $(basename "$nb") "*) echo "skipping $nb (downloads large datasets; not run in CI)"; continue;;
38
+ esac
39
+ echo "executing $nb"
40
+ uv run jupyter nbconvert --to notebook --execute --stdout "$nb" > /dev/null
41
+ done
@@ -0,0 +1,30 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ concurrency:
8
+ group: ${{ github.workflow }}-${{ github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ release:
13
+ name: Upload release to PyPI
14
+ runs-on: ubuntu-latest
15
+ environment:
16
+ name: pypi
17
+ url: https://pypi.org/p/scperteval
18
+ permissions:
19
+ id-token: write # required for trusted publishing
20
+ steps:
21
+ - uses: actions/checkout@v6
22
+ with:
23
+ filter: blob:none
24
+ fetch-depth: 0
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v7
27
+ - name: Build package
28
+ run: uv build
29
+ - name: Publish package distributions to PyPI
30
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,150 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "0 5 1,15 * *"
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ # Get the test environment from hatch as defined in pyproject.toml.
17
+ # This ensures that the pyproject.toml is the single point of truth for test definitions and the same tests are
18
+ # run locally and on continuous integration.
19
+ # Check [[tool.hatch.envs.hatch-test.matrix]] in pyproject.toml and https://hatch.pypa.io/latest/environment/ for
20
+ # more details.
21
+ get-environments:
22
+ runs-on: ubuntu-slim
23
+ outputs:
24
+ envs: ${{ steps.get-envs.outputs.envs }}
25
+ steps:
26
+ - uses: actions/checkout@v6
27
+ with:
28
+ filter: blob:none
29
+ fetch-depth: 0
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v7
32
+ - name: Get test environments
33
+ id: get-envs
34
+ run: |
35
+ ENVS_JSON=$(uvx hatch env show --json | jq -c 'to_entries
36
+ | map(
37
+ select(.key | startswith("hatch-test"))
38
+ | {
39
+ name: .key,
40
+ label: (if (.key | contains("pre")) then .key + " (PRE-RELEASE DEPENDENCIES)" else .key end),
41
+ python: .value.python
42
+ }
43
+ )')
44
+ echo "envs=${ENVS_JSON}" | tee $GITHUB_OUTPUT
45
+
46
+ # Run tests through hatch. Spawns a separate runner for each environment defined in the hatch matrix obtained above.
47
+ test:
48
+ needs: get-environments
49
+ permissions:
50
+ id-token: write # for codecov OIDC
51
+ contents: read
52
+
53
+ strategy:
54
+ fail-fast: false
55
+ matrix:
56
+ os: [ubuntu-latest]
57
+ env: ${{ fromJSON(needs.get-environments.outputs.envs) }}
58
+
59
+ name: ${{ matrix.env.label }}
60
+ runs-on: ${{ matrix.os }}
61
+ continue-on-error: ${{ contains(matrix.env.name, 'pre') }} # make "all-green" pass even if pre-release job fails
62
+
63
+ steps:
64
+ - uses: actions/checkout@v6
65
+ with:
66
+ filter: blob:none
67
+ fetch-depth: 0
68
+ - name: Install uv
69
+ uses: astral-sh/setup-uv@v7
70
+ with:
71
+ python-version: ${{ matrix.env.python }}
72
+ - name: create hatch environment
73
+ run: uvx hatch env create ${{ matrix.env.name }}
74
+ - name: list all all installed package versions
75
+ run: uvx hatch run ${{ matrix.env.name }}:uv pip list
76
+ - name: run tests using hatch
77
+ env:
78
+ MPLBACKEND: agg
79
+ PLATFORM: ${{ matrix.os }}
80
+ DISPLAY: :42
81
+ run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto
82
+ - name: generate coverage report
83
+ run: |
84
+ # See https://coverage.readthedocs.io/page/config.html#run-patch
85
+ test -f .coverage || uvx hatch run ${{ matrix.env.name }}:cov-combine
86
+ uvx hatch run ${{ matrix.env.name }}:cov-report # report visibly
87
+ uvx hatch run ${{ matrix.env.name }}:coverage xml # create report for upload
88
+ - name: Upload coverage
89
+ uses: codecov/codecov-action@v6
90
+ with:
91
+ # Don't fail the job if the upload fails: the repo isn't activated in the
92
+ # Virtual-Cell-Research-Community Codecov org yet, so OIDC tokenless upload is
93
+ # rejected. Flip back to true once Codecov is set up (see CONTRIBUTORS/PR notes).
94
+ fail_ci_if_error: false
95
+ use_oidc: true
96
+
97
+ # Install the *exact* lower bounds declared in [project.dependencies] and run the suite against
98
+ # them. The floors encode real, bisected compatibility limits (see the comment above the
99
+ # dependency list in pyproject.toml); without this job they are unverifiable claims that drift
100
+ # out of date silently. Runs on the oldest supported Python for the same reason.
101
+ floors:
102
+ name: Tests pass at the declared dependency floors
103
+ runs-on: ubuntu-latest
104
+ steps:
105
+ - uses: actions/checkout@v6
106
+ with:
107
+ filter: blob:none
108
+ fetch-depth: 0
109
+ - name: Install uv
110
+ uses: astral-sh/setup-uv@v7
111
+ with:
112
+ python-version: "3.11"
113
+ - name: Pin every dependency to its declared floor
114
+ run: |
115
+ python - <<'PY' > floors.txt
116
+ import pathlib, re, tomllib
117
+ project = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]
118
+ deps = project["dependencies"] + project["optional-dependencies"]["sinkhorn"]
119
+ for dep in deps:
120
+ m = re.fullmatch(r"([A-Za-z0-9._-]+)>=([0-9][^,;\s]*)", dep.strip())
121
+ if not m:
122
+ raise SystemExit(f"dependency {dep!r} is not a simple `name>=version` floor")
123
+ print(f"{m[1]}=={m[2]}")
124
+ PY
125
+ cat floors.txt
126
+ - name: Install at the floors
127
+ # the floors go in first, then the package with --no-deps so it cannot pull them back up
128
+ run: |
129
+ uv venv --python 3.11
130
+ uv pip install -r floors.txt "pytest>=8"
131
+ uv pip install --no-deps .
132
+ - name: Run tests
133
+ env:
134
+ MPLBACKEND: agg
135
+ run: uv run --no-sync pytest -v --color=yes
136
+
137
+ # Check that all tests defined above pass. This makes it easy to set a single "required" test in branch
138
+ # protection instead of having to update it frequently. See https://github.com/re-actors/alls-green#why.
139
+ check:
140
+ name: Tests pass in all hatch environments
141
+ if: always()
142
+ needs:
143
+ - get-environments
144
+ - test
145
+ - floors
146
+ runs-on: ubuntu-latest
147
+ steps:
148
+ - uses: re-actors/alls-green@release/v1
149
+ with:
150
+ jobs: ${{ toJSON(needs) }}
@@ -0,0 +1,30 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+
10
+ # Environment
11
+ uv.lock
12
+ .envrc
13
+ requirements-local.txt
14
+
15
+ # Run outputs
16
+ results/
17
+
18
+ # Local research/scratch work — kept out of git and out of the sdist (hatchling's sdist
19
+ # includes anything not ignored here, so an untracked scratch file would otherwise ship).
20
+ debug_api.py
21
+ stress_empirical/
22
+
23
+ # Editor / OS
24
+ .idea/
25
+ .vscode/
26
+ .DS_Store
27
+
28
+ # docs
29
+ docs/generated/
30
+ docs/_build/
@@ -0,0 +1,25 @@
1
+ # Run `pre-commit install` once; hooks then run on every commit.
2
+ # Update pinned revs with `pre-commit autoupdate`.
3
+ repos:
4
+ - repo: https://github.com/astral-sh/ruff-pre-commit
5
+ rev: v0.15.13
6
+ hooks:
7
+ - id: ruff
8
+ args: [--fix]
9
+ - id: ruff-format
10
+
11
+ - repo: https://github.com/tox-dev/pyproject-fmt
12
+ rev: "v2.25.1"
13
+ hooks:
14
+ - id: pyproject-fmt
15
+
16
+ - repo: https://github.com/pre-commit/pre-commit-hooks
17
+ rev: v5.0.0
18
+ hooks:
19
+ - id: end-of-file-fixer
20
+ - id: trailing-whitespace
21
+ - id: check-yaml
22
+ - id: check-toml
23
+ - id: check-merge-conflict
24
+ - id: check-added-large-files
25
+ args: [--maxkb=1024]
@@ -0,0 +1,16 @@
1
+ # https://docs.readthedocs.io/en/stable/config-file/v2.html
2
+ version: 2
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.12"
7
+ jobs:
8
+ create_environment:
9
+ - asdf plugin add uv
10
+ - asdf install uv latest
11
+ - asdf global uv latest
12
+ build:
13
+ html:
14
+ - uv sync --group docs
15
+ - uv run sphinx-build -M html docs docs/_build -W
16
+ - mv docs/_build $READTHEDOCS_OUTPUT
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.0
6
+
7
+ First release of scPertEval — reference implementations of single-cell perturbation evaluation
8
+ protocols, usable from the command line and as a native Python API.
9
+
10
+ scPertEval accompanies [Schäfer et al. (2026), *Towards Principled Evaluation of Single-Cell
11
+ Perturbation Prediction Models*](https://doi.org/10.64898/2026.07.23.740433).
12
+
13
+ ### Features
14
+
15
+ - **22 evaluation protocols** in one declarative table, spanning three groups: `pseudobulk`
16
+ (Pearson/MSE/weighted-MSE variants over full, top-k and DEG feature spaces, plus
17
+ cross-perturbation retrieval rank), `distributional` (unbiased MMD, energy distance, and
18
+ Sinkhorn W2 over top-k/PCA spaces), and `de` (AUPRC, AUROC, top-k overlap).
19
+ - **Three actions over the same catalog** — `calibrate` (score a protocol against empirical
20
+ positive/negative controls per perturbation, reporting DRF or BDS), `score` (score model
21
+ predictions against ground truth), and `de` (export per-gene differential expression).
22
+ - **Two calibrators**: Dynamic Range Fraction (DRF) and Bound Discrimination Score (BDS).
23
+ - **Native Python API** (`prepare` / `calibrate` / `score` / `de`) that reads and indexes a
24
+ dataset once and reuses it across calls, alongside the `scperteval` CLI.
25
+ - **Extension points** for new protocols, metrics, feature spaces, DE methods, control sources,
26
+ and calibrators — each a registered function plus one table row.
27
+
28
+ ### Packaging
29
+
30
+ - `torch` and `geomloss` are an optional `sinkhorn` extra rather than base dependencies, so
31
+ `pip install scperteval` stays light. Bulk protocol selections (`-p all`, `-p distributional`)
32
+ skip the Sinkhorn protocols with a warning when the extra is absent; naming one explicitly
33
+ raises an error that points at `pip install "scperteval[sinkhorn]"`.
34
+ - Ships a `py.typed` marker, so downstream users get the package's type hints.
35
+ - The version is derived from the git tag by `hatch-vcs`; see `RELEASE.md`.
36
+
37
+ ### A note on the dependency floors
38
+
39
+ The lower bounds in `pyproject.toml` are bisected, not guessed — each is the oldest version the
40
+ full test suite passes on, and the `floors` CI job reinstalls exactly those pins on every push so
41
+ they cannot drift. Both ends of the supported range are verified: the declared floors
42
+ (anndata 0.12.7 / numpy 2.0 / pandas 2.2.2 / torch 2.4, Python 3.11) and current releases
43
+ (anndata 0.13 / numpy 2.4 / pandas 3.0 / torch 2.13, Python 3.14) both pass, which is why
44
+ dependencies are specified as open-ended floors with no upper caps.
45
+
46
+ What sets the floors, in case someone tries to lower them:
47
+
48
+ - **`numpy>=2`** is the dominant constraint. Under numpy 1.x, `illico`'s asymptotic-Wilcoxon
49
+ path (the `MWU` DE method) derives its test count as a float and formats it with `:,d`,
50
+ raising `Unknown format code 'd' for object of type 'float'`.
51
+ - **`h5py>=3.11`** and **`scikit-learn>=1.5`** follow from that: earlier releases ship numpy-1.x
52
+ ABI wheels and abort at import with `numpy.dtype size changed`.
53
+ - **`torch>=2.4`** for the same reason — 2.1–2.3 compute correct results against numpy 2 but
54
+ bury every run in `A module that was compiled using NumPy 1.x` warnings.
55
+ - **`anndata>=0.12.7`** — below it the suite cannot write its own h5ad fixtures (anndata refuses
56
+ `pd.arrays.StringArray` without `settings.allow_write_nullable_strings`), and at 0.11.x and
57
+ older it is a genuine runtime break: `illico` reaches for
58
+ `anndata._core.sparse_dataset._CSCDataset`, which does not exist yet.
59
+ - **`pandas>=2.2.2`** and **`scipy>=1.13`** are inherited from `illico`'s own requirements;
60
+ anything lower is unsatisfiable. Note `illico` 0.6.0's declared `anndata>=0.10.8` is looser
61
+ than what it actually needs, which is why scPertEval pins these from testing rather than
62
+ relying on the transitive constraint.
@@ -0,0 +1,66 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use scPertEval, please cite the paper it accompanies (preferred-citation)."
3
+ title: scPertEval
4
+ abstract: >-
5
+ Evaluation Protocols for Perturbation Studies: reference implementations of single-cell
6
+ perturbation evaluation protocols, with per-metric DRF/BDS calibration against empirical
7
+ positive and negative controls on a single preprocessed dataset.
8
+ type: software
9
+ license: MIT
10
+ repository-code: "https://github.com/Virtual-Cell-Research-Community/scPertEval"
11
+ url: "https://scperteval.readthedocs.io/"
12
+ keywords:
13
+ - single-cell
14
+ - perturb-seq
15
+ - perturbation
16
+ - differential-expression
17
+ - benchmark
18
+ - evaluation
19
+ - bioinformatics
20
+ # The authors of the paper that introduces scPertEval, in publication order.
21
+ authors:
22
+ - family-names: Schäfer
23
+ given-names: Philipp S. L.
24
+ affiliation: >-
25
+ Institute for Computational Biomedicine, Heidelberg University and Heidelberg University
26
+ Hospital
27
+ - family-names: Reid
28
+ given-names: Kendall A.
29
+ affiliation: >-
30
+ Bioinformatics Program, Center for Biotechnology Education, Johns Hopkins University
31
+ - family-names: Boldyga
32
+ given-names: Zach
33
+ affiliation: Independent Researcher
34
+ - family-names: Aksu
35
+ given-names: Ekin D.
36
+ affiliation: >-
37
+ Department of Computational Molecular Biology, Max Planck Institute for Molecular Genetics
38
+ - family-names: Hakem
39
+ given-names: Hugo
40
+ affiliation: >-
41
+ European Molecular Biology Laboratory, European Bioinformatics Institute (EMBL-EBI)
42
+ - family-names: Saez-Rodriguez
43
+ given-names: Julio
44
+ affiliation: >-
45
+ Institute for Computational Biomedicine, Heidelberg University and Heidelberg University
46
+ Hospital
47
+ preferred-citation:
48
+ type: article
49
+ title: Towards Principled Evaluation of Single-Cell Perturbation Prediction Models
50
+ journal: bioRxiv
51
+ year: 2026
52
+ doi: 10.64898/2026.07.23.740433
53
+ url: "https://www.biorxiv.org/content/10.64898/2026.07.23.740433v1"
54
+ authors:
55
+ - family-names: Schäfer
56
+ given-names: Philipp S. L.
57
+ - family-names: Reid
58
+ given-names: Kendall A.
59
+ - family-names: Boldyga
60
+ given-names: Zach
61
+ - family-names: Aksu
62
+ given-names: Ekin D.
63
+ - family-names: Hakem
64
+ given-names: Hugo
65
+ - family-names: Saez-Rodriguez
66
+ given-names: Julio
@@ -0,0 +1,31 @@
1
+ # Contributing to scPertEval
2
+
3
+ scPertEval is meant to be a shared catalog of evaluation protocols, so contributions are
4
+ welcome. There are a few paths, depending on what you're changing.
5
+
6
+ ## New evaluation protocol implementations — open a Pull Request
7
+
8
+ If you're adding a protocol (a new metric, or a new combination of an existing metric with
9
+ a space / centering / controls), **open a PR directly.** This is the common case and the
10
+ whole point of the project. See [Create a protocol](https://github.com/Virtual-Cell-Research-Community/scPertEval/blob/main/docs/user-guide/protocols.md#create-a-protocol) for the
11
+ two-step pattern (a pure function in `src/scperteval/protocols/metrics.py` plus a row in
12
+ `src/scperteval/protocols/table.py`). Adding a new building block (feature space, DE method, control
13
+ source, calibrator) the same way is also welcome as a PR.
14
+
15
+ Please include:
16
+ - a one-line reference to the source paper/method the protocol comes from, where applicable;
17
+ - the protocol added to the table and runnable via `scperteval calibrate ... -p <name>`.
18
+
19
+ ## Bugs or changes to core code — open an Issue first
20
+
21
+ If you've found a bug, or want to change shared/core behavior (the runner, the context
22
+ engine, the reference/sampling logic, the calibrators, or the scoring semantics),
23
+ **open an Issue and discuss it first** before sending a PR. Core changes affect every
24
+ protocol's results, so we want to agree on the approach before implementation.
25
+
26
+ ## Tutorials and notebooks
27
+
28
+ For tutorials and more in-depth examples, consider adding a notebook under `docs/notebooks/`.
29
+ Commit it as an executed notebook (outputs saved) so the rendered docs show real output, then
30
+ add it to the toctree in `docs/tutorials.md`. The `Notebooks` CI workflow re-executes every
31
+ notebook against the current API to guard against tutorials that no longer run.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Virtual Cell Research Community
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.