GradientOptimizer designs a protein by gradient descent on a differentiable representation
of the sequence rather than by discrete mutation. This program hallucinates a 20-residue binder
in two stages that mirror the Germinal pipeline: a logit phase, in which a soft, continuous
sequence evolves, and a softmax phase, in which the temperature is annealed to sharpen it into a
discrete sequence.
To keep the example runnable on CPU, it uses a mock differentiable constraint in place of a
real structure model; the structure of the program is identical when real constraints are
substituted.
Open as a runnable notebook
View as a Python script
Demonstration: this example uses a mock constraint that pushes every position toward alanine, so it runs on CPU without model weights. The all-alanine result is the expected output of that mock; substitute real structure constraints (for example an AF2-backed confidence constraint) to perform actual design.
A differentiable constraint
Gradient optimization requires constraints that return a gradient, not just a score. Instead of the discretefunction used elsewhere, a gradient constraint supplies a backward callable that
reads each sequence’s continuous logits (an (L, |vocab|) matrix) and returns a
GradientConstraintOutput carrying two things: gradient, the gradient of the objective with
respect to those logits, and loss, the scalar objective value the optimizer minimizes. The mock
below builds a target distribution that places all mass on alanine (column 0 of the vocabulary),
sets the gradient to logits - target, and reports the mean squared difference as the loss; a
real constraint would backpropagate through a structure predictor instead.
python
Stage 1: logit phase
The design starts from a VHH seed sequence carried on theSegment. The GradientOptimizer does
not mutate that string directly; its optimization variable is each proposal’s per-position
logits, which it updates by gradient descent over num_steps. PositionWeightGenerator never
proposes here; it only decodes those logits back into a discrete sequence (by default the most
likely token at each position) at tracked steps. GradientOptimizerConfig.germinal_logit_preset()
configures this stage: 65 SGD steps with the soft blend ramping from 0 to 1 while the softmax
temperature stays fixed, so the continuous logits relax freely. Passing the same target_segment
to both stages is what lets stage two continue from this stage’s logits. The custom_logging
callback track records the decoded sequence at each tracked step.
python
Stage 2: softmax phase
The second stage appliesGradientOptimizerConfig.germinal_softmax_preset(): 35 SGD steps that
hold the soft blend at 1 and anneal the softmax temperature from 1 toward 0.01 on a quadratic
schedule, sharpening the soft distribution into a near-discrete one. Lowering the temperature
concentrates each position’s probability mass on a single token. Because it reuses the same
segment object by identity, it picks up the logits stage one left behind rather than
reinitializing. A fresh PositionWeightGenerator and a gradient Constraint are bound for this
stage, and the same track callback keeps appending to the shared trajectory list.
python
Run
TheProgram holds both optimizers and runs them in order, so the logit phase completes before
the softmax phase begins on the same segment. num_results=1 runs a single trajectory.
python
Inspect the result
segment.result_sequences[0] is the decoded design; segment.original_sequence holds the VHH
seed it started from. The printout compares the two and walks the trajectory recorded by the
track callback, sampling a handful of evenly spaced snapshots to show the decoded sequence
sharpening from the seed toward the mock target. With this mock constraint the optimizer drives
every position to alanine, so the designed sequence is all A; substituting real structure
constraints would yield a real design while the program structure stays the same.
python
Next Steps
Using Generators
The gradient generator family.
Using Optimizers
The optimizer that drives this design.