use crate::core::executor::OptimizationResult;
use crate::core::inner::{InitialState, InnerExecutor, WarmStart};
use crate::core::math::{
ComponentMulAssign, MatTransposeVec, MatVec, MatrixFromDiagonal,
MatrixIdentity, NormSquared, RankOneUpdate, SampleStandardNormal, Scalar,
ScaleInPlace, ScaledAdd, SymmetricEigen, VectorLen,
};
use crate::core::problem::{CostFunction, Problem};
use crate::core::solver::Solver;
use crate::core::state::{
CountsMirror, FirstOrderState, IntoInitialSimplex, PointState,
PopulationProgress, SimplexProgress, State,
};
use crate::core::termination::Termination;
use crate::solver::cma_es::{CmaEs, sort_population_ascending};
use crate::solver::lbfgs::{Bounded, Lbfgs};
use crate::solver::levenberg_marquardt::LevenbergMarquardt;
use crate::solver::nelder_mead::NelderMead;
pub trait MemeticInner<V, F = f64>: WarmStart<V>
where
F: Scalar,
{
fn seed_scaled(&mut self, x: &V, _sigma: F) -> Self::State {
self.seed(x)
}
}
type ClosureSeedFn<V, S, F> = Box<dyn Fn(&V, F) -> S>;
pub struct ClosureInner<I, S, V, F = f64> {
inner: I,
seed_fn: ClosureSeedFn<V, S, F>,
}
impl<I, S, V, F> ClosureInner<I, S, V, F> {
pub fn new(inner: I, seed_fn: impl Fn(&V, F) -> S + 'static) -> Self {
Self {
inner,
seed_fn: Box::new(seed_fn),
}
}
}
impl<P, I, S, V, F> Solver<P, S> for ClosureInner<I, S, V, F>
where
I: Solver<P, S>,
S: State,
{
type Error = I::Error;
fn init(
&mut self,
problem: &mut Problem<P>,
state: S,
) -> Result<S, Self::Error> {
self.inner.init(problem, state)
}
fn next_iter(
&mut self,
problem: &mut Problem<P>,
state: S,
) -> Result<crate::SolverStep<S>, Self::Error> {
self.inner.next_iter(problem, state)
}
fn terminate(&self, state: &S) -> Option<Termination<S::Float>> {
self.inner.terminate(state)
}
}
impl<I, S, V, F> InitialState<V> for ClosureInner<I, S, V, F>
where
F: Scalar,
S: State<Param = V>,
{
type State = S;
fn seed(&self, x: &V) -> S {
(self.seed_fn)(x, F::zero())
}
}
impl<I, S, V, F> WarmStart<V> for ClosureInner<I, S, V, F>
where
F: Scalar,
S: State<Param = V>,
{
}
impl<I, S, V, F> MemeticInner<V, F> for ClosureInner<I, S, V, F>
where
F: Scalar,
S: State<Param = V>,
{
fn seed_scaled(&mut self, x: &V, sigma: F) -> S {
(self.seed_fn)(x, sigma)
}
}
impl<Mode, V, F> InitialState<V> for NelderMead<V, F, Mode>
where
F: Scalar,
V: VectorLen + Clone + IntoInitialSimplex<V, F>,
{
type State = SimplexProgress<V, F>;
fn seed(&self, x: &V) -> SimplexProgress<V, F> {
SimplexProgress::new(x.clone())
}
}
impl<Mode, V, F> WarmStart<V> for NelderMead<V, F, Mode>
where
F: Scalar,
V: VectorLen + Clone + IntoInitialSimplex<V, F>,
{
}
impl<Mode, V, F> MemeticInner<V, F> for NelderMead<V, F, Mode>
where
F: Scalar,
V: VectorLen + Clone + IntoInitialSimplex<V, F> + crate::VectorIndex<F>,
{
fn seed_scaled(&mut self, x: &V, sigma: F) -> SimplexProgress<V, F> {
let n = x.vec_len();
let mut vertices = Vec::with_capacity(n + 1);
vertices.push(x.clone());
for j in 0..n {
let mut v = x.clone();
v.set_scalar(j, v.get_scalar(j) + sigma);
vertices.push(v);
}
SimplexProgress::from_simplex(vertices)
}
}
impl<V, M, F> InitialState<V> for LevenbergMarquardt<V, M, F>
where
F: Scalar,
V: Clone,
{
type State = PointState<V, F>;
fn seed(&self, x: &V) -> PointState<V, F> {
PointState::new(x.clone())
}
}
impl<V, M, F> WarmStart<V> for LevenbergMarquardt<V, M, F>
where
F: Scalar,
V: Clone,
{
}
impl<V, M, F> MemeticInner<V, F> for LevenbergMarquardt<V, M, F>
where
F: Scalar,
V: Clone,
{
}
impl<Mode, S, V, F> InitialState<V> for Lbfgs<V, F, Mode, S>
where
F: Scalar,
V: Clone,
{
type State = FirstOrderState<V, F>;
fn seed(&self, x: &V) -> FirstOrderState<V, F> {
FirstOrderState::new(x.clone())
}
}
impl<Mode, S, V, F> WarmStart<V> for Lbfgs<V, F, Mode, S>
where
F: Scalar,
V: Clone,
{
}
impl<S, V, F> MemeticInner<V, F> for Lbfgs<V, F, Bounded, S>
where
F: Scalar,
V: Clone,
{
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "I: serde::Serialize, V: serde::Serialize, M: serde::Serialize, F: serde::Serialize",
deserialize = "I: serde::Deserialize<'de>, V: serde::Deserialize<'de>, M: serde::Deserialize<'de>, F: serde::Deserialize<'de>"
))
)]
pub struct CmaInject<I, V, M, F = f64>
where
F: Scalar,
I: MemeticInner<V, F>,
{
cma: CmaEs<V, M, F>,
inner: InnerExecutor<I::State, I>,
#[cfg_attr(feature = "serde", serde(default))]
inner_diagnostics: Option<Vec<crate::core::inner::diagnostics::Record<F>>>,
k: usize,
c_y_override: Option<F>,
}
impl<I, V, M, F> CmaInject<I, V, M, F>
where
F: Scalar,
I: MemeticInner<V, F>,
I::State: CountsMirror,
{
pub fn with_inner_solver(cma: CmaEs<V, M, F>, inner: I) -> Self {
Self {
cma,
inner: InnerExecutor::new(inner).max_iter(50),
k: 1,
c_y_override: None,
inner_diagnostics: None,
}
}
pub fn cma(&self) -> &CmaEs<V, M, F> {
&self.cma
}
pub fn with_k(mut self, k: usize) -> Self {
assert!(k >= 1, "CmaInject requires k >= 1, got {}", k);
self.k = k;
self
}
pub fn with_c_y(mut self, c_y: F) -> Self {
assert!(c_y > F::zero(), "CmaInject requires c_y > 0, got {:?}", c_y);
self.c_y_override = Some(c_y);
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 {
cma,
inner,
k,
c_y_override,
inner_diagnostics,
} = self;
Self {
cma,
inner: inner.max_iter(n),
k,
c_y_override,
inner_diagnostics,
}
}
pub fn with_absolute_distribution_size_tolerance(
mut self,
value: impl Into<Option<F>>,
) -> Self {
self.cma = self.cma.with_absolute_distribution_size_tolerance(value);
self
}
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 {
cma,
inner,
k,
c_y_override,
inner_diagnostics,
} = self;
Self {
cma,
inner: inner.stop_when_factory(make),
k,
c_y_override,
inner_diagnostics,
}
}
}
pub(crate) fn default_c_y<F: Scalar>(n: usize) -> F {
let n = F::from_usize(n).unwrap();
let two = F::from_f64(2.0).unwrap();
n.sqrt() + two * n / (n + two)
}
impl<P, I, V, M, F> Solver<P, PopulationProgress<V, F>>
for CmaInject<I, V, M, F>
where
F: Scalar,
P: CostFunction<Param = V, Output = F>,
I: MemeticInner<V, F>
+ Solver<P, <I as InitialState<V>>::State, Error = P::Error>,
I::State: State<Param = V, Float = F> + CountsMirror,
V: VectorLen
+ Clone
+ ScaledAdd<F>
+ ScaleInPlace<F>
+ ComponentMulAssign
+ NormSquared<F>
+ SampleStandardNormal
+ std::ops::Index<usize, Output = F>
+ std::ops::IndexMut<usize, Output = F>,
M: MatrixIdentity
+ MatrixFromDiagonal<V>
+ MatVec<V>
+ MatTransposeVec<V>
+ ScaleInPlace<F>
+ RankOneUpdate<V, F>
+ SymmetricEigen<V>
+ Clone,
CmaEs<V, M, F>: Solver<P, PopulationProgress<V, F>, Error = P::Error>,
{
type Error = P::Error;
fn init(
&mut self,
problem: &mut Problem<P>,
state: PopulationProgress<V, F>,
) -> Result<PopulationProgress<V, F>, Self::Error> {
if let Some(records) = &mut self.inner_diagnostics {
records.clear();
}
self.cma.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.cma.next_iter(problem, state)?;
if step.termination.is_some() {
return Ok(step);
}
let mut state = step.state;
let work = self.cma.work.as_mut().expect("CMA init precedes injection");
let n = work.m.vec_len();
let m = work.m.clone();
let sigma = work.sigma;
let c_y = self.c_y_override.unwrap_or_else(|| default_c_y::<F>(n));
let refine = self.k.min(state.candidates.len());
for i in 0..refine {
let inner_state = self
.inner
.solver_mut()
.seed_scaled(&state.candidates[i], sigma);
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(sigma),
},
|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,
);
return Ok(crate::SolverStep::from((
state,
inner_result.report.into_outer_termination(
crate::PartialResultPolicy::Consume,
),
)));
}
let x_refined = inner_result.state.param().clone();
let mut y = x_refined;
y.scaled_add(-F::one(), &m);
y.scale_in_place(F::one() / sigma);
let inv_sqrt_norm = {
let mut bt_y = work.b.mat_transpose_vec(&y);
bt_y.component_mul_assign(&work.d_inv);
bt_y.norm_squared().sqrt()
};
if inv_sqrt_norm > F::zero() {
let alpha = (c_y / inv_sqrt_norm).min(F::one());
if alpha < F::one() {
y.scale_in_place(alpha);
}
}
let mut x_inj = m.clone();
x_inj.scaled_add(sigma, &y);
let cost_new = problem.cost(&x_inj)?;
state.candidates[i] = x_inj;
state.costs[i] = cost_new;
}
if refine > 0 {
sort_population_ascending(&mut state.candidates, &mut state.costs);
}
Ok(crate::SolverStep::from((state, None)))
}
fn terminate(
&self,
state: &PopulationProgress<V, F>,
) -> Option<Termination<F>> {
<_ as Solver<P, PopulationProgress<V, F>>>::terminate(&self.cma, state)
}
}