use crate::core::problem::{EvalCounts, Problem};
use crate::core::solver::Solver;
use crate::core::state::{CountsMirror, 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: 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 + CountsMirror,
So: Solver<P, S>,
{
pub fn state(&self) -> &S {
self.state
.as_ref()
.expect("state slot is Some between steps")
}
pub fn counts(&self) -> &EvalCounts {
self.problem.counts()
}
pub fn finished(&self) -> Option<&TerminationReason> {
self.finished.as_ref()
}
pub fn iter(&self) -> u64 {
self.state().iter()
}
pub fn step(&mut self) -> Result<StepOutcome, So::Error> {
if let Some(reason) = self.finished {
return Ok(StepOutcome::Stopped(reason));
}
let outcome = step_once(
&mut self.problem,
&EvalCounts::default(),
&mut self.state,
&mut self.solver,
&mut self.criteria,
self.max_iter,
)?;
if let StepOutcome::Stopped(reason) = outcome {
self.finished = Some(reason);
}
Ok(outcome)
}
pub fn run_to_end(mut self) -> Result<OptimizationResult<S>, So::Error> {
loop {
if let StepOutcome::Stopped(reason) = self.step()? {
return Ok(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: &mut Problem<P>,
baseline: &EvalCounts,
state_slot: &mut Option<S>,
solver: &mut So,
criteria: &mut [Box<dyn TerminationCriterion<S>>],
max_iter: u64,
) -> Result<StepOutcome, So::Error>
where
S: State + CountsMirror,
So: Solver<P, S>,
{
{
let state = state_slot
.as_ref()
.expect("step_once called with empty state slot");
if state.iter() >= max_iter {
return Ok(StepOutcome::Stopped(TerminationReason::MaxIter));
}
for criterion in criteria.iter_mut() {
if let Some(reason) = criterion.check(state) {
return Ok(StepOutcome::Stopped(reason));
}
}
if let Some(reason) = solver.terminate(state) {
return Ok(StepOutcome::Stopped(reason));
}
}
let prev = state_slot.take().unwrap();
let next_iter_result = solver.next_iter(problem, prev);
let (mut next, mid_iter_reason) = match next_iter_result {
Ok(t) => t,
Err(e) => {
return Err(e);
}
};
next.mirror(&problem.counts().delta_since(baseline));
if let Some(reason) = mid_iter_reason {
*state_slot = Some(next);
return Ok(StepOutcome::Stopped(reason));
}
next.increment_iter();
*state_slot = Some(next);
Ok(StepOutcome::Continue)
}
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>,
{
let baseline = *problem.counts();
let mut state = solver.init(problem, state)?;
state.mirror(&problem.counts().delta_since(&baseline));
let mut slot = Some(state);
let reason = loop {
match step_once(problem, &baseline, &mut slot, solver, criteria, max_iter)? {
StepOutcome::Continue => continue,
StepOutcome::Stopped(reason) => break reason,
}
};
Ok(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 + CountsMirror,
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) -> Result<Stepper<P, S, So>, So::Error> {
let Self {
problem,
state,
mut solver,
max_iter,
criteria,
} = self;
let mut problem = Problem::new(problem);
let mut state = solver.init(&mut problem, state)?;
state.mirror(problem.counts());
Ok(Stepper {
problem,
state: Some(state),
solver,
criteria,
max_iter,
finished: None,
})
}
pub fn run(self) -> Result<OptimizationResult<S>, So::Error> {
self.into_stepper()?.run_to_end()
}
}