#![allow(clippy::needless_doctest_main)]
mod state_machine;
use std::ops::{Mul, RangeInclusive, SubAssign};
use derive_getters::{Dissolve, Getters};
pub use optimal_core::prelude::*;
use rand::{
distributions::uniform::{SampleUniform, Uniform},
prelude::*,
};
use self::state_machine::*;
pub use super::StepSize;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Getters, Dissolve)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[dissolve(rename = "into_parts")]
pub struct FixedStepSteepest<A, FD> {
config: Config<A>,
state: State<A>,
obj_func_d: FD,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Config<A> {
pub step_size: StepSize<A>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct State<A>(DynState<A>);
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StateKind {
Started,
Evaluated,
Stepped,
Finished,
}
impl<A, FD> FixedStepSteepest<A, FD> {
fn new(state: State<A>, config: Config<A>, obj_func_d: FD) -> Self {
Self {
config,
obj_func_d,
state,
}
}
}
impl<A, FD> StreamingIterator for FixedStepSteepest<A, FD>
where
A: Clone + SubAssign + Mul<Output = A>,
FD: Fn(&[A]) -> Vec<A>,
{
type Item = Self;
fn advance(&mut self) {
replace_with::replace_with_or_abort(&mut self.state.0, |state| match state {
DynState::Started(x) => DynState::Evaluated(x.into_evaluated(&self.obj_func_d)),
DynState::Evaluated(x) => {
DynState::Stepped(x.into_stepped(self.config.step_size.clone()))
}
DynState::Stepped(x) => DynState::Finished(x.into_finished()),
DynState::Finished(x) => DynState::Started(x.into_started()),
});
}
fn get(&self) -> Option<&Self::Item> {
Some(self)
}
}
impl<A, FD> Optimizer for FixedStepSteepest<A, FD>
where
A: Clone,
{
type Point = Vec<A>;
fn best_point(&self) -> Self::Point {
match &self.state.0 {
DynState::Started(x) => x.point.clone(),
DynState::Evaluated(x) => x.point.x().clone(),
DynState::Stepped(x) => x.point.clone(),
DynState::Finished(x) => x.point.clone(),
}
}
}
impl<A> Config<A> {
pub fn new(step_size: StepSize<A>) -> Self {
Self { step_size }
}
}
impl<A> Config<A> {
pub fn start<FD>(
self,
initial_bounds: impl IntoIterator<Item = RangeInclusive<A>>,
obj_func_d: FD,
) -> FixedStepSteepest<A, FD>
where
A: SampleUniform,
FD: Fn(&[A]) -> Vec<A>,
{
FixedStepSteepest::new(
self.initial_state_using(initial_bounds, &mut thread_rng()),
self,
obj_func_d,
)
}
pub fn start_using<FD, R>(
self,
initial_bounds: impl IntoIterator<Item = RangeInclusive<A>>,
obj_func_d: FD,
rng: &mut R,
) -> FixedStepSteepest<A, FD>
where
A: SampleUniform,
FD: Fn(&[A]) -> Vec<A>,
R: Rng,
{
FixedStepSteepest::new(
self.initial_state_using(initial_bounds, rng),
self,
obj_func_d,
)
}
pub fn start_from<FD>(self, obj_func_d: FD, state: State<A>) -> FixedStepSteepest<A, FD>
where
FD: Fn(&[A]) -> Vec<A>,
{
FixedStepSteepest::new(state, self, obj_func_d)
}
fn initial_state_using<R>(
&self,
initial_bounds: impl IntoIterator<Item = RangeInclusive<A>>,
rng: &mut R,
) -> State<A>
where
A: SampleUniform,
R: Rng,
{
State(DynState::new(
initial_bounds
.into_iter()
.map(|range| {
let (start, end) = range.into_inner();
Uniform::new_inclusive(start, end).sample(rng)
})
.collect(),
))
}
}
impl<A> State<A> {
pub fn new(point: Vec<A>) -> Self {
Self(DynState::new(point))
}
pub fn evaluatee(&self) -> Option<&[A]> {
match &self.0 {
DynState::Started(x) => Some(&x.point),
DynState::Evaluated(_) => None,
DynState::Stepped(_) => None,
DynState::Finished(_) => None,
}
}
pub fn evaluation(&self) -> Option<&[A]> {
match &self.0 {
DynState::Started(_) => None,
DynState::Evaluated(x) => Some(x.point.value()),
DynState::Stepped(_) => None,
DynState::Finished(_) => None,
}
}
pub fn best_point(&self) -> &[A] {
match &self.0 {
DynState::Started(x) => &x.point,
DynState::Evaluated(x) => x.point.x(),
DynState::Stepped(x) => &x.point,
DynState::Finished(x) => &x.point,
}
}
pub fn kind(&self) -> StateKind {
match &self.0 {
DynState::Started(_) => StateKind::Started,
DynState::Evaluated(_) => StateKind::Evaluated,
DynState::Stepped(_) => StateKind::Stepped,
DynState::Finished(_) => StateKind::Finished,
}
}
}