Skip to main content

Programs

While individual optimizers run a single search strategy, a Program chains multiple optimizers into a multi-stage pipeline: broad exploration followed by targeted refinement, cheap filters before expensive scoring, temperature annealing across stages. A Program runs its optimizers sequentially, automatically handling the handoff of results between stages.

Single vs Multi-Stage

For simple designs, wrap one optimizer in a Program:
python
InputSequenceMCMC500 stepsResults(5 best)
InputSequenceMCMC500 stepsResults(5 best)

The Handoff

When one optimizer finishes and the next begins, the Program performs a carefully orchestrated handoff:
Stage 1: Rejection SamplingHandoffStage 2: MCMCRun optimizerSort result_sequencesby energy (best first)Initialize next optimizer’s poolsby cycling through sorted resultsClear stale constraintmetadataRun optimizer
Stage 1: Rejection SamplingHandoffStage 2: MCMCRun optimizerSort result_sequencesby energy (best first)Initialize next optimizer’s poolsby cycling through sorted resultsClear stale constraintmetadataRun optimizer
After each optimizer completes: Optimizers are responsible for their own ordering. Rejection Sampling keeps result_sequences sorted by energy (best first) throughout its run. Other optimizers preserve their natural ordering. Before the next optimizer runs:
  1. _initialize_sequence_pools() reads from the previous optimizer’s result_sequences
  2. Both pools are filled by cycling through source (preserving diversity when sizes differ)
  3. Stale constraint metadata is cleared so the new stage starts with a clean slate

Optimizer-Specific Behavior

Not all optimizers use inherited state the same way:
BeamSearch ignores previous optimizer results by design. It always starts fresh from its configured prompt since it is built for autoregressive generation. Place it as the first stage in a pipeline, or use it standalone.

Pipeline Design Recipes

The snippets below are illustrative patterns. They assume the segment, construct, generators, and the named constraint objects (for example gc_constraint, structure_constraint, expression_constraint) have already been defined as shown in the earlier examples and the Constraints guide.

Exploration then Refinement

Rejection Sampling (broad) then MCMC (focused)Use Rejection Sampling to quickly sample thousands of proposals with cheap constraints, then hand the best ones to MCMC for detailed optimization with expensive constraints.Most common multi-stage pattern.

Progressive Constraints

MCMC (basic) then MCMC (+ structure) then MCMC (+ expression)Start with cheap sequence-level constraints, then progressively add expensive constraints. Each stage builds on the previous one’s results.Avoids wasting GPU time scoring bad sequences.

Temperature Annealing

MCMC (hot) then MCMC (warm) then MCMC (cold)Explicit temperature stages: high temperature for broad exploration, medium for narrowing, low for final polishing. More control than single-optimizer annealing.Better for rugged energy landscapes.

Generator Switching

Rejection Sampling + RandomNucleotide then MCMC + ESM2Start with fast random mutations for initial screening, then switch to language-model-guided mutations for biologically informed refinement.Combines fast screening with language-model-guided refinement.

Exploration then Refinement

python

Progressive Constraints

python

Temperature Annealing

python

Running Stages Individually

Use run_stage() for fine-grained control: inspect results between stages, conditionally skip stages, or re-run a stage with different parameters.
python
A previous stage can also be re-run, which resets the pipeline to that point and invalidates subsequent stages:
python

Results and Export

Accessing Results

python

Export Formats

Stage-Specific Results

Access results from any completed stage:
python

Optimizer-Level Export

Individual Optimizer instances also provide the same export methods (without the stage parameter):
python

State Serialization

Save and restore program state for long-running optimization or checkpointing:
python

Important Rules

All optimizers in a Program must share the same Construct objects (by identity, not just value). This is how state persists between stages. The construct is created once and the same object is passed to all optimizers.
python
Each generator and constraint instance can only be used in one optimizer. This prevents shared mutable state bugs. Create new instances for each stage.
python

Properties

Next Steps

Quickstart

A complete program, from scratch

Optimizers

Deep dive into individual optimizer strategies

Constraints

Scoring functions for design objectives

Tools

The bioinformatics tools that constraints and generators call