> ## Documentation Index
> Fetch the complete documentation index at: https://proto.evodesign.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cycling Optimizer

> Alternates a conditioning step with a generation step each cycle to drive feedback loops such as Protein Hunter (structure prediction -> inverse folding), keeping proposals that pass the filter constraints.

<div class="page-hero">
  <div className="block dark:hidden">
    <svg viewBox="0 0 720 432" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cycling optimizer: a two-node loop alternating sequence and structure through conditioning and generation" style={{width:"100%",height:"auto",display:"block"}}><defs><pattern id="gridcycL" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="1.2" fill="#344649" fillOpacity="0.10" /></pattern><marker id="arrcycL" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 L3,5 z" fill="#768b8e" /></marker></defs><rect x="12" y="12" width="696" height="408" rx="16" fill="#f9fcfc" stroke="#dee9e8" strokeWidth="1.2" /><rect x="12" y="12" width="696" height="408" rx="16" fill="url(#gridcycL)" /><text x="360" y="56" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13.5" fontWeight="600" fill="#1d2c2f" textAnchor="middle">each cycle:  condition on sequences → generate → keep proposals passing all filters</text><circle cx="248" cy="230" r="50" fill="#046e7a" /><text x="248" y="234" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13" fontWeight="600" fill="#ffffff" textAnchor="middle">Sequence</text><rect x="422" y="180" width="100" height="100" rx="16" fill="#2f8f6b" /><text x="472" y="234" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13" fontWeight="600" fill="#ffffff" textAnchor="middle">Structure</text><path d="M296,212 Q361,153 426,202" fill="none" stroke="#046e7a" strokeWidth="2.4" /><path d="M0,0 L-7,-3.8500000000000005 L-7,3.8500000000000005 Z" fill="#046e7a" transform="translate(426,202) rotate(37.01)" /><path d="M424,260 Q359,309 294,250" fill="none" stroke="#C77D2E" strokeWidth="2.4" /><path d="M0,0 L-7,-3.8500000000000005 L-7,3.8500000000000005 Z" fill="#C77D2E" transform="translate(294,250) rotate(-137.77)" /><text x="360" y="148" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="500" fill="#344649" textAnchor="middle">predict structure  (conditioning\_fn)</text><text x="360" y="320" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="500" fill="#344649" textAnchor="middle">inverse folding  (generator)</text><text x="360" y="234" fontFamily="'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace" fontSize="11" fontWeight="500" fill="#768b8e" textAnchor="middle">↻ × num\_steps</text><path d="M239,392 l20,0" fill="none" stroke="#046e7a" strokeWidth="2.6" /><text x="266" y="396" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="400" fill="#768b8e" textAnchor="start">predict structure</text><path d="M397,392 l20,0" fill="none" stroke="#C77D2E" strokeWidth="2.6" /><text x="424" y="396" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="400" fill="#768b8e" textAnchor="start">redesign sequence</text></svg>
  </div>

  <div className="hidden dark:block">
    <svg viewBox="0 0 720 432" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cycling optimizer: a two-node loop alternating sequence and structure through conditioning and generation" style={{width:"100%",height:"auto",display:"block"}}><defs><pattern id="gridcycD" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="1.2" fill="#9eb4b2" fillOpacity="0.10" /></pattern><marker id="arrcycD" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 L3,5 z" fill="#7e9498" /></marker></defs><rect x="12" y="12" width="696" height="408" rx="16" fill="#0e1718" stroke="#2b3c3e" strokeWidth="1.2" /><rect x="12" y="12" width="696" height="408" rx="16" fill="url(#gridcycD)" /><text x="360" y="56" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13.5" fontWeight="600" fill="#eef5f4" textAnchor="middle">each cycle:  condition on sequences → generate → keep proposals passing all filters</text><circle cx="248" cy="230" r="50" fill="#0a7e8c" /><text x="248" y="234" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13" fontWeight="600" fill="#ffffff" textAnchor="middle">Sequence</text><rect x="422" y="180" width="100" height="100" rx="16" fill="#2f8f6b" /><text x="472" y="234" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="13" fontWeight="600" fill="#ffffff" textAnchor="middle">Structure</text><path d="M296,212 Q361,153 426,202" fill="none" stroke="#0a7e8c" strokeWidth="2.4" /><path d="M0,0 L-7,-3.8500000000000005 L-7,3.8500000000000005 Z" fill="#0a7e8c" transform="translate(426,202) rotate(37.01)" /><path d="M424,260 Q359,309 294,250" fill="none" stroke="#C77D2E" strokeWidth="2.4" /><path d="M0,0 L-7,-3.8500000000000005 L-7,3.8500000000000005 Z" fill="#C77D2E" transform="translate(294,250) rotate(-137.77)" /><text x="360" y="148" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="500" fill="#d6e1df" textAnchor="middle">predict structure  (conditioning\_fn)</text><text x="360" y="320" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="500" fill="#d6e1df" textAnchor="middle">inverse folding  (generator)</text><text x="360" y="234" fontFamily="'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace" fontSize="11" fontWeight="500" fill="#9eb4b2" textAnchor="middle">↻ × num\_steps</text><path d="M239,392 l20,0" fill="none" stroke="#0a7e8c" strokeWidth="2.6" /><text x="266" y="396" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="400" fill="#9eb4b2" textAnchor="start">predict structure</text><path d="M397,392 l20,0" fill="none" stroke="#C77D2E" strokeWidth="2.6" /><text x="424" y="396" fontFamily="Geist, ui-sans-serif, system-ui, -apple-system, sans-serif" fontSize="11" fontWeight="400" fill="#9eb4b2" textAnchor="start">redesign sequence</text></svg>
  </div>
</div>

<p class="entity-disclaimer">This optimizer is open source. Any third-party models, product names, or trademarks referenced are the property of their respective owners, and Proto is not affiliated with them.</p>

<hr class="entity-rule" />

<div class="tool-tab-bar entity-source-bar"><span class="tool-tab-wrap"><span class="tool-tab badge-source entity-source-tab"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" /></svg> Source</span></span></div>

<a href="https://github.com/evo-design/proto-language/blob/d3b7822f74ea64747cc751a3b2ab1aa6b799ac47/proto_language/optimizer/cycling_optimizer.py#L244" target="_blank" class="tab-panel source-panel entity-source-panel">
  <div class="source-info">
    <img noZoom src="https://github.com/evo-design.png?size=40" class="source-avatar" width="36" height="36" />

    <span class="source-path">evo-design/proto-language<span class="source-subpath">/proto\_language/optimizer/cycling\_optimizer.py</span></span>
  </div>

  <span class="panel-goto-btn source-goto-btn"><span><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" /></svg> View source</span></span>
</a>

<div class="entity-contributors"><span class="entity-contributors-label">Optimizer contributors</span><span class="entity-contributors-people"><a class="entity-contributor" href="https://github.com/dguo8412" target="_blank" rel="noopener" title="dguo8412: 6 commits"><img noZoom class="entity-contributor-avatar" src="https://avatars.githubusercontent.com/u/46211285?v=4&s=64" alt="" loading="lazy" /><span class="entity-contributor-login">dguo8412</span></a><a class="entity-contributor" href="https://github.com/bviggiano" target="_blank" rel="noopener" title="bviggiano: 2 commits"><img noZoom class="entity-contributor-avatar" src="https://avatars.githubusercontent.com/u/21143637?v=4&s=64" alt="" loading="lazy" /><span class="entity-contributor-login">bviggiano</span></a></span></div>
Cycling optimizer for iterative sequence refinement.

A generalized optimizer that cycles between a user-defined conditioning function
and a generator:

1. Call conditioning function with current sequences (from result\_sequences)
2. Pass conditioning output to generator's sample() method (into proposal\_sequences)
3. Accept passing proposals into result\_sequences (failed stay unchanged)
4. Repeat for num\_steps

This enables flexible optimization patterns such as:

* Protein Hunter: Structure prediction -> inverse folding cycles
* Evo2 with feedback: Constraint-guided prompt modification -> generation cycles

<Note>
  - Constraints are optional; if provided, must be filter constraints
    (have `threshold` set) - only passing proposals update result\_sequences
</Note>

## How It Works

Cycling alternates a conditioning function with a generator, looping between two views of the design (for `protein-hunter`, sequence and structure) and keeping only proposals that pass every filter.

Cycling alternates a conditioning function with a generator. Each cycle conditions on the current sequences, generates proposals from that conditioning, and keeps only proposals that pass every filter:

```
for cycle in 1..num_steps:
    data      = conditioning_fn(result_sequences)
    proposals = generator.sample(data)
    result_i ← proposal_i   ⇔   proposal_i passes all filters   (else keep prior)
```

Cycling takes **filter-only** constraints (each needs a `threshold`; a scoring constraint raises an error). The built-in `protein-hunter` pipeline sets `conditioning_fn` to structure prediction (Boltz-2 / Chai-1 / AlphaFold3) feeding inverse folding: predict a structure, then redesign the sequence for it.

## API Reference

<div class="api-model-section api-model-static api-config-section">
  <div class="api-model-header"><span class="api-model-badge api-config-badge">Config</span><span class="api-model-name">CyclingOptimizerConfig</span><a href="https://github.com/evo-design/proto-language/blob/d3b7822f74ea64747cc751a3b2ab1aa6b799ac47/proto_language/optimizer/cycling_optimizer.py#L154" target="_blank" class="func-table-btn func-source-btn api-model-source"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" /></svg> Source</a></div>

  Configuration for CyclingOptimizer.

  This optimizer cycles between a conditioning function and a generator.
  On each cycle, the conditioning function receives the current proposal sequences,
  produces conditioning data, which is then passed to the generator's sample() method.

  The conditioning function can be provided either:

  1. Directly via the `conditioning_fn` parameter (programmatic use)
  2. Via the `pipeline` field using a predefined pipeline (API/JSON use)

  <Note>
    * Pipeline-specific constraints:
      * `protein-hunter` requires an inverse\_folding generator
    * Constraints are optional but if provided must be filter constraints
      (must have `threshold` set)
  </Note>

  <ParamField path="num_steps" type="integer" required>
    Number of conditioning-then-generation cycles to run.
  </ParamField>

  <ParamField path="num_results" type="integer">
    Candidate design trajectories for this optimizer. Overrides program-level count.
  </ParamField>

  <ParamField path="pipeline" type="string">
    Predefined conditioning pipeline. 'protein-hunter' uses structure prediction -> inverse folding.
  </ParamField>

  <ParamField path="protein_hunter" type="ProteinHunterPipelineConfig">
    Configuration for protein-hunter pipeline. Only used when pipeline='protein-hunter'.
  </ParamField>

  <ParamField path="seed" type="integer">
    Random seed for reproducible optimization, generator, and constraint tool streams.
  </ParamField>

  <ParamField path="tracking_interval" type="integer" default="1">
    Save history and log progress every N steps. Step 0 and final step always saved.
  </ParamField>

  <ParamField path="track_proposals" type="boolean" default="False">
    Save granular per-proposal results (accept/reject) in history snapshots.
  </ParamField>

  <ParamField path="verbose" type="boolean" default="False">
    Emit per-step debug information about proposals, scores, and acceptance through the logger.
  </ParamField>
</div>

## Usage

```python python icon="python" theme={null}
>>> def my_conditioning_fn(sequences):
...     return [process(seq) for seq in sequences]
>>> optimizer = CyclingOptimizer(
...     target_segment=segment,
...     constructs=[construct],
...     generators=[generator],
...     constraints=[],
...     config=CyclingOptimizerConfig(num_steps=5, num_results=4),
...     conditioning_fn=my_conditioning_fn,
... )
>>> optimizer.run()
```

## Metadata

| Property               | Value              |
| ---------------------- | ------------------ |
| Key                    | `cycling`          |
| Class                  | `CyclingOptimizer` |
| Targets Single Segment | `True`             |
| Uses GPU               | `False`            |
