Skip to content

Commit d09b9dc

Browse files
committed
add benchmarking numbers
1 parent 5b36b72 commit d09b9dc

2 files changed

Lines changed: 67 additions & 40 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ dist/
88
.venv/
99
venv/
1010
.DS_Store
11+
12+
13+
examples/rnp100_ab/*

README.md

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,38 @@ Implements the two mechanisms used by Boltz-1x and Protenix-v2:
1212
* **Feynman-Kac steering** — sample a particle population and resample it
1313
toward low-energy trajectories.
1414

15-
The engine is host-agnostic. It consumes a `ChemicalContext` of atom-indexed
16-
constraint tensors and exposes two pure functions (`guide`, `resample_indices`);
17-
it never sees the denoiser, the noise schedule, or the model. Adding a host
18-
means writing one adapter.
15+
## Benchmark on OF3p2
16+
17+
OpenFold3-p2 (155k) on the 100 smallest Runs N' Poses post-2025 targets, 1 seed
18+
× 5 samples, guidance only (20 GD steps), scored with PXMeter. Rates are over
19+
the 103 ligand chains, each represented by its **top-1 sample ranked by
20+
`chain_pair_iptm`**.
21+
22+
| | baseline | steered |
23+
| --- | --- | --- |
24+
| PoseBusters valid | 66.0% | **89.3%** |
25+
| PB valid & ligand RMSD < 2 Å & lDDT-PLI > 0.8 | 38.8% | **47.6%** |
26+
| ligand RMSD < 2 Å & lDDT-PLI > 0.8 | 49.5% | 50.5% |
27+
| wall clock, 4× GH200 | 9m56s | 17m11s (**1.73×**) |
28+
29+
Steering fixes chemistry without moving docking accuracy: paired ligand by
30+
ligand it fixed 25 validity failures and broke 1 (McNemar p < 1e-4), while
31+
accuracy moved by a net +1 (p = 1.0). Nearly all of the gain is sterics —
32+
`minimum_distance_to_protein` failures fall 141 → 3 across all 515 poses;
33+
chirality is second (31 → 13).
34+
35+
Caveat: 10 of 161 multi-atom ligand chains were skipped because RDKit rejected
36+
the molecule rebuilt from `atom_array` (valence errors on quaternary nitrogen
37+
and boron, which need a formal charge the rebuild does not assign), leaving 7 of
38+
the 100 targets unsteered and still counted in the steered arm.
39+
40+
41+
# TODOs
42+
43+
- Test out the FK steering to see how much better / slower that is
44+
- See how valency errors can be overcome (possibly some change to featureization is needed?)
45+
46+
# Usage
1947

2048
## Install
2149

@@ -24,34 +52,41 @@ pip install -e . # engine only, no folding model needed
2452
pip install -e '.[openfold3]' # with the OpenFold3 adapter
2553
```
2654

55+
**Already have OpenFold3 installed?** Install foldsteer into that same
56+
environment with `pip install -e . --no-deps` — it must share the interpreter
57+
with OF3 (see below), and `--no-deps` keeps pip from touching your pinned
58+
torch. Nothing about the OF3 install changes; foldsteer imports it lazily and
59+
patches at runtime. You need OF3 >= 0.4.5 (or any `main` carrying
60+
`SampleDiffusion._sample_rollout`; at tag 0.4.4 that loop is still inlined in
61+
`forward` and the adapter will raise `AttributeError`).
62+
2763
## Use with OpenFold3
2864

29-
Under OF3's runner Lightning builds the model itself, so patch the class before
30-
inference and invoke the CLI **in the same process**:
65+
From the command line, `examples/run_of3_steered.py` does the patches OF3 inference to use the steering.
66+
It passes everything after `--` straight to `run_openfold
67+
predict`, so the steered and unsteered arms take identical OF3 arguments:
3168

32-
```python
33-
from foldsteer import default_config
34-
from foldsteer.adapters.openfold3 import patch_sample_diffusion_class
35-
from openfold3.run_openfold import cli
36-
37-
unpatch = patch_sample_diffusion_class(default_config(fk_steering=False))
38-
cli.main(["predict", "--query-json", ..., "--output-dir", ...],
39-
standalone_mode=False)
40-
unpatch()
69+
```bash
70+
python examples/run_of3_steered.py --steer --num-gd-steps 20 \
71+
--stats-json steering_stats.json -- \
72+
--query_json examples/query_protein_ligand.json \
73+
--inference_ckpt_path /path/to/of3-p2-155k.pt \
74+
--num_diffusion_samples 5 \
75+
--use_msa_server false --use_templates false \
76+
--output_dir out_steered
4177
```
4278

43-
If you already hold the model object, patch that instance instead:
44-
45-
```python
46-
from foldsteer.adapters.openfold3 import patch_sample_diffusion
47-
48-
unpatch = patch_sample_diffusion(model, default_config(num_particles=3))
49-
```
79+
Swap `--steer` for `--no-steer` to get the baseline. **Always check the
80+
`[foldsteer] targets=... ligand_chains=... guided_steps=...` line it prints**: a
81+
run that found no steerable ligand produces output indistinguishable from an
82+
unsteered one.
5083

51-
Guidance only is the default above: it is the cheaper half and carries most of
52-
the benefit. Feynman-Kac steering reuses OF3's rollout-sample axis as the
53-
particle axis, so `num_diffusion_samples` must be a multiple of `num_particles`
54-
and fewer structures come back than were sampled.
84+
Guidance only is the default: it is the cheaper half and carries the benefit
85+
measured below. **Feynman-Kac steering has not been tested end-to-end against a
86+
real model** — it is exercised only by unit tests against a mock sampler. It
87+
also reuses OF3's rollout-sample axis as the particle axis, so
88+
`num_diffusion_samples` must be a multiple of `num_particles` and fewer
89+
structures come back than were sampled.
5590

5691
A complete runnable example — query JSON, both arms, and how to confirm
5792
steering actually fired — is in [`examples/`](examples/README.md).
@@ -68,18 +103,6 @@ engine = SteeringEngine(default_config(), ctx)
68103
coords = coords + engine.guide(coords, t=0.5) # t: 1 = noisy, 0 = clean
69104
```
70105

71-
## Validation
72-
73-
![validation](docs_validation.png)
74-
75-
**(a)** Guidance relaxes a distorted ligand. **(b)** Ablation over 6 seeds
76-
against a mock sampler reproducing OpenFold3's rollout contract. **(c)** The
77-
package detects the failure modes Protenix-v2 reports slipping past standard
78-
PoseBusters validity; valid geometry scores exactly zero.
79-
80-
Every analytic gradient is verified against autograd to ~1e-15. See
81-
[DESIGN.md](DESIGN.md) §7 for the full results and caveats.
82-
83106
## Potentials
84107

85108
`BoundsMatrixPotential`, `VDWOverlapPotential`, `ChiralAtomPotential`,
@@ -106,5 +129,6 @@ OpenFold3 `AtomArray` reconstruction path has now been exercised against real
106129
OF3 inference input — which turned up three silent failures in it (integer
107130
`MoleculeType` ids, Kekulé order on aromatic bonds, stereo perceived from
108131
`ref_pos` rather than the not-yet-predicted `coord`), all fixed and pinned by
109-
`tests/test_of3_extraction.py`. Steering quality itself has not been benchmarked
110-
at scale from this repo.
132+
`tests/test_of3_extraction.py`. Guidance is now benchmarked against real OF3
133+
inference (see Benchmark above); Feynman-Kac steering is not, and the formal
134+
charge gap in the `AtomArray` rebuild is the clearest outstanding fix.

0 commit comments

Comments
 (0)