if-split 0.1.0__tar.gz → 0.3.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.
- if_split-0.3.0/CHANGELOG.md +88 -0
- {if_split-0.1.0 → if_split-0.3.0}/CLAUDE.md +14 -6
- if_split-0.3.0/PKG-INFO +546 -0
- {if_split-0.1.0 → if_split-0.3.0}/PLAN.md +44 -14
- if_split-0.3.0/README.md +527 -0
- {if_split-0.1.0 → if_split-0.3.0}/config/default.yaml +31 -5
- if_split-0.3.0/config/masterclass.yaml +51 -0
- if_split-0.3.0/examples/IF-Split-2026.07.14/README.md +118 -0
- if_split-0.3.0/examples/IF-Split-2026.07.14/STATS.txt +28 -0
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/config.yaml +8 -4
- if_split-0.3.0/examples/IF-Split-2026.07.14/manifest.json +167 -0
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/test/metal_test.json +193 -157
- if_split-0.1.0/examples/IF-Split-2026.05.31/test/nucleotide_test.json → if_split-0.3.0/examples/IF-Split-2026.07.14/test/nucleic_acid_test.json +9 -2
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/test/small_molecule_test.json +3118 -292
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/test.json +247 -84
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/train.json +1550 -289
- {if_split-0.1.0/examples/IF-Split-2026.05.31 → if_split-0.3.0/examples/IF-Split-2026.07.14}/val.json +222 -117
- {if_split-0.1.0 → if_split-0.3.0}/pyproject.toml +1 -1
- if_split-0.3.0/scripts/audit_nico_histag.py +206 -0
- if_split-0.3.0/scripts/consume_split.py +157 -0
- if_split-0.3.0/scripts/eval_metal_tiering.py +122 -0
- if_split-0.3.0/scripts/eval_sm_tiering.py +73 -0
- if_split-0.3.0/scripts/eval_structural_clustering.py +52 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/__init__.py +1 -1
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/cli.py +252 -52
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/cluster.py +46 -1
- if_split-0.3.0/src/ifsplit/config.py +312 -0
- if_split-0.3.0/src/ifsplit/dataset.py +200 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/enumerate.py +22 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/hydrate.py +17 -2
- if_split-0.3.0/src/ifsplit/ligands.py +477 -0
- if_split-0.3.0/src/ifsplit/manifest.py +713 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/parse.py +62 -5
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/rcsb.py +24 -2
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/schema.py +104 -2
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/split.py +116 -6
- if_split-0.3.0/tests/test_config.py +197 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/test_download.py +45 -0
- if_split-0.3.0/tests/test_enumerate_lock.py +298 -0
- if_split-0.3.0/tests/test_ligands.py +647 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/test_loader.py +60 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/test_pipeline.py +277 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/test_schema.py +101 -0
- {if_split-0.1.0 → if_split-0.3.0}/uv.lock +1 -1
- if_split-0.1.0/PKG-INFO +0 -312
- if_split-0.1.0/README.md +0 -293
- if_split-0.1.0/examples/IF-Split-2026.05.31/README.md +0 -80
- if_split-0.1.0/examples/IF-Split-2026.05.31/STATS.txt +0 -20
- if_split-0.1.0/examples/IF-Split-2026.05.31/manifest.json +0 -129
- if_split-0.1.0/src/ifsplit/config.py +0 -146
- if_split-0.1.0/src/ifsplit/dataset.py +0 -112
- if_split-0.1.0/src/ifsplit/ligands.py +0 -267
- if_split-0.1.0/src/ifsplit/manifest.py +0 -417
- if_split-0.1.0/tests/test_config.py +0 -106
- if_split-0.1.0/tests/test_enumerate_lock.py +0 -101
- if_split-0.1.0/tests/test_ligands.py +0 -247
- {if_split-0.1.0 → if_split-0.3.0}/.github/workflows/ci.yml +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/.github/workflows/publish.yml +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/.gitignore +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/.python-version +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/LICENSE +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/data/cache/.gitkeep +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/data/out/.gitkeep +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/__main__.py +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/src/ifsplit/download.py +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/conftest.py +0 -0
- {if_split-0.1.0 → if_split-0.3.0}/tests/test_integration.py +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to IF-Split are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
|
|
5
|
+
follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
The **split is always computed from metadata + sequences only** — `build` never
|
|
8
|
+
downloads structure coordinates. That invariant holds across every release below.
|
|
9
|
+
|
|
10
|
+
## [0.3.0] — 2026-07-14
|
|
11
|
+
|
|
12
|
+
A large release: fold-honest splitting, split-output certification, a two-corpus
|
|
13
|
+
training model, a metadata-only curation overhaul, and offline re-derivability.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Fold-level structural leakage control** (opt-in `structural_clustering`:
|
|
18
|
+
`off` | `cath` | `ecod` | `scop2`). Same-fold protein chains are union-merged into
|
|
19
|
+
one leakage-safe component in addition to shared sequence clusters, so a fold cannot
|
|
20
|
+
straddle train/test — using RCSB's precomputed CATH/ECOD/SCOP2 classifications
|
|
21
|
+
(metadata only, no coordinates).
|
|
22
|
+
- **Balance-aware split strategy** (`split_strategy: balanced`). Caps dominant folds
|
|
23
|
+
to train and fills val/test to their *entry* targets from the fold tail, restoring
|
|
24
|
+
~80/10/10 by entries with thousands of held-out folds. `config/masterclass.yaml`
|
|
25
|
+
ships the fold-honest recipe (`scop2` + `balanced`).
|
|
26
|
+
- **Split-output certification.** The `@2` `dataset.lock` records `split_sha256` (a
|
|
27
|
+
hash of the entry→split partition); `verify` re-derives Stages 3–6 and certifies the
|
|
28
|
+
split *output* reproduced, not just the Stage-1 candidate set.
|
|
29
|
+
- **Two training corpora from one split**: all kept structures as design *backbones*,
|
|
30
|
+
plus a functional-ligand *conditioning-target* corpus (`targets.jsonl`, one row per
|
|
31
|
+
ligand keyed to entry + split + class + tier). `SplitView` exposes both views.
|
|
32
|
+
- **Offline `resplit`** (`if-split resplit --candidates candidates.jsonl --config X`):
|
|
33
|
+
re-derives Stages 3–7 from a cached snapshot with no RCSB — ablate curation /
|
|
34
|
+
clustering / split settings, or tighten a filter, in seconds instead of
|
|
35
|
+
re-enumerating the PDB. The lock records `source` (`build` | `resplit`).
|
|
36
|
+
- **Offline `verify`** (`verify LOCK --candidates candidates.jsonl`): integrity-check
|
|
37
|
+
a distributed dataset with no network; a corrupt candidates file is reported as an
|
|
38
|
+
integrity failure. A `resplit` lock is steered to offline verification.
|
|
39
|
+
- **Per-method resolution caps** (`resolution_max_A_by_method`) and a **cryo-EM
|
|
40
|
+
map-fit floor** (`min_em_backbone_inclusion`, wiring in the previously-unused
|
|
41
|
+
`em_backbone_inclusion` metric). Resolution is now re-derived in Stage 3, so the cut
|
|
42
|
+
is auditable from `candidates.jsonl` and tightenable offline.
|
|
43
|
+
- **Opt-in sequence-usability floor** (`min_modeled_residues`) and an always-on drop of
|
|
44
|
+
empty / all-`X` (poly-UNK) protein chains, which carry no learnable label.
|
|
45
|
+
- RCSB **metal-binding annotations** (GO/InterPro/Pfam) captured to rescue native
|
|
46
|
+
metalloenzymes; `if-split spec` to emit a portable, self-identifying split spec.
|
|
47
|
+
|
|
48
|
+
### Changed / curation
|
|
49
|
+
|
|
50
|
+
- **Metal tiering**: heavy-atom / lanthanide **phasing derivatives** (Hg/Au/Pt/Pb/Tl/…)
|
|
51
|
+
demoted to `ambiguous` (reported, recoverable) rather than counted as functional
|
|
52
|
+
metal sites; inorganic **Fe-S / metal-oxo / FeMo clusters** (SF4/FES, the OEC) now
|
|
53
|
+
classed `metal`; native Ni/Co (and heavy/lanthanide) sites rescued via annotation,
|
|
54
|
+
affinity, or subject-of-investigation. The lone-Ni/Co His-tag figure was corrected
|
|
55
|
+
(~96% → ~82%).
|
|
56
|
+
- **Glycans** (RCSB CCD `type` = *saccharide*) with no measured affinity are tiered
|
|
57
|
+
`glycan` (decorative / detergent), recoverable via an opt-in tier — not counted as
|
|
58
|
+
small-molecule conditioning targets.
|
|
59
|
+
- **Small molecules**: a measured binding affinity now overrides the additive
|
|
60
|
+
blacklist, so a blacklisted comp that is the real measured ligand stays functional.
|
|
61
|
+
- **Nucleic acids**: `is_nucleic` now recognizes the `NA-hybrid` polymer type; the
|
|
62
|
+
ligand class was renamed `nucleotide` → `nucleic_acid`.
|
|
63
|
+
- The size cap keeps `< 6000` residues correctly (`> max_total_residues`, not `>=`).
|
|
64
|
+
- Adding a resolution-less method (NMR/SAXS) now warns instead of silently returning
|
|
65
|
+
zero entries.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- `verify` warns (rather than fails) on a version-only lock mismatch.
|
|
70
|
+
- `fetch` reads split id-lists from the manifest directory, not the current directory.
|
|
71
|
+
- `identity_threshold` is validated against RCSB's precomputed cluster levels
|
|
72
|
+
(30/50/70/90/95/100) so an unsupported level can't silently disable clustering.
|
|
73
|
+
- A bound halide is tiered a counterion, not a functional small molecule.
|
|
74
|
+
|
|
75
|
+
## [0.2.0] — 2026
|
|
76
|
+
|
|
77
|
+
- Recover non-covalently bound cofactors (FAD/NAD/FMN/NADP, inhibitors) via RCSB's
|
|
78
|
+
`is_subject_of_investigation` flag.
|
|
79
|
+
- Harden Ni/Co metal curation against His-tags absent from the deposited sequence.
|
|
80
|
+
- Shareable split spec (`if-split spec`) and a self-identifying config header.
|
|
81
|
+
- Rename the ligand class `nucleotide` → `nucleic_acid`; PyPI/CI badges + install docs.
|
|
82
|
+
|
|
83
|
+
## [0.1.0] — 2026
|
|
84
|
+
|
|
85
|
+
- Initial release: a reproducible, date-pinned, ligand-aware train/val/test splitter
|
|
86
|
+
for the PDB. Enumerate → filter → tier ligands → cluster (union-find, leakage-safe)
|
|
87
|
+
→ deterministic split → manifest + lock, all from RCSB Search + Data API metadata
|
|
88
|
+
(no coordinates). Optional `fetch` downloads structures for a built split.
|
|
@@ -10,7 +10,8 @@ usage. This file is the orientation for working in the repo.
|
|
|
10
10
|
**The split is computed from metadata + sequences only — `build` never downloads
|
|
11
11
|
structure coordinates.** Everything needed (resolution, method, release date,
|
|
12
12
|
residue counts, per-entity sequences, ligand chem-comp + bound-component signals,
|
|
13
|
-
RCSB cluster membership
|
|
13
|
+
RCSB sequence-cluster membership, and CATH/ECOD/SCOP2 structural classifications)
|
|
14
|
+
comes from the RCSB Search + Data APIs. Coordinates
|
|
14
15
|
(mmCIF) are large and only needed downstream, so `fetch` (Stage 2) is optional.
|
|
15
16
|
Keep it that way: do not add coordinate access to the build path.
|
|
16
17
|
|
|
@@ -36,6 +37,8 @@ wsl -d ubuntu bash -lc 'cd ~/projects/IF-Split && export PATH="$HOME/.local/bin:
|
|
|
36
37
|
uv run ruff check . # lint (must pass)
|
|
37
38
|
uv run ruff format . # format
|
|
38
39
|
uv run if-split build --limit 50 --out /tmp/ifs # dev build (small, live RCSB)
|
|
40
|
+
uv run if-split build --config config/masterclass.yaml --out /tmp/mc # fold-honest split (scop2 + balanced)
|
|
41
|
+
uv run if-split resplit --candidates data/out/candidates.jsonl --config X.yaml --out /tmp/rs # re-derive Stages 3-7 offline (no RCSB)
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
- `uv sync` sets up the env; `uv sync --extra mlops` adds pyarrow for `fetch`'s
|
|
@@ -47,17 +50,22 @@ uv run if-split build --limit 50 --out /tmp/ifs # dev build (small, live RCSB)
|
|
|
47
50
|
|
|
48
51
|
`enumerate.py`+`rcsb.py` (Stage 1, Search+Data API → candidates.jsonl) →
|
|
49
52
|
`parse.py` (3, metadata filters) → `ligands.py` (4, confidence tiering) →
|
|
50
|
-
`cluster.py` (5, union-find components
|
|
53
|
+
`cluster.py` (5, union-find components: sequence + optional fold-level structural
|
|
54
|
+
clustering) → `split.py` (6, split assignment: `hash` | `balanced`) →
|
|
51
55
|
`manifest.py` (7, lock + manifest + registry, verify/stats) → `dataset.py` (8,
|
|
52
|
-
loader). `download.py`+`hydrate.py` are the optional Stage 2 `fetch`.
|
|
56
|
+
loader). `download.py`+`hydrate.py` are the optional Stage 2 `fetch`. `cli.py`'s
|
|
57
|
+
`resplit` re-runs Stages 3-7 from a cached `candidates.jsonl` (no Stage 1) via the
|
|
58
|
+
shared `_run_pipeline`; `verify --candidates` does the same for offline checking.
|
|
53
59
|
|
|
54
60
|
Invariants that must not regress:
|
|
55
61
|
- **Determinism:** same config → byte-identical `manifest.json` (no wall-clock
|
|
56
62
|
fields). `test_manifest_is_deterministic` guards this.
|
|
57
63
|
- **No cross-split leakage:** sequence clusters joined by a shared multi-chain
|
|
58
|
-
entry
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
entry (and, with `structural_clustering` on, by a shared fold superfamily) are
|
|
65
|
+
union-find–merged into one component; a component maps to exactly one split, so
|
|
66
|
+
overlap is impossible by construction. This holds for both split strategies
|
|
67
|
+
(`hash` and `balanced`, which only chooses *which* split a whole component lands
|
|
68
|
+
in). `check_no_leakage` is a real invariant (not a tautology) — keep it that way.
|
|
61
69
|
- **Growth stability:** a cluster/component's split is `hash(salt + canonical_key)`
|
|
62
70
|
into cumulative fractions, keyed on the global-min member id (not RCSB's volatile
|
|
63
71
|
integer id). A larger snapshot only *adds* components; `splits.registry.json`
|