use std::marker::PhantomData;
use rand_distr::uniform::SampleUniform;
use crate::core::constraint::evaluation::BoxProblem;
use crate::core::executor::OptimizationResult;
use crate::core::inner::{InitialState, InnerExecutor};
use crate::core::math::{
SampleUniformBox, Scalar, ScaleInPlace, ScaledAdd, VectorLen,
};
use crate::core::problem::{CostFunction, Problem};
use crate::core::solver::Solver;
use crate::core::state::{CountsMirror, PopulationProgress, State};
use crate::core::termination::Termination;
use crate::solver::cma_es::sort_population_ascending;
use crate::solver::cma_inject::MemeticInner;
use crate::solver::de::De;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "I: serde::Serialize, F: serde::Serialize",
deserialize = "I: serde::Deserialize<'de>, F: serde::Deserialize<'de>"
))
)]
pub struct DeInject<I, V, F = f64>
where
F: Scalar,
I: MemeticInner<V, F>,
{
de: De<F>,
inner: InnerExecutor<I::State, I>,
#[cfg_attr(feature = "serde", serde(default))]
inner_diagnostics: Option<Vec<crate::core::inner::diagnostics::Record<F>>>,
k: usize,
refine_every: u64,
_phantom: PhantomData<V>,
}
impl<I, V, F> DeInject<I, V, F>
where
F: Scalar,
I: MemeticInner<V, F>,
I::State: CountsMirror,
{
pub fn with_inner_solver(de: De<F>, inner: I) -> Self {
Self {
de,
inner: InnerExecutor::new(inner).max_iter(50),
k: 1,
refine_every: 1,
inner_diagnostics: None,
_phantom: PhantomData,
}
}
pub fn with_k(mut self, k: usize) -> Self {
assert!(k >= 1, "DeInject requires k >= 1, got {}", k);
self.k = k;
self
}
pub fn with_refine_every(mut self, n: u64) -> Self {
assert!(n >= 1, "DeInject requires refine_every >= 1, got {}", n);
self.refine_every = n;
self
}
pub fn with_inner_diagnostics(mut self, enabled: bool) -> Self {
self.inner_diagnostics = enabled.then(Vec::new);
self
}
pub fn inner_diagnostics(
&self,
) -> &[crate::core::inner::diagnostics::Record<F>] {
self.inner_diagnostics.as_deref().unwrap_or(&[])
}
pub fn with_inner_max_iter(self, n: u64) -> Self {
let Self {
de,
inner,
k,
refine_every,
inner_diagnostics,
_phantom,
} = self;
Self {
de,
inner: inner.max_iter(n),
k,
refine_every,
inner_diagnostics,
_phantom,
}
}
pub fn inner_stop_when_factory<Mk, CheckFn>(self, make: Mk) -> Self
where
Mk: FnMut() -> CheckFn + 'static,
CheckFn: FnMut(&I::State) -> Option<crate::ApplicationStop> + 'static,
{
let Self {
de,
inner,
k,
refine_every,
inner_diagnostics,
_phantom,
} = self;
Self {
de,
inner: inner.stop_when_factory(make),
k,
refine_every,
inner_diagnostics,
_phantom,
}
}
}
impl<I, V, F> DeInject<I, V, F>
where
F: Scalar,
I: MemeticInner<V, F>,
I::State: CountsMirror,
{
pub fn with_ls_intensity(mut self, evals: u64) -> Self {
self.inner = self.inner.max_cost_evals(evals);
self
}
}
impl<P, I, V, F> Solver<P, PopulationProgress<V, F>> for DeInject<I, V, F>
where
F: Scalar + SampleUniform,
P: CostFunction<Param = V, Output = F> + BoxProblem<Param = V>,
I: MemeticInner<V, F>
+ Solver<
P,
<I as InitialState<V>>::State,
Error = crate::ConstraintSolveError<P::Error>,
>,
I::State: State<Param = V, Float = F> + CountsMirror,
V: VectorLen
+ Clone
+ SampleUniformBox
+ ScaledAdd<F>
+ ScaleInPlace<F>
+ std::ops::Index<usize, Output = F>
+ std::ops::IndexMut<usize, Output = F>,
De<F>: Solver<
P,
PopulationProgress<V, F>,
Error = crate::ConstraintSolveError<P::Error>,
>,
V: crate::VectorLen + std::ops::Index<usize, Output = F>,
{
type Error = crate::ConstraintSolveError<P::Error>;
fn init(
&mut self,
problem: &mut Problem<P>,
state: PopulationProgress<V, F>,
) -> Result<PopulationProgress<V, F>, Self::Error> {
crate::core::constraint::evaluation::validate_box_problem(
problem.inner(),
None,
true,
)
.map_err(crate::core::constraint::evaluation::invalid)?;
if let Some(records) = &mut self.inner_diagnostics {
records.clear();
}
self.de.init(problem, state)
}
fn next_iter(
&mut self,
problem: &mut Problem<P>,
state: PopulationProgress<V, F>,
) -> Result<crate::SolverStep<PopulationProgress<V, F>>, Self::Error> {
let step = self.de.next_iter(problem, state)?;
if step.termination.is_some() {
return Ok(step);
}
let mut state = step.state;
if state.iter() % self.refine_every != 0 {
return Ok(crate::SolverStep::from((state, None)));
}
let lo = problem.inner().lower().clone();
let hi = problem.inner().upper().clone();
let refine = self.k.min(state.candidates.len());
for i in 0..refine {
let x_seed = state.candidates[i].clone();
let c_orig = state.costs[i];
let inner_state =
self.inner.solver_mut().seed_scaled(&x_seed, F::one());
let inner_result: OptimizationResult<I::State> =
crate::core::inner::diagnostics::run(
&mut self.inner_diagnostics,
problem,
crate::core::inner::diagnostics::Context {
outer_iteration: state.iter(),
member: Some(i),
resumed: false,
initial_scale: Some(F::one()),
},
|problem| self.inner.run(problem, inner_state),
)?;
if !inner_result
.report
.termination
.can_continue_as_inner(crate::PartialResultPolicy::Consume)
{
sort_population_ascending(
&mut state.candidates,
&mut state.costs,
);
state.select_best_member();
return Ok(crate::SolverStep::from((
state,
inner_result.report.into_outer_termination(
crate::PartialResultPolicy::Consume,
),
)));
}
let mut x_refined = inner_result.state.param().clone();
let n = x_refined.vec_len();
for j in 0..n {
if x_refined[j] < lo[j] {
x_refined[j] = lo[j];
} else if x_refined[j] > hi[j] {
x_refined[j] = hi[j];
}
}
let c_refined = problem.cost(&x_refined)?;
if c_refined < c_orig {
state.candidates[i] = x_refined;
state.costs[i] = c_refined;
}
}
if refine > 0 {
sort_population_ascending(&mut state.candidates, &mut state.costs);
state.select_best_member();
}
Ok(crate::SolverStep::from((state, None)))
}
fn terminate(
&self,
state: &PopulationProgress<V, F>,
) -> Option<Termination<F>> {
<De<F> as Solver<P, PopulationProgress<V, F>>>::terminate(
&self.de, state,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::solver::nelder_mead::NelderMead;
#[test]
#[should_panic(expected = "DeInject requires k >= 1")]
fn with_k_zero_panics() {
let _ = DeInject::<
crate::RelaxedInner<NelderMead<Vec<f64>>>,
Vec<f64>,
f64,
>::with_inner_solver(
De::new(0),
crate::RelaxedInner::new(NelderMead::new()),
)
.with_k(0);
}
#[test]
#[should_panic(expected = "DeInject requires refine_every >= 1")]
fn with_refine_every_zero_panics() {
let _ = DeInject::<
crate::RelaxedInner<NelderMead<Vec<f64>>>,
Vec<f64>,
f64,
>::with_inner_solver(
De::new(0),
crate::RelaxedInner::new(NelderMead::new()),
)
.with_refine_every(0);
}
}