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.
- scperteval-0.1.0/.github/workflows/build.yaml +26 -0
- scperteval-0.1.0/.github/workflows/lint.yaml +32 -0
- scperteval-0.1.0/.github/workflows/notebooks.yaml +41 -0
- scperteval-0.1.0/.github/workflows/release.yaml +30 -0
- scperteval-0.1.0/.github/workflows/test.yaml +150 -0
- scperteval-0.1.0/.gitignore +30 -0
- scperteval-0.1.0/.pre-commit-config.yaml +25 -0
- scperteval-0.1.0/.readthedocs.yaml +16 -0
- scperteval-0.1.0/CHANGELOG.md +62 -0
- scperteval-0.1.0/CITATION.cff +66 -0
- scperteval-0.1.0/CONTRIBUTORS.md +31 -0
- scperteval-0.1.0/LICENSE +21 -0
- scperteval-0.1.0/PKG-INFO +192 -0
- scperteval-0.1.0/README.md +134 -0
- scperteval-0.1.0/RELEASE.md +179 -0
- scperteval-0.1.0/docs/_mermaid/architecture.mmd +89 -0
- scperteval-0.1.0/docs/_mermaid/user-guide-overview.mmd +19 -0
- scperteval-0.1.0/docs/_static/css/custom.css +145 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-dark-logo.svg +136 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-dark-mascot-only.svg +121 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-dark-text-only.svg +57 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-favicon.svg +194 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-logo.svg +136 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-mascot-only.svg +121 -0
- scperteval-0.1.0/docs/_static/logo/scPertEval-text-only.svg +57 -0
- scperteval-0.1.0/docs/_templates/autosummary/class.rst +25 -0
- scperteval-0.1.0/docs/api/api.md +33 -0
- scperteval-0.1.0/docs/api/architecture.md +68 -0
- scperteval-0.1.0/docs/api/extensions.md +102 -0
- scperteval-0.1.0/docs/api/io.md +12 -0
- scperteval-0.1.0/docs/api/protocols.md +43 -0
- scperteval-0.1.0/docs/api/types.md +46 -0
- scperteval-0.1.0/docs/api.md +71 -0
- scperteval-0.1.0/docs/changelog.md +3 -0
- scperteval-0.1.0/docs/conf.py +148 -0
- scperteval-0.1.0/docs/contributing.md +6 -0
- scperteval-0.1.0/docs/extensions/notebook_cell_tabs.py +75 -0
- scperteval-0.1.0/docs/extensions/notebook_shell_lexer.py +39 -0
- scperteval-0.1.0/docs/extensions/protocol_table.py +61 -0
- scperteval-0.1.0/docs/extensions/typed_returns.py +31 -0
- scperteval-0.1.0/docs/index.md +122 -0
- scperteval-0.1.0/docs/installation.md +59 -0
- scperteval-0.1.0/docs/notebooks/01_cli_walkthrough.ipynb +648 -0
- scperteval-0.1.0/docs/notebooks/02_preparing_a_dataset.ipynb +551 -0
- scperteval-0.1.0/docs/notebooks/03_python_api.ipynb +926 -0
- scperteval-0.1.0/docs/references.bib +99 -0
- scperteval-0.1.0/docs/references.md +5 -0
- scperteval-0.1.0/docs/tutorials.md +22 -0
- scperteval-0.1.0/docs/user-guide/building-blocks.md +79 -0
- scperteval-0.1.0/docs/user-guide/calibration.md +98 -0
- scperteval-0.1.0/docs/user-guide/datasets.md +134 -0
- scperteval-0.1.0/docs/user-guide/index.md +38 -0
- scperteval-0.1.0/docs/user-guide/protocols.md +240 -0
- scperteval-0.1.0/docs/user-guide/python-api.md +135 -0
- scperteval-0.1.0/docs/user-guide/scoring.md +57 -0
- scperteval-0.1.0/docs/user-guide/usage.md +159 -0
- scperteval-0.1.0/pyproject.toml +221 -0
- scperteval-0.1.0/src/scperteval/__init__.py +64 -0
- scperteval-0.1.0/src/scperteval/__main__.py +3 -0
- scperteval-0.1.0/src/scperteval/api.py +486 -0
- scperteval-0.1.0/src/scperteval/blocks/__init__.py +3 -0
- scperteval-0.1.0/src/scperteval/blocks/de.py +262 -0
- scperteval-0.1.0/src/scperteval/blocks/spaces.py +205 -0
- scperteval-0.1.0/src/scperteval/calibrators.py +55 -0
- scperteval-0.1.0/src/scperteval/cli.py +249 -0
- scperteval-0.1.0/src/scperteval/context.py +357 -0
- scperteval-0.1.0/src/scperteval/dataset.py +106 -0
- scperteval-0.1.0/src/scperteval/io.py +120 -0
- scperteval-0.1.0/src/scperteval/predictions.py +65 -0
- scperteval-0.1.0/src/scperteval/protocols/__init__.py +3 -0
- scperteval-0.1.0/src/scperteval/protocols/metrics.py +423 -0
- scperteval-0.1.0/src/scperteval/protocols/resolve.py +99 -0
- scperteval-0.1.0/src/scperteval/protocols/table.py +111 -0
- scperteval-0.1.0/src/scperteval/py.typed +0 -0
- scperteval-0.1.0/src/scperteval/reference.py +50 -0
- scperteval-0.1.0/src/scperteval/registry.py +63 -0
- scperteval-0.1.0/src/scperteval/runner.py +239 -0
- scperteval-0.1.0/src/scperteval/sources.py +132 -0
- scperteval-0.1.0/src/scperteval/types.py +181 -0
- scperteval-0.1.0/tests/conftest.py +90 -0
- scperteval-0.1.0/tests/test_api.py +250 -0
- scperteval-0.1.0/tests/test_calibrate.py +53 -0
- scperteval-0.1.0/tests/test_cli.py +67 -0
- scperteval-0.1.0/tests/test_controls.py +95 -0
- scperteval-0.1.0/tests/test_de.py +179 -0
- scperteval-0.1.0/tests/test_defaults.py +40 -0
- scperteval-0.1.0/tests/test_optional_extras.py +137 -0
- scperteval-0.1.0/tests/test_score.py +82 -0
- scperteval-0.1.0/tests/test_user_sources.py +167 -0
- 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.
|
scperteval-0.1.0/LICENSE
ADDED
|
@@ -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.
|