RFOptimization: A Practical Guide to Protein Design Refinement
A designed protein can look plausible and still fail a structure-based filter. RFOptimization (RFO) starts with an existing molecular complex and searches for sequence changes that improve its predicted properties. This guide explains the refinement loop, shows how to set up a small trial, and provides a Python checker for completed cycles.
Sources were checked on October 8, 2026. The method comes from Zhang and colleagues’ RFOptimization preprint, first posted in September 2026. The installation and model commands below were checked against the released source; GPU optimization and the paper’s benchmarks were not run for this article. The standalone records checker and its synthetic example were tested locally.
1. What RFOptimization changes
RFO is a training-free refinement framework: it uses pretrained models without fitting a new set of model weights. Sequence optimization can still use gradients. The preprint combines gradient-guided mutation search with structure prediction and MPNN redesign, then uses AlphaFold3 as a separate final evaluator. Its reported improvements concern in silico filtering; they do not establish experimental binding for every passing design. See the original abstract.
In the released workflow, each cycle chooses one of two branches:
| Stage | Role |
|---|---|
| RF3 branch | Guide sequence optimization with RoseTTAFold3 |
| Boltz branch | Predict a structure for the current sequence with Boltz-2 |
| External MPNN | Redesign unfixed residues in chain A, then pass the complex to the next cycle |
| Separate evaluation | Assess selected designs independently after optimization |
backprop_fraction: 0.5 is a probability for choosing RF3 at each cycle. It does not guarantee alternating branches or an equal split in a short run. Values of 1 and 0 force RF3 and Boltz respectively; MPNN remains part of both branches.
Unlike FlashPPI’s interaction screening, this workflow changes a supplied design. Keep the initial complex so that you can compare the starting sequence, every proposed sequence, and the final candidates under the same evaluation procedure.
2. Prepare the input and model environments
Use a Linux machine with an NVIDIA GPU and a working Conda installation. The driver/RF3, MPNN, and Boltz environments are separate. Keep the commands below in the same shell so their exported paths remain available. The installation guide is the reference for CUDA compatibility and model assets.
The designed protein must be chain A, with standard amino acids and complete N, CA, C, and O backbone atoms. In a two-protein example, chain B is the partner. Check chain IDs and residue completeness before launching; the RF3 branch’s MPNN mask uses contacts with chain B. Paths in the run YAML resolve from the working directory. See the cycling input conventions.
Start with a new working directory. This pins the Foundry source inspected for the tutorial:
mkdir rfo-tutorial
cd rfo-tutorial
export RFO_WORK="$PWD"
git clone https://github.com/RosettaCommons/foundry.git
cd foundry
git checkout 94c78ab125c6ef2cc7d1226de86d1bd578bf8bee
conda create -n rfo-driver python=3.12 -y
conda activate rfo-driver
python -m pip install -e '.[rf3,rfo]'
foundry install rf3
export RF3_CKPT="$HOME/.foundry/checkpoints/rf3_foundry_01_24_latest_remapped.ckpt"
rfo --help
The public RF3 checkpoint must support confidence prediction and include its training configuration. The installation guide notes that historical research configurations used other checkpoints. This recipe is a software trial, not a claim to reproduce the paper’s experiments. Consult the RF3 setup documentation for model-specific resources.
Install the upstream LigandMPNN repository, which supplies the run.py interface used for both ProteinMPNN and LigandMPNN:
cd "$RFO_WORK"
git clone https://github.com/dauparas/LigandMPNN.git
cd LigandMPNN
conda create -n rfo-mpnn python=3.11 -y
conda activate rfo-mpnn
python -m pip install -r requirements.txt
bash get_model_params.sh ./model_params
export MPNN_REPO="$PWD"
export MPNN_PYTHON="$CONDA_PREFIX/bin/python"
export MPNN_MODEL_TYPE=protein_mpnn
"$MPNN_PYTHON" "$MPNN_REPO/run.py" --help
git rev-parse HEAD > "$RFO_WORK/mpnn-commit.txt"
python -m pip freeze > "$RFO_WORK/mpnn-packages.txt"
Use the upstream CUDA/PyTorch requirements appropriate to your machine. RFO’s adapter does not use Foundry’s bundled mpnn command. Install Boltz in its own environment, then return to the driver:
conda create -n rfo-boltz python=3.12 -y
conda activate rfo-boltz
python -m pip install 'boltz[cuda]'
export BOLTZ_EXECUTABLE="$CONDA_PREFIX/bin/boltz"
export BOLTZ_CACHE="$HOME/.cache/boltz"
"$BOLTZ_EXECUTABLE" predict --help
python -m pip freeze > "$RFO_WORK/boltz-packages.txt"
conda activate rfo-driver
cd "$RFO_WORK/foundry/models/rfo"
python -m pip freeze > "$RFO_WORK/driver-packages.txt"
These commands record the external MPNN commit and installed package versions; they do not lock the whole dependency graph. Also retain checkpoint filenames and checksums, your input structure, and the actual run configuration. Boltz may download model assets on first use, so allow for that separately from optimization time.
3. Check each branch before a mixed run
From foundry/models/rfo, use the repository’s example complex to exercise the two branches independently:
rfo --config src/rfo/cycling/configs/example.yaml \
--backprop-fraction 1 --total-cycles 1 \
--out-dir outputs/smoke-rf3
rfo --config src/rfo/cycling/configs/example.yaml \
--backprop-fraction 0 --total-cycles 1 \
--out-dir outputs/smoke-boltz
Use a fresh output directory for each run. The driver implementation initializes the records file at the start; reusing a directory can overwrite records and leave older artifacts alongside newer ones.
After both one-cycle runs succeed, save this as tutorial.yaml in the same directory:
input_structure: ./src/rfo/cycling/examples/example_complex.cif
out_dir: ./outputs/tutorial-mixed
backprop_fraction: 0.5
total_cycles: 3
mpnn_num_seqs: 1
mpnn_temperature: 0.1
seed: 42
Run it and inspect the records:
rfo --config tutorial.yaml
python -m json.tool outputs/tutorial-mixed/example_complex_records.json
The CLI lets command-line flags override YAML settings. Explicit assets: settings override environment variables. This example uses the exported paths and intentionally omits target-specific MSAs and templates. Boltz runs in single-sequence mode without an MSA; a configured target MSA changes the experiment and should be recorded.
This is a three-cycle integration trial. It is not a recommended optimization budget for every protein, and its output is not a benchmark result.
4. Read the output without mixing up stages
For the supplied example, look under outputs/tutorial-mixed/:
| Artifact | What to inspect |
|---|---|
example_complex_records.json |
Completed cycles, branch names, scores, sequences, and timing |
recycle_1/mpnn/mpnn_spec.json |
Residues fixed during the first MPNN step |
recycle_1/mpnn/mpnn_result.json |
MPNN sequence and handoff information |
inputs/example_complex_1.pdb |
Redesigned complex passed forward after the first cycle |
recycle_1/rf3_config.yaml |
Per-cycle configuration, when the branch is RF3 |
Records number cycles from 0, while output folders begin at recycle_1. The scores in a record come from the structure/optimization branch before that cycle’s MPNN redesign; mpnn_sequence is the sequence produced afterward. Do not attach those scores to the handoff sequence as though it had already been independently refolded. This ordering is visible in the run loop.
Two other details affect interpretation. Increasing mpnn_num_seqs saves more samples but still carries the first sample forward; the driver does not rank them. Also, an AF3_Status field in the Boltz branch is a legacy label, not evidence that AlphaFold3 ran. Read the method field and the actual model outputs. Independent AF3 filtering is outside this pipeline.
5. Check completed cycles with a small Python script
The following checker uses only Python’s standard library. It checks the requested cycle count, contiguous cycle IDs, branch names, standard amino-acid sequences, consistent chain length, and finite nonnegative timings. It does not inspect coordinates or interpret confidence scores.
Download check_rfo_records.py, or save the complete code below:
curl -fL https://www.bobobk.com/downloads/rfoptimization/check_rfo_records.py \
-o check_rfo_records.py
python3 check_rfo_records.py --help
#!/usr/bin/env python3
"""Audit completed RFO cycles; this does not evaluate molecular quality."""
import argparse
from collections import Counter
import json
import math
from pathlib import Path
def audit(records, expected):
if expected < 1:
raise ValueError("expected cycle count must be positive")
if not isinstance(records, list) or not records:
raise ValueError("records must be a nonempty JSON list")
if len(records) != expected:
raise ValueError(f"expected {expected} cycles, found {len(records)}")
methods = Counter()
sequences = []
seconds = 0.0
for index, row in enumerate(records):
if not isinstance(row, dict):
raise ValueError(f"record {index}: expected an object")
cycle = row.get("cycle")
if type(cycle) is not int or cycle != index:
raise ValueError(f"record {index}: expected zero-based cycle {index}")
method = row.get("method")
if method not in ("RF3", "Boltz"):
raise ValueError(f"cycle {index}: unknown method")
sequence = row.get("mpnn_sequence")
if not isinstance(sequence, str) or not sequence:
raise ValueError(f"cycle {index}: missing MPNN sequence")
if set(sequence) - set("ACDEFGHIKLMNPQRSTVWY"):
raise ValueError(f"cycle {index}: nonstandard amino acid")
if sequences and len(sequence) != len(sequences[0]):
raise ValueError(f"cycle {index}: chain-A length changed")
elapsed = row.get("wall_time_sec")
if (type(elapsed) not in (int, float)
or not math.isfinite(elapsed) or elapsed < 0):
raise ValueError(f"cycle {index}: invalid wall_time_sec")
methods[method] += 1
sequences.append(sequence)
seconds += elapsed
return {
"completed_cycles": len(records),
"methods": dict(sorted(methods.items())),
"chain_a_length": len(sequences[0]),
"unique_handoff_sequences": len(set(sequences)),
"recorded_cycle_seconds": round(seconds, 2),
"quality_evaluated": False,
}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("records", type=Path)
parser.add_argument("--expected-cycles", required=True, type=int)
args = parser.parse_args()
try:
records = json.loads(args.records.read_text(encoding="utf-8"))
result = audit(records, args.expected_cycles)
except (OSError, UnicodeError, ValueError) as exc:
parser.exit(1, f"ERROR: {exc}\n")
print(json.dumps(result, indent=2, allow_nan=False))
if __name__ == "__main__":
main()
Try this synthetic fixture first. The sequences and timings are invented solely to demonstrate the checker; they are not RFO predictions or performance measurements.
python3 - <<'PY'
import json
from pathlib import Path
records = [
{"cycle": 0, "method": "RF3", "mpnn_sequence": "ACDEFG",
"wall_time_sec": 12.5},
{"cycle": 1, "method": "Boltz", "mpnn_sequence": "ACDEYG",
"wall_time_sec": 20.0},
]
Path("synthetic_records.json").write_text(json.dumps(records))
PY
python3 check_rfo_records.py synthetic_records.json --expected-cycles 2
Expected output:
{
"completed_cycles": 2,
"methods": {
"Boltz": 1,
"RF3": 1
},
"chain_a_length": 6,
"unique_handoff_sequences": 2,
"recorded_cycle_seconds": 32.5,
"quality_evaluated": false
}
For the real three-cycle trial, run the checker from foundry/models/rfo, with the script saved there:
python3 check_rfo_records.py \
outputs/tutorial-mixed/example_complex_records.json --expected-cycles 3
A nonzero exit means the record file did not meet these checks. A successful check establishes record consistency only. recorded_cycle_seconds sums saved completed-cycle durations; it excludes failed attempts that never became records and is not total job wall time. Repeated sequences are counted, not rejected, because an unchanged handoff can be valid.
6. Troubleshooting and evaluation
| Symptom | Action |
|---|---|
| MPNN executable or weights missing | Check MPNN_REPO/run.py, the absolute MPNN_PYTHON path, and the upstream model_params directory |
| RF3 checkpoint rejected | Set RF3_CKPT to a confidence-enabled checkpoint with its training configuration |
| Input file not found | Run from foundry/models/rfo or replace the relative input path with an absolute path |
| Fewer records than requested cycles | Inspect the failing model logs; directory creation alone does not establish completion |
| Several MPNN samples but only one next input | Inspect saved samples separately; the current driver forwards the first |
| Scores improve but the handoff sequence is untested | Refold the actual saved sequence with the intended partner in a separate evaluation |
Choose the evaluation procedure before comparing runs. Preserve the starting candidate, specify which cycles can be selected, and apply the same evaluator and inputs to baseline and refined candidates. Keep failed or missing evaluations visible in the denominator. Computational confidence is useful for prioritization; binding, specificity, stability, and activity still require the corresponding measurements.
For cyclic-peptide work, inspect the implementation separately: cyclic: true is passed to Boltz for chain A, but the RF3 optimizer does not add explicit cyclic bond constraints in this release. The cycling documentation describes this limitation. The protein-complex trial above does not validate cyclic-peptide behavior.
7. Summary
Start with a checked input complex, verify each model branch, and retain every sequence handed to the next cycle. Use the records checker to catch incomplete runs, then evaluate the actual refined sequences independently. RFOptimization’s useful output is a set of candidates with a traceable optimization history; experimental success remains a separate question.
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/en/rfoptimization-protein-design-guide.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。