pub struct InnerExecutor<S, So> { /* private fields */ }Expand description
Pre-configured inner solver an outer solver drives once per outer iteration.
Owns the configured inner solver, execution controls, and its max_iter
budget. The problem is supplied (borrowed) at run time,
so the outer solver can pass the &P it receives in
next_iter without taking
ownership.
Mirrors Executor’s builder API:
max_iter and stop_when_factory
are chainable. The differences are (a) the problem isn’t owned, and
(b) run is reusable: the same InnerExecutor is
expected to be invoked many times across the outer’s lifetime.
run_loop_with_control provides the lower-level
interface for custom outer solvers.
§Serialization
With serde, the solver and iteration/evaluation/time budgets serialize.
Application hooks, deprecated criteria, capability controls (raw budgets
and publication validation), and erased target/stall checks
cannot be reconstructed and cause a serialization error. They are never
silently dropped from an exact checkpoint.
§Composition contracts
Three rules outer solvers must follow when consuming the result of
run; see also CONTRIBUTING.md “Solver composition”:
-
Eval aggregation. The
Problemwrapper bumpsEvalCountson every cost/gradient/residual/Jacobian/Hessian call, and the executor mirrors the per-run delta onto the inner state viaCountsMirror. What the outer must do depends on which problem the inner sees:- Same-problem inner (the outer passes its own
&mut Problem<P>torun): the inner’s calls bump the same wrapper as the outer’s, so aggregation happens transparently. No explicit roll-up; the outer state’sCountsMirrorimpl decides how the counts surface on itsState::cost_evals/GradientState::gradient_evals. - Adapter-problem inner (the outer builds a fresh
Problem::new(adapter)per outer iter, e.g. the barrier and augmented-Lagrangian methods): afterrunreturns, fold the inner wrapper’s counts back into the outer’s wrapper viaEvalCounts::addonProblem::counts_mut. Skipping this fold silently corruptsMaxCostEvalsbudgets and the publicresult.cost_evals().
See the
Solver::next_itercontract for the canonical wording. - Same-problem inner (the outer passes its own
-
History resets per run. Each fresh run resets solver convergence, built-in clocks and stall checks, and deprecated criterion history.
stop_when_factorycreates a fresh custom closure per run. A directstop_whenclosure retains its captures across calls. -
Failure routing.
runreturns a fullOptimizationResult; classify the reason. UseTerminationReason::is_failureto decide whether to bubble:SolverFailedshould bubble via the outer’s mid-iterOption<TerminationReason>return; everything else (MaxIter,*Tolerance,SolverConverged,NumericalNoProgress) is a “clean stop” the outer can consume and continue past. A clean stop does not itself establish convergence or solution accuracy.
Implementations§
Source§impl<S: State + CountsMirror, So> InnerExecutor<S, So>
impl<S: State + CountsMirror, So> InnerExecutor<S, So>
Sourcepub fn new(solver: So) -> Self
pub fn new(solver: So) -> Self
Build an inner executor around solver. Default max_iter is
1000, mirroring Executor::new.
Sourcepub fn max_iter(self, n: u64) -> Self
pub fn max_iter(self, n: u64) -> Self
Set the inner-loop iteration budget. Each call to
run drives the inner solver up to this many
iterations.
Sourcepub fn require_evaluated_state(self) -> Selfwhere
S: EvaluatedState,
pub fn require_evaluated_state(self) -> Selfwhere
S: EvaluatedState,
Validate complete records at publication boundaries.
See RunControl::require_evaluated_state
for validation ordering and solver-contract panics. This control
cannot be serialized as part of an inner executor.
Sourcepub fn max_evaluations(self, kind: EvaluationKind, limit: u64) -> Selfwhere
S: RawEvaluationState,
pub fn max_evaluations(self, kind: EvaluationKind, limit: u64) -> Selfwhere
S: RawEvaluationState,
Set a raw category or total-work budget at iteration boundaries.
See RunControl::max_evaluations
for accounting, precedence, and serialization limits.
Sourcepub fn target_objective<F: Scalar + 'static>(self, target: F) -> Selfwhere
S: ObjectiveIncumbentState<Float = F>,
pub fn target_objective<F: Scalar + 'static>(self, target: F) -> Selfwhere
S: ObjectiveIncumbentState<Float = F>,
Stop when an eligible objective-ordered incumbent reaches a finite target.
Replaces target_cost, and vice versa. See
RunControl::target_objective.
Sourcepub fn no_objective_improvement<F: Scalar + 'static>(
self,
patience: u64,
min_delta: F,
) -> Selfwhere
S: ObjectiveIncumbentState<Float = F>,
pub fn no_objective_improvement<F: Scalar + 'static>(
self,
patience: u64,
min_delta: F,
) -> Selfwhere
S: ObjectiveIncumbentState<Float = F>,
Stop after completed iterations without a sufficient objective decrease.
Replaces no_improvement, and vice versa. See
RunControl::no_objective_improvement
for threshold validation, publication age, and resume behavior.
Sourcepub fn max_cost_evals(self, limit: u64) -> Self
pub fn max_cost_evals(self, limit: u64) -> Self
Set a cost-evaluation budget, checked after initialization and between iterations.
Sourcepub fn max_gradient_evals(self, limit: u64) -> Selfwhere
S: GradientState,
pub fn max_gradient_evals(self, limit: u64) -> Selfwhere
S: GradientState,
Set a gradient-evaluation budget, checked between iterations.
Sourcepub fn max_time(self, limit: Duration) -> Self
pub fn max_time(self, limit: Duration) -> Self
Set a time budget starting at the first post-initialization check.
Sourcepub fn target_cost<F: Scalar + 'static>(self, target: F) -> Selfwhere
S: State<Float = F>,
pub fn target_cost<F: Scalar + 'static>(self, target: F) -> Selfwhere
S: State<Float = F>,
Stop when the state’s best cost reaches the finite target.
Sourcepub fn no_improvement<F: Scalar + 'static>(
self,
patience: u64,
min_delta: F,
) -> Selfwhere
S: State<Float = F>,
pub fn no_improvement<F: Scalar + 'static>(
self,
patience: u64,
min_delta: F,
) -> Selfwhere
S: State<Float = F>,
Stop after patience checks without improvement greater than min_delta.
Sourcepub fn no_acceptance(self, patience: u64) -> Selfwhere
S: AcceptanceState,
pub fn no_acceptance(self, patience: u64) -> Selfwhere
S: AcceptanceState,
Stop after a positive number of iterations without an accepted move.
Sourcepub fn stop_when_factory<M, C>(self, make: M) -> Self
pub fn stop_when_factory<M, C>(self, make: M) -> Self
Append a factory creating fresh application-stop history for each run.
Sourcepub fn terminate_on<C>(self, criterion: C) -> Selfwhere
C: TerminationCriterion<S> + 'static,
👎Deprecated: configure inner solver convergence or use stop_when_factory; removal scheduled for Basin 2.0
pub fn terminate_on<C>(self, criterion: C) -> Selfwhere
C: TerminationCriterion<S> + 'static,
configure inner solver convergence or use stop_when_factory; removal scheduled for Basin 2.0
Add a termination criterion to the inner loop. Criteria are
checked in insertion order before each inner iteration. See the
type-level “Composition contracts” for the statelessness
requirement that applies because criteria are reused across
run calls.
Sourcepub fn stop_when<C>(self, check: C) -> Self
pub fn stop_when<C>(self, check: C) -> Self
Append an application stop whose captures persist across inner runs.
Use stop_when_factory for fresh per-run history.
Sourcepub fn solver(&self) -> &So
pub fn solver(&self) -> &So
Read-only access to the inner solver. Lets composed outer
solvers dispatch on the inner before run, e.g. to
build an inner state via InitialState::seed or
MemeticInner::seed_scaled. Mutable access goes through
run, which already takes &mut self.
Sourcepub fn run<P>(
&mut self,
problem: &mut Problem<P>,
state: S,
) -> Result<OptimizationResult<S>, So::Error>where
So: Solver<P, S>,
pub fn run<P>(
&mut self,
problem: &mut Problem<P>,
state: S,
) -> Result<OptimizationResult<S>, So::Error>where
So: Solver<P, S>,
Drive the inner solver against problem from state, returning
the final inner state and termination reason. Reusable: call once
per outer iter.
The inner state’s State::cost_evals reflects only per-run
work (snapshot-relative against the wrapper count at entry), not
cumulative across calls. The wrapper itself accumulates
monotonically: for same-problem composition the outer reads
its own Problem::counts after run to see total work; for
adapter-problem composition the outer builds a fresh inner
Problem and folds counts via
EvalCounts::add on
Problem::counts_mut after run returns.
Internally exactly
run_loop_with_control: init is called
on every invocation, so the inner solver sees a fresh setup pass
each time (e.g. seeding cost/gradient at the new starting point).
Trait Implementations§
Source§impl<'de, S, So> Deserialize<'de> for InnerExecutor<S, So>where
So: Deserialize<'de>,
Available on crate feature serde only.
impl<'de, S, So> Deserialize<'de> for InnerExecutor<S, So>where
So: Deserialize<'de>,
serde only.Source§fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Auto Trait Implementations§
impl<S, So> !RefUnwindSafe for InnerExecutor<S, So>
impl<S, So> !Send for InnerExecutor<S, So>
impl<S, So> !Sync for InnerExecutor<S, So>
impl<S, So> !UnwindSafe for InnerExecutor<S, So>
impl<S, So> Freeze for InnerExecutor<S, So>
impl<S, So> Unpin for InnerExecutor<S, So>
impl<S, So> UnsafeUnpin for InnerExecutor<S, So>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
impl<T, U> Imply<T> for U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.