Skip to main content
The optimizer engine is the shared evaluation core under every optimizer. Strategies register candidates and ask the engine to evaluate them; the engine handles overlay application, rollout fan-out, caching, budget accounting, and score bookkeeping. Every optimizer in DSRs (COPRO, GEPA, MIPROv2, bootstrap) is a thin strategy over this core, which exists in two lanes: Both lanes share the same EngineConfig, Budget, Spend, RolloutCache, ScoreMatrix, Pareto bookkeeping, outcome types, and minibatch gate. All items on this page are exported from the crate root except where a feature gate is noted.

EvalEngine<'m, E, MT>

Owns the example set, the candidate registry, the score matrix, the rollout cache, and the budget meter. E is the trainset row type; construction requires E: Serialize. Strategies register candidates and call evaluate or evaluate_gated; the engine handles application, fan-out, caching, accounting, and bookkeeping. evaluate requires E: ToInput<M::Input> + Sync, M: Module + Facet, and MT: TypedMetric<E, M>. The metric runs outside the trace capture scope, so LM-as-judge metrics do not pollute the execution trace. Concurrency model. Candidates mutate shared predictor state, so the engine serializes candidate application and parallelizes across examples within one candidate (bounded by EngineConfig::concurrency). Candidate-level parallelism requires overlays resolved at render time; that is the program lane below.

EngineConfig

Candidates and the mutation seam

A Candidate is data: overlays: BTreeMap<String, Overlay> mapping predictor name to a partial parameter update, plus a stable content hash. The empty candidate (Candidate::default()) is the baseline, the module exactly as it is.
This module-lane Overlay (instruction plus demos per predictor) is a different type from the IR ir::Overlay, which maps ParamId to ParamValue over a compiled Program. The program lane below consumes the IR type.

Budget and Spend

Budget sets hard caps on evaluation spend; None means unlimited. Budget::allows(&spend, upcoming_rollouts) reports whether the batch fits. Zero upcoming rollouts always fit, so cache-only batches never stall. Call and metric caps are enforced prospectively (the batch must fit under the cap); the token cap is retrospective (a batch may overshoot, and the following batch is refused). Spend is what the engine has consumed so far, serialized into checkpoints:
This Budget is not the IR runtime Budget documented on Runtime. The runtime type caps one program execution: max_lm_calls: Option<u32>, max_tokens, and a deadline: Option<Instant>, enforced call by call inside the run by a BudgetMeter with parent chaining for nested agent loops. The optimizer type caps an entire optimization search across all rollouts: it adds max_metric_calls, has no deadline, and is checked per batch before anything runs, with consumption tracked in Spend. The program lane runs each rollout under the runtime Budget::unlimited() while the optimizer Budget governs the batch.

RolloutCache

In-memory map from a rollout key to its Eval (score plus optional feedback). A candidate re-evaluated on a seen example returns the cached Eval with no LM call and no metric call. The cache is serialized into checkpoints, so a resumed run skips completed rollouts. The key recipe is (baseline, candidate, example, salt), formatted as four 16-digit hex hashes joined by colons: Public surface: get, insert, len, is_empty.

Score bookkeeping

ScoreMatrix is a per-instance matrix of candidates (rows, registration order) by examples (columns). Cells are None until scored. Methods: new(columns), candidates(), examples(), ensure_rows(n), record(candidate, example, score), score(candidate, example), row(candidate), mean(candidate), best_by_mean(), pareto(), pareto_over(columns). The column-restricted view supports GEPA-style setups where train and validation examples share one matrix. ParetoView is a dominance snapshot computed from the matrix. best_scores() gives the best score per viewed column; wins(candidate) counts columns the candidate wins or ties on (tolerance 1e-6); frontier() lists candidates winning on at least one column; statistics() summarizes. A candidate with zero wins is dominated. ParetoFrontier is a standalone convenience wrapper over the same bookkeeping for callers that track candidate payloads outside an engine; GEPA itself uses the engine’s matrix directly. It stores GEPACandidate payloads, prunes dominated candidates automatically, and offers add_candidate(candidate, scores) -> bool, sample_proportional_to_coverage(), best_by_average(), candidates(), len(), is_empty(), statistics(). ParetoStatistics fields: num_candidates, num_examples_covered, avg_coverage: f32, max_coverage, min_coverage. A healthy search grows num_candidates slowly while avg_coverage rises; num_candidates == 1 means the search has collapsed.

Outcome types

Program lane: ProgramEvalEngine<'m, MT: ProgramMetric>

Behind the ir feature. The IR-native evaluation path: candidate ir::Overlays evaluated over one shared Arc<Program> through the Interpreter, with true candidate-level parallelism. The interpreter reads instructions, demos, and code through the overlay at render time, so there is no mutation, no apply, and no restore. Every uncached (candidate, example) pair across all requested candidates joins one bounded-concurrency stream.
ProgramMetric is the JSON-native sibling of TypedMetric: loaded programs have no static output type, so the metric scores the interpreter’s output JsonMap against a labeled DemoRow. The program lane shares Spend accounting, budget gating, and matrix bookkeeping with the module lane, but it does not offer checkpoint or resume; those exist only on EvalEngine.

See also