Skip to main content

run_loop

Function run_loop 

Source
pub fn run_loop<P, S, So>(
    problem: &mut Problem<P>,
    state: S,
    solver: &mut So,
    criteria: &mut [Box<dyn TerminationCriterion<S>>],
    max_iter: u64,
) -> Result<OptimizationResult<S>, So::Error>
where S: State + CountsMirror, So: Solver<P, S>,
Expand description

Drive a solver to completion against a shared Problem wrapper.

Executor is a thin owning wrapper over this. Composed solvers (e.g. CG inside CMA, NM inside DE) call run_loop directly so the inner solver shares the outer’s wrapper: inner cost and gradient calls bump the same EvalCounts as outer calls, so the eval aggregation contract (CONTRIBUTING.md “Solver composition” rule 1) is satisfied automatically for same-problem inners. For composed solvers driving an inner against an adapter problem (e.g. LogBarrier), construct a fresh Problem::new(adapter), pass &mut into run_loop, then fold the inner wrapper’s EvalCounts back into the outer’s via EvalCounts::add on Problem::counts_mut.

The inner state’s State::cost_evals (mirrored via CountsMirror) reflects only per-run work: run_loop takes a baseline snapshot of Problem::counts at entry, and the state mirror computes the delta against that. Nested run_loop calls against the same wrapper therefore see clean per-call counters.

Semantics match Executor::run: each criterion is reset at entry, so a criteria vector reused across calls (as an InnerExecutor does) sees fresh per-run state. Then init is called once, then on each iteration framework criteria are checked in insertion order before the solver’s own terminate hook, before stepping. max_iter is checked against state.iter() and exits with TerminationReason::MaxIter. next_iter may also report a mid-iter termination via its return tuple; in that case the iteration counter is left untouched so the final state.iter() still reflects the last fully completed iteration.