arc-pie 0.1.6__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 (163) hide show
  1. arc_pie-0.1.6/.github/workflows/release.yml +42 -0
  2. arc_pie-0.1.6/.gitignore +23 -0
  3. arc_pie-0.1.6/.python-version +1 -0
  4. arc_pie-0.1.6/AGENTS.md +211 -0
  5. arc_pie-0.1.6/LICENSE.md +429 -0
  6. arc_pie-0.1.6/PKG-INFO +174 -0
  7. arc_pie-0.1.6/README.md +131 -0
  8. arc_pie-0.1.6/common.sh.example +10 -0
  9. arc_pie-0.1.6/pyproject.toml +103 -0
  10. arc_pie-0.1.6/src/pie/__init__.py +8 -0
  11. arc_pie-0.1.6/src/pie/assets.py +301 -0
  12. arc_pie-0.1.6/src/pie/cli.py +229 -0
  13. arc_pie-0.1.6/src/pie/config.py +267 -0
  14. arc_pie-0.1.6/src/pie/configs/eval.yaml +14 -0
  15. arc_pie-0.1.6/src/pie/configs/experiment/replogle_wdataset.yaml +40 -0
  16. arc_pie-0.1.6/src/pie/configs/experiment/replogle_xdataset.yaml +48 -0
  17. arc_pie-0.1.6/src/pie/configs/infer.yaml +14 -0
  18. arc_pie-0.1.6/src/pie/configs/prep/dataset/arc_vcc_25.yaml +14 -0
  19. arc_pie-0.1.6/src/pie/configs/prep/dataset/jiang.yaml +7 -0
  20. arc_pie-0.1.6/src/pie/configs/prep/dataset/orion.yaml +7 -0
  21. arc_pie-0.1.6/src/pie/configs/prep/dataset/replogle.yaml +12 -0
  22. arc_pie-0.1.6/src/pie/configs/prep/dataset/tahoe.yaml +10 -0
  23. arc_pie-0.1.6/src/pie/configs/prep/label_format/pie_process.yaml +10 -0
  24. arc_pie-0.1.6/src/pie/configs/prep/label_format/table.yaml +10 -0
  25. arc_pie-0.1.6/src/pie/configs/prep.yaml +39 -0
  26. arc_pie-0.1.6/src/pie/configs/process/dataset/arc_vcc_25.yaml +13 -0
  27. arc_pie-0.1.6/src/pie/configs/process/dataset/jiang.yaml +12 -0
  28. arc_pie-0.1.6/src/pie/configs/process/dataset/orion.yaml +11 -0
  29. arc_pie-0.1.6/src/pie/configs/process/dataset/replogle.yaml +9 -0
  30. arc_pie-0.1.6/src/pie/configs/process/dataset/tahoe.yaml +8 -0
  31. arc_pie-0.1.6/src/pie/configs/process.yaml +37 -0
  32. arc_pie-0.1.6/src/pie/configs/sources.yaml +25 -0
  33. arc_pie-0.1.6/src/pie/configs/train.yaml +80 -0
  34. arc_pie-0.1.6/src/pie/data/__init__.py +0 -0
  35. arc_pie-0.1.6/src/pie/data/datamodule.py +474 -0
  36. arc_pie-0.1.6/src/pie/data/dataset.py +269 -0
  37. arc_pie-0.1.6/src/pie/data/delta_p.py +119 -0
  38. arc_pie-0.1.6/src/pie/data/evidence.py +1057 -0
  39. arc_pie-0.1.6/src/pie/data/preprocessed.py +298 -0
  40. arc_pie-0.1.6/src/pie/data/samplers.py +170 -0
  41. arc_pie-0.1.6/src/pie/data/splits.py +126 -0
  42. arc_pie-0.1.6/src/pie/evaluate.py +124 -0
  43. arc_pie-0.1.6/src/pie/infer.py +33 -0
  44. arc_pie-0.1.6/src/pie/metrics.py +439 -0
  45. arc_pie-0.1.6/src/pie/model/__init__.py +17 -0
  46. arc_pie-0.1.6/src/pie/model/evidence.py +86 -0
  47. arc_pie-0.1.6/src/pie/model/heads.py +36 -0
  48. arc_pie-0.1.6/src/pie/model/layers.py +52 -0
  49. arc_pie-0.1.6/src/pie/model/losses.py +151 -0
  50. arc_pie-0.1.6/src/pie/model/pie.py +304 -0
  51. arc_pie-0.1.6/src/pie/model/trunk.py +103 -0
  52. arc_pie-0.1.6/src/pie/predict.py +311 -0
  53. arc_pie-0.1.6/src/pie/prep/__init__.py +0 -0
  54. arc_pie-0.1.6/src/pie/prep/config.py +83 -0
  55. arc_pie-0.1.6/src/pie/prep/labels.py +738 -0
  56. arc_pie-0.1.6/src/pie/process/__init__.py +0 -0
  57. arc_pie-0.1.6/src/pie/process/config.py +88 -0
  58. arc_pie-0.1.6/src/pie/process/de.py +148 -0
  59. arc_pie-0.1.6/src/pie/process/io.py +113 -0
  60. arc_pie-0.1.6/src/pie/process/knockdown.py +290 -0
  61. arc_pie-0.1.6/src/pie/process/normalize.py +174 -0
  62. arc_pie-0.1.6/src/pie/process/run.py +76 -0
  63. arc_pie-0.1.6/src/pie/sources/__init__.py +0 -0
  64. arc_pie-0.1.6/src/pie/sources/chem_profiles.py +483 -0
  65. arc_pie-0.1.6/src/pie/sources/common.py +146 -0
  66. arc_pie-0.1.6/src/pie/sources/config.py +76 -0
  67. arc_pie-0.1.6/src/pie/sources/contract.py +323 -0
  68. arc_pie-0.1.6/src/pie/sources/curated_aliases.yaml +38 -0
  69. arc_pie-0.1.6/src/pie/sources/depmap.py +91 -0
  70. arc_pie-0.1.6/src/pie/sources/embed/__init__.py +0 -0
  71. arc_pie-0.1.6/src/pie/sources/embed/chemberta.py +152 -0
  72. arc_pie-0.1.6/src/pie/sources/embed/esm2.py +140 -0
  73. arc_pie-0.1.6/src/pie/sources/embed/openai.py +120 -0
  74. arc_pie-0.1.6/src/pie/sources/embed/qwen.py +135 -0
  75. arc_pie-0.1.6/src/pie/sources/registry.py +248 -0
  76. arc_pie-0.1.6/src/pie/sources/string.py +101 -0
  77. arc_pie-0.1.6/src/pie/sources/text/__init__.py +0 -0
  78. arc_pie-0.1.6/src/pie/sources/text/_http.py +67 -0
  79. arc_pie-0.1.6/src/pie/sources/text/context_file.py +50 -0
  80. arc_pie-0.1.6/src/pie/sources/text/contexts.py +650 -0
  81. arc_pie-0.1.6/src/pie/sources/text/drugs.py +541 -0
  82. arc_pie-0.1.6/src/pie/sources/text/genes.py +586 -0
  83. arc_pie-0.1.6/src/pie/sources/text/ncbi.py +510 -0
  84. arc_pie-0.1.6/src/pie/sources/text/tools.py +243 -0
  85. arc_pie-0.1.6/src/pie/sources/uniprot.py +140 -0
  86. arc_pie-0.1.6/src/pie/sources/verify.py +160 -0
  87. arc_pie-0.1.6/src/pie/train.py +506 -0
  88. arc_pie-0.1.6/src/pie/utils.py +333 -0
  89. arc_pie-0.1.6/tests/__init__.py +0 -0
  90. arc_pie-0.1.6/tests/conftest.py +40 -0
  91. arc_pie-0.1.6/tests/data/__init__.py +0 -0
  92. arc_pie-0.1.6/tests/data/test_datamodule.py +330 -0
  93. arc_pie-0.1.6/tests/data/test_dataset.py +193 -0
  94. arc_pie-0.1.6/tests/data/test_delta_p.py +165 -0
  95. arc_pie-0.1.6/tests/data/test_evidence.py +247 -0
  96. arc_pie-0.1.6/tests/data/test_preprocessed.py +230 -0
  97. arc_pie-0.1.6/tests/data/test_samplers.py +277 -0
  98. arc_pie-0.1.6/tests/data/test_splits.py +194 -0
  99. arc_pie-0.1.6/tests/fixtures/metrics/de_metrics_oracle.json +1 -0
  100. arc_pie-0.1.6/tests/fixtures/metrics/l1_parity.json +1 -0
  101. arc_pie-0.1.6/tests/fixtures.py +346 -0
  102. arc_pie-0.1.6/tests/model/__init__.py +0 -0
  103. arc_pie-0.1.6/tests/model/helpers.py +132 -0
  104. arc_pie-0.1.6/tests/model/test_evidence.py +83 -0
  105. arc_pie-0.1.6/tests/model/test_heads.py +59 -0
  106. arc_pie-0.1.6/tests/model/test_layers.py +78 -0
  107. arc_pie-0.1.6/tests/model/test_losses.py +193 -0
  108. arc_pie-0.1.6/tests/model/test_pie.py +285 -0
  109. arc_pie-0.1.6/tests/model/test_trunk.py +79 -0
  110. arc_pie-0.1.6/tests/pipeline.py +106 -0
  111. arc_pie-0.1.6/tests/prep/__init__.py +0 -0
  112. arc_pie-0.1.6/tests/prep/test_config.py +144 -0
  113. arc_pie-0.1.6/tests/prep/test_labels.py +634 -0
  114. arc_pie-0.1.6/tests/process/__init__.py +0 -0
  115. arc_pie-0.1.6/tests/process/conftest.py +64 -0
  116. arc_pie-0.1.6/tests/process/helpers.py +102 -0
  117. arc_pie-0.1.6/tests/process/test_cli.py +84 -0
  118. arc_pie-0.1.6/tests/process/test_config.py +202 -0
  119. arc_pie-0.1.6/tests/process/test_de.py +198 -0
  120. arc_pie-0.1.6/tests/process/test_io.py +110 -0
  121. arc_pie-0.1.6/tests/process/test_knockdown.py +216 -0
  122. arc_pie-0.1.6/tests/process/test_normalize.py +190 -0
  123. arc_pie-0.1.6/tests/process/test_run.py +147 -0
  124. arc_pie-0.1.6/tests/sources/embed/test_chemberta.py +63 -0
  125. arc_pie-0.1.6/tests/sources/embed/test_esm2.py +132 -0
  126. arc_pie-0.1.6/tests/sources/embed/test_openai.py +134 -0
  127. arc_pie-0.1.6/tests/sources/embed/test_qwen.py +91 -0
  128. arc_pie-0.1.6/tests/sources/helpers.py +15 -0
  129. arc_pie-0.1.6/tests/sources/inputs.py +430 -0
  130. arc_pie-0.1.6/tests/sources/test_chem_profiles.py +141 -0
  131. arc_pie-0.1.6/tests/sources/test_cli.py +114 -0
  132. arc_pie-0.1.6/tests/sources/test_common.py +116 -0
  133. arc_pie-0.1.6/tests/sources/test_config.py +46 -0
  134. arc_pie-0.1.6/tests/sources/test_contract.py +458 -0
  135. arc_pie-0.1.6/tests/sources/test_depmap.py +27 -0
  136. arc_pie-0.1.6/tests/sources/test_registry.py +361 -0
  137. arc_pie-0.1.6/tests/sources/test_registry_tools.py +209 -0
  138. arc_pie-0.1.6/tests/sources/test_string.py +35 -0
  139. arc_pie-0.1.6/tests/sources/test_uniprot.py +72 -0
  140. arc_pie-0.1.6/tests/sources/test_verify.py +152 -0
  141. arc_pie-0.1.6/tests/sources/text/fakes.py +56 -0
  142. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/56.0/CVCL_0186.json +1 -0
  143. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/56.0/CVCL_9771.json +1 -0
  144. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/56.0/CVCL_Y001.json +1 -0
  145. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/56.0/CVCL_Y002.json +1 -0
  146. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/56.0/CVCL_Y003.json +1 -0
  147. arc_pie-0.1.6/tests/sources/text/fixtures/cellosaurus/release-info.json +1 -0
  148. arc_pie-0.1.6/tests/sources/text/test_contexts.py +380 -0
  149. arc_pie-0.1.6/tests/sources/text/test_drugs.py +268 -0
  150. arc_pie-0.1.6/tests/sources/text/test_genes.py +393 -0
  151. arc_pie-0.1.6/tests/sources/text/test_http.py +87 -0
  152. arc_pie-0.1.6/tests/sources/text/test_ncbi.py +258 -0
  153. arc_pie-0.1.6/tests/test_assets.py +425 -0
  154. arc_pie-0.1.6/tests/test_cli.py +135 -0
  155. arc_pie-0.1.6/tests/test_config.py +668 -0
  156. arc_pie-0.1.6/tests/test_evaluate.py +188 -0
  157. arc_pie-0.1.6/tests/test_infer.py +84 -0
  158. arc_pie-0.1.6/tests/test_metrics.py +576 -0
  159. arc_pie-0.1.6/tests/test_package.py +36 -0
  160. arc_pie-0.1.6/tests/test_predict.py +199 -0
  161. arc_pie-0.1.6/tests/test_train.py +492 -0
  162. arc_pie-0.1.6/tests/test_utils.py +434 -0
  163. arc_pie-0.1.6/uv.lock +1957 -0
@@ -0,0 +1,42 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v6
13
+ - name: Check that the tag matches the package version
14
+ run: |
15
+ version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
16
+ test "v$version" = "$GITHUB_REF_NAME"
17
+ - run: uv build
18
+ - name: Smoke-test the wheel outside the source tree
19
+ run: |
20
+ uv venv /tmp/smoke --python 3.12
21
+ uv pip install --python /tmp/smoke/bin/python dist/*.whl
22
+ cd /tmp
23
+ /tmp/smoke/bin/pie --version
24
+ /tmp/smoke/bin/pie train --help
25
+ /tmp/smoke/bin/pie eval --help
26
+ - uses: actions/upload-artifact@v4
27
+ with:
28
+ name: dist
29
+ path: dist/
30
+
31
+ pypi:
32
+ needs: build
33
+ runs-on: ubuntu-latest
34
+ environment: pypi
35
+ permissions:
36
+ id-token: write
37
+ steps:
38
+ - uses: actions/download-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,23 @@
1
+ # Local environment
2
+ common.sh
3
+ .venv/
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[oc]
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+
12
+ # Tool caches
13
+ .pytest_cache/
14
+ .ruff_cache/
15
+ .mypy_cache/
16
+
17
+ # Training and evaluation outputs
18
+ *.ckpt
19
+ wandb/
20
+ lightning_logs/
21
+ outputs/
22
+ multirun/
23
+ .hydra/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,211 @@
1
+ # PIE
2
+
3
+ PIE predicts how a perturbation changes gene expression in a cell context. For each (context,
4
+ perturbation) pair and every measured gene it outputs the probability of differential expression
5
+ (`p_de`), the log2 fold change (`lfc_pred`) and the expression-level shift (`delta_p_pred`), from
6
+ knowledge-source embeddings of the perturbation, context and genes plus pooled training evidence.
7
+
8
+ ## Repository map
9
+
10
+ - `src/pie/configs/`: Hydra configs shipped in the package, one per command (`process.yaml`, `prep.yaml`, `sources.yaml`, `train.yaml`,
11
+ `eval.yaml`, `infer.yaml`), overlays in `process/dataset/`, `prep/dataset/`, `prep/label_format/`
12
+ and `experiment/` (the two canonical experiments).
13
+ - `src/pie/cli.py`: the `pie <command>` entry point (`process`, `prep`, `sources`, `train`, `eval`,
14
+ `infer`); the old `pie-<command>` scripts remain as deprecated aliases.
15
+ - On HF, not in the repo: split files in `arcinstitute/PIE_splits`
16
+ (`{"<dataset>.<context>": [perturbation, ...]}`), each dataset's context map in
17
+ `preprocessed/contexts.yaml` (context to Cellosaurus accession, Jiang stimulations) and
18
+ per-source perturbation aliases in `PIE_sources/<name>/aliases.yaml` (used only when a key is
19
+ missing). `src/pie/sources/curated_aliases.yaml` is the reviewed table: `pie sources` writes
20
+ each source's entries into its output dir, and runs saved before aliases moved read it.
21
+ - `src/pie/config.py`: strict schema of train/eval/infer (unknown keys are errors); `utils.py`:
22
+ `common.sh` loading, env checks, config composition, logging, determinism, hashing, atomic writes.
23
+ - `src/pie/assets.py`: local/HF asset resolver, revision pinning, selective downloads,
24
+ repository locks and completion manifests under `PIE_DATA_ROOT/hf/`.
25
+ - `src/pie/process/`: raw count h5ads to log1p expression h5ads and per-context DE parquets.
26
+ - `src/pie/prep/`: labels and expression h5ads to a preprocessed dir (`config.py` is its schema).
27
+ - `src/pie/sources/`: source format (`contract.py`), tool registry (`registry.py`), config schema
28
+ (`config.py`), text renderers (`text/`), embedders (`embed/`) and the UniProt, STRING, DepMap and
29
+ chemical-profile builders.
30
+ - `src/pie/data/`: preprocessed reader, splits, delta-p grid, evidence cache, samplers, datamodule.
31
+ - `src/pie/model/`: `PieModel` (Perceiver trunk, evidence module, heads) and the losses.
32
+ - `src/pie/metrics.py` (the scorer for validation and evaluation), `train.py` (Lightning module,
33
+ trainer, checkpoints), `predict.py` (shared by `evaluate.py` and `infer.py`).
34
+ - `tests/`: mirrors `src/pie/`.
35
+
36
+ ## Setup
37
+
38
+ ```bash
39
+ uv sync --frozen # runtime and dev tools
40
+ uv sync --frozen --extra sources # also the source-building tools
41
+ uv sync --frozen --extra process # also the dataset-processing DE stage
42
+ cp common.sh.example common.sh # then fill it in; common.sh is gitignored
43
+ ```
44
+
45
+ `common.sh` sets `WANDB_ENTITY`, `WANDB_PROJECT`, `PIE_DATA_ROOT`, `PIE_RUNS_ROOT`, `PIE_CACHE_DIR`
46
+ and, optionally, `NCBI_API_KEY` and `NCBI_EMAIL`. Values are literal (no `$VAR` or `~` expansion, no
47
+ inline comments). Every command loads the first of `$PIE_ENV_FILE`, `./common.sh` and
48
+ `~/.config/pie/common.sh`; variables already set in the environment win,
49
+ and a CLI stops at once when a variable it needs is missing. The text-embedding steps also need
50
+ `OPENAI_API_KEY`: export it in your own environment, never in `common.sh`.
51
+
52
+ ## CLI tools
53
+
54
+ Every command takes Hydra-style `key=value` overrides on its config in `src/pie/configs/`
55
+ (`pie process` → `process.yaml`, `pie prep` → `prep.yaml`, `pie sources` → `sources.yaml`,
56
+ `pie train` → `train.yaml`, `pie eval` → `eval.yaml`, `pie infer` → `infer.yaml`); `pie` lists the
57
+ commands and `pie <command> --help` prints the usage. Overlays are
58
+ picked with `dataset=<name>`, `label_format=<name>` or `experiment=<name>`, unknown keys are errors,
59
+ relative paths are relative to the current directory, and an existing output is an error unless you pass
60
+ `overwrite=true`.
61
+
62
+ Preprocessed dataset inputs (`data.preprocessed_dirs` in train; `preprocessed_dirs` in
63
+ eval/infer/sources) and model source inputs (`data.source_dirs`, `data.gene_text_dir`) accept
64
+ local paths or `hf://datasets/<owner>/<repo>[@revision]/<directory>` references. Bare repo IDs
65
+ are local paths; use the explicit URI for HF. The canonical experiment overlays pin full HF
66
+ commits for every dataset and source, including gene text. Dataset repos use `preprocessed/`;
67
+ `arcinstitute/PIE_sources` uses one directory per source name.
68
+
69
+ HF assets download only the selected directory to
70
+ `$PIE_DATA_ROOT/hf/datasets/<owner>/<repo>/<commit>/<directory>/`; download metadata, locks,
71
+ revision records, completion manifests and the Xet transfer cache also stay under this root.
72
+ Do not resolve HF references through `resolve_path` or turn them into `Path` before resolution:
73
+ use `resolve_asset(value, kind="preprocessed"|"source")`. Config composition and checkpoint
74
+ loading must stay free of network/download side effects. Training pins remote references before
75
+ saving config/checkpoints; resume replays the previous run's commits. An unpinned revision resolves
76
+ once per data root; use a new explicit commit for an update. Fully downloaded assets need no
77
+ network. `HF_HUB_OFFLINE=1` refuses missing/incomplete assets. Authenticate when needed with
78
+ `uv run hf auth login` or `HF_TOKEN` in the environment; never store tokens in configs/common.sh.
79
+
80
+ `pie process` turns raw count h5ads into the log1p expression h5ads and per-context DE parquets
81
+ that `pie prep` consumes: `dataset=<name>` selects a per-dataset overlay enabling the stages it
82
+ needs among `filter` (on-target knockdown removal), `normalize` (CP10k plus natural log1p) and `de`
83
+ (needs a GPU and the `process` extra).
84
+
85
+ `pie prep` builds one preprocessed dir per dataset from DE label tables and expression h5ads: give
86
+ `labels` (glob), `h5ad` (glob) and `output_dir`. `dataset=<name>` sets the h5ad obs columns (`obs.*`)
87
+ and the label rewrites; `label_format=table` (default) reads PIE label tables and
88
+ `label_format=pie_process` reads `pie process` DE parquets. `obs.pert_id_col` records each
89
+ perturbation's Ensembl id in `meta.json` (Replogle: `gene_id`), which `pie sources` uses. A label
90
+ context without h5ad or control cells is an error; `genes=<file>` fixes the gene axis;
91
+ `controls_only=true` writes only the gene axis, vocab and control means (inference on new
92
+ contexts). Without `genes` the derived axis can differ from a canonical one (Tahoe: 17845 vs. 18151
93
+ genes), so reproducing a canonical dir needs the gene list shipped with it. `contexts=<file>`
94
+ copies a context map into the dir as `contexts.yaml`; `pie sources` needs it for `context_text`.
95
+
96
+ ```bash
97
+ uv run pie prep dataset=replogle label_format=pie_process \
98
+ labels="$PIE_DATA_ROOT/replogle/de/*.parquet" \
99
+ h5ad="$PIE_DATA_ROOT/replogle/expression/*.h5ad" \
100
+ output_dir="$PIE_DATA_ROOT/replogle/preprocessed"
101
+ ```
102
+
103
+ `pie sources` builds knowledge sources into `<output_root>/<name>/` for the datasets in
104
+ `preprocessed_dirs`. `tools=[...]` adds and orders dependencies (`with_deps=false` builds only the
105
+ named tools); builder settings live under `options.*`; `mode=verify` reports per-dataset coverage
106
+ and, with `verify.reference=<dir>`, the difference to published sources. `prior_root` extends
107
+ earlier text sources instead of re-embedding them. `depmap_gene_effect` needs a manual download
108
+ (`options.depmap_csv`), the chemical sources need `options.drug_metadata`, and `ncbi_text` and
109
+ `esm2` need a GPU (`options.device`, default `cuda`). Context text reads
110
+ `<preprocessed dir>/contexts.yaml`; aliases come from `<source dir>/aliases.yaml`, plus an optional
111
+ multi-source file that you pass in the config (`verify.aliases`, `data.aliases_path`), loaded as
112
+ given.
113
+
114
+ ```bash
115
+ uv run pie sources \
116
+ "tools=[esm2,ncbi_text,string_space,depmap_gene_effect,context_text,perturbation_text,gene_text]" \
117
+ "preprocessed_dirs=[$PIE_DATA_ROOT/replogle/preprocessed]" \
118
+ options.depmap_csv=CRISPRGeneEffect.csv output_root="$PIE_DATA_ROOT/sources"
119
+ ```
120
+
121
+ `pie train` trains from `train.yaml` plus Hydra overrides (for example
122
+ `experiment=replogle_wdataset vars.fold=k562`). At setup it fits the delta-p grid on the training
123
+ rows, loads or builds the evidence cache and writes `data_stats.json`. A non-empty run dir is an
124
+ error unless you pass `resume=true` (continue from `last.ckpt`) or `overwrite=true`; resume keeps
125
+ the run's saved data paths (datasets, sources, splits) and logs any requested path it ignores.
126
+ `data.split_dir=<dir>` (local or `hf://`, holding `train.json` and `val.json`) trains on your own
127
+ splits. `logger.enabled=false` trains without wandb.
128
+
129
+ `pie eval` scores a checkpoint on a split file and writes `<run_dir>/eval/<row_set>/metrics_<ckpt>.csv`
130
+ (one row per context plus an `all` row) and `granular_<ckpt>.csv` (one row per pair);
131
+ `save_predictions=true` also writes the predictions.
132
+
133
+ `pie infer` predicts without labels and writes one parquet row per pair (`p_de`, `lfc_pred`,
134
+ `delta_p_pred`; the gene axis is in the file metadata). For new contexts, build a
135
+ `controls_only=true` dir with `pie prep` and pass a query file in split format.
136
+
137
+ ```bash
138
+ uv run pie infer experiment_name=replogle_wdataset/k562 ckpt=best_auprc \
139
+ rows_kind=query rows_path=query.json \
140
+ "preprocessed_dirs=[$PIE_DATA_ROOT/my_screen/preprocessed]" output_path=predictions.parquet
141
+ ```
142
+
143
+ ## End-to-end flow
144
+
145
+ raw count h5ads → `pie process` (optional: knockdown filter, log1p normalize, DE) → labels + h5ad
146
+ → `pie prep` → `pie sources tools=[…]` → `pie train` (grid and evidence built at setup)
147
+ → `pie eval` / `pie infer`.
148
+
149
+ ```
150
+ $PIE_DATA_ROOT/<dataset>/counts/ count h5ads (knockdown-filtered when pie process filters)
151
+ $PIE_DATA_ROOT/<dataset>/expression/ log1p expression h5ads
152
+ $PIE_DATA_ROOT/<dataset>/de/ per-context DE parquets
153
+ $PIE_DATA_ROOT/<dataset>/preprocessed/ meta.json and the .npy arrays
154
+ $PIE_DATA_ROOT/sources/<name>/ meta.json, embeddings.npy [offsets.npy, descriptions.json]
155
+ $PIE_RUNS_ROOT/<experiment_name>/ config.yaml, data_stats.json, best_auprc.ckpt, last.ckpt,
156
+ eval/<row_set>/
157
+ $PIE_CACHE_DIR/evidence/<key>/ evidence, keyed by the data, train split and settings
158
+ $PIE_CACHE_DIR/http/, embed/ downloads and resumable embedding progress
159
+ ```
160
+
161
+ ## Experiments
162
+
163
+ Evaluate `best_auprc.ckpt` (maximum `val/binary_auprc`) on every test set.
164
+ Both recipes fetch their pinned preprocessed datasets and knowledge sources automatically at
165
+ runtime; `uv sync --frozen` is sufficient to use them. For distributed runs, a shared
166
+ `PIE_DATA_ROOT` lets repository locks coordinate downloads across ranks and nodes. With local
167
+ data roots, each node downloads its own assets. Run directories must still use shared storage.
168
+
169
+ **replogle_wdataset**: Replogle only, four folds (hepg2, jurkat, k562, rpe1). A fold trains and
170
+ validates on the other three cell lines and is tested in three settings: `unseen_ctx` (held-out
171
+ line, seen perturbations), `unseen_pert` (seen lines, unseen perturbations) and `unseen_ctx_pert`.
172
+ Hardware: 2 GPUs per fold (H100 80GB: about 1.2 h and 20 GiB per GPU); eval on 1 GPU.
173
+
174
+ ```bash
175
+ SPLITS=hf://datasets/arcinstitute/PIE_splits@396ab9563175ee887750c9eed7ccaea6f5fdbf50
176
+ for fold in hepg2 jurkat k562 rpe1; do
177
+ uv run pie train experiment=replogle_wdataset vars.fold=$fold
178
+ for setting in unseen_ctx unseen_pert unseen_ctx_pert; do
179
+ uv run pie eval experiment_name=replogle_wdataset/$fold ckpt=best_auprc \
180
+ split_path=$SPLITS/replogle_wdataset/$setting/$fold/test.json row_set=$setting
181
+ done
182
+ done
183
+ ```
184
+
185
+ **replogle_xdataset**: trains on Tahoe, Jiang, ARC VCC 25 and Orion (weights 0.68, 0.01, 0.01, 0.3)
186
+ and is tested zero-shot on Replogle, which stays in the config with weight 0 to fix the gene axis.
187
+ `test_seen` holds the pairs whose perturbation is in `train.json`, `test_unseen` the rest; the
188
+ per-context rows of each metrics file are the per-cell-line results. Hardware: 2 nodes × 4 GPUs
189
+ (H100 80GB: about 4.5 h, peak 78.5 GiB per GPU, about 46 GiB host RAM per rank), a run dir on
190
+ shared storage; eval on 1 GPU. Run `torchrun` on each node with its rank and one fresh `RDZV_ID`
191
+ shared by both nodes:
192
+
193
+ ```bash
194
+ SPLITS=hf://datasets/arcinstitute/PIE_splits@396ab9563175ee887750c9eed7ccaea6f5fdbf50
195
+ uv run torchrun --nnodes=2 --nproc-per-node=4 --node-rank=<0|1> \
196
+ --master-addr=<node-0 host> --master-port=29500 --rdzv-id="$RDZV_ID" \
197
+ --no-python pie train experiment=replogle_xdataset
198
+ for rows in test_seen test_unseen; do
199
+ uv run pie eval experiment_name=replogle_xdataset ckpt=best_auprc \
200
+ split_path=$SPLITS/replogle_xdataset/$rows.json row_set=$rows
201
+ done
202
+ ```
203
+
204
+ ## Conventions
205
+
206
+ - `tests/` mirrors `src/pie/`; tests run on CPU with synthetic fixtures: `uv run pytest -q`.
207
+ - Lint with `uv run ruff check .` (100 columns); type-check with `uv run mypy src`.
208
+ - Direct dependencies use version ranges in `pyproject.toml` (lower bound = the tested version);
209
+ `uv.lock` holds the exact versions and is committed (`uv lock`).
210
+ - Configs are strict: each value lives once in YAML, and a new key needs a schema field.
211
+ - Never commit machine-specific paths, `common.sh`, API keys or other secrets.