@@ -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
2452pip 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
5691A complete runnable example — query JSON, both arms, and how to confirm
5792steering actually fired — is in [ ` examples/ ` ] ( examples/README.md ) .
@@ -68,18 +103,6 @@ engine = SteeringEngine(default_config(), ctx)
68103coords = 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
106129OF3 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