use crate::core::solver::Solver;
use crate::core::state::State;
use crate::core::termination::{TerminationCriterion, TerminationReason};
pub struct OptimizationResult<S> {
pub state: S,
pub reason: TerminationReason,
}
impl<S: State> OptimizationResult<S> {
pub fn param(&self) -> &S::Param {
self.state.param()
}
pub fn cost(&self) -> S::Float {
self.state.cost()
}
pub fn iter(&self) -> u64 {
self.state.iter()
}
pub fn cost_evals(&self) -> u64 {
self.state.cost_evals()
}
pub fn into_state(self) -> S {
self.state
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepOutcome {
Continue,
Stopped(TerminationReason),
}
pub struct Stepper<P, S, So> {
problem: P,
state: Option<S>,
solver: So,
criteria: Vec<Box<dyn TerminationCriterion<S>>>,
max_iter: u64,
finished: Option<TerminationReason>,
}
impl<P, S, So> Stepper<P, S, So>
where
S: State,
So: Solver<P, S>,
{
pub fn state(&self) -> &S {
self.state
.as_ref()
.expect("state slot is Some between steps")
}
pub fn finished(&self) -> Option<&TerminationReason> {
self.finished.as_ref()
}
pub fn iter(&self) -> u64 {
self.state().iter()
}
pub fn step(&mut self) -> StepOutcome {
if let Some(reason) = self.finished {
return StepOutcome::Stopped(reason);
}
let outcome = step_once(
&self.problem,
&mut self.state,
&mut self.solver,
&mut self.criteria,
self.max_iter,
);
if let StepOutcome::Stopped(reason) = outcome {
self.finished = Some(reason);
}
outcome
}
pub fn run_to_end(mut self) -> OptimizationResult<S> {
loop {
if let StepOutcome::Stopped(reason) = self.step() {
return OptimizationResult {
state: self.state.take().expect("state slot is Some on stop"),
reason,
};
}
}
}
pub fn into_state(self) -> S {
self.state.expect("state slot is Some at drop")
}
}
fn step_once<P, S, So>(
problem: &P,
state_slot: &mut Option<S>,
solver: &mut So,
criteria: &mut [Box<dyn TerminationCriterion<S>>],
max_iter: u64,
) -> StepOutcome
where
S: State,
So: Solver<P, S>,
{
{
let state = state_slot
.as_ref()
.expect("step_once called with empty state slot");
if state.iter() >= max_iter {
return StepOutcome::Stopped(TerminationReason::MaxIter);
}
for criterion in criteria.iter_mut() {
if let Some(reason) = criterion.check(state) {
return StepOutcome::Stopped(reason);
}
}
if let Some(reason) = solver.terminate(state) {
return StepOutcome::Stopped(reason);
}
}
let prev = state_slot.take().unwrap();
let (mut next, mid_iter_reason) = solver.next_iter(problem, prev);
if let Some(reason) = mid_iter_reason {
*state_slot = Some(next);
return StepOutcome::Stopped(reason);
}
next.increment_iter();
*state_slot = Some(next);
StepOutcome::Continue
}
pub fn run_loop<P, S, So>(
problem: &P,
state: S,
solver: &mut So,
criteria: &mut [Box<dyn TerminationCriterion<S>>],
max_iter: u64,
) -> OptimizationResult<S>
where
S: State,
So: Solver<P, S>,
{
let state = solver.init(problem, state);
let mut slot = Some(state);
let reason = loop {
match step_once(problem, &mut slot, solver, criteria, max_iter) {
StepOutcome::Continue => continue,
StepOutcome::Stopped(reason) => break reason,
}
};
OptimizationResult {
state: slot.take().expect("state slot is Some on stop"),
reason,
}
}
pub struct Executor<P, S, So> {
problem: P,
state: S,
solver: So,
max_iter: u64,
criteria: Vec<Box<dyn TerminationCriterion<S>>>,
}
impl<P, S, So> Executor<P, S, So>
where
S: State,
So: Solver<P, S>,
{
pub fn new(problem: P, solver: So, state: S) -> Self {
Self {
problem,
state,
solver,
max_iter: 1000,
criteria: Vec::new(),
}
}
pub fn max_iter(mut self, n: u64) -> Self {
self.max_iter = n;
self
}
pub fn terminate_on<C>(mut self, criterion: C) -> Self
where
C: TerminationCriterion<S> + 'static,
{
self.criteria.push(Box::new(criterion));
self
}
pub fn into_stepper(self) -> Stepper<P, S, So> {
let Executor {
problem,
state,
mut solver,
max_iter,
criteria,
} = self;
let state = solver.init(&problem, state);
Stepper {
problem,
state: Some(state),
solver,
criteria,
max_iter,
finished: None,
}
}
pub fn run(self) -> OptimizationResult<S> {
self.into_stepper().run_to_end()
}
}