diff --git a/argmin/src/solver/mod.rs b/argmin/src/solver/mod.rs index 1edf64afe..baf19ab1c 100644 --- a/argmin/src/solver/mod.rs +++ b/argmin/src/solver/mod.rs @@ -15,6 +15,7 @@ pub mod linesearch; pub mod neldermead; pub mod newton; pub mod particleswarm; +pub mod powell; pub mod quasinewton; pub mod simulatedannealing; pub mod trustregion; diff --git a/argmin/src/solver/powell/mod.rs b/argmin/src/solver/powell/mod.rs new file mode 100644 index 000000000..bd93e7e0f --- /dev/null +++ b/argmin/src/solver/powell/mod.rs @@ -0,0 +1,97 @@ +use crate::core::{ + ArgminFloat, CostFunction, DeserializeOwnedAlias, Executor, IterState, LineSearch, + OptimizationResult, SerializeAlias, Solver, State, +}; +use argmin_math::{ArgminAdd, ArgminDot, ArgminSub, ArgminZeroLike}; +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; +use std::mem; + +#[derive(Clone)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct PowellLineSearch { + search_vectors: Vec

, + linesearch: L, +} + +impl PowellLineSearch { + pub fn new(initial_search_vectors: Vec

, linesearch: L) -> Self { + PowellLineSearch { + search_vectors: initial_search_vectors, + linesearch, + } + } +} + +impl Solver> for PowellLineSearch +where + O: CostFunction, + P: Clone + + SerializeAlias + + DeserializeOwnedAlias + + ArgminAdd + + ArgminZeroLike + + ArgminSub + + ArgminDot, + F: ArgminFloat, + L: Clone + LineSearch + Solver>, +{ + const NAME: &'static str = "Powell-LS"; + + fn next_iter( + &mut self, + problem: &mut crate::core::Problem, + mut state: IterState, + ) -> Result<(IterState, Option), anyhow::Error> { + let param = state + .take_param() + .ok_or_else(argmin_error_closure!(NotInitialized, "not initialized"))?; // TODO add Error message + + // new displacement vector created from displacement vectors of line searches + let new_displacement = param.zero_like(); + let mut best_direction: (usize, F) = (0, float!(0.0)); + + // init line search + let (ls_state, _) = self.linesearch.init(problem, state.clone())?; + + // Loop over all search vectors and perform line optimization along each search direction + for (i, search_vector) in self.search_vectors.iter().enumerate() { + self.linesearch.search_direction(search_vector.clone()); + + let line_cost = ls_state.get_cost(); + + // Run solver + let OptimizationResult { + problem: _sub_problem, + state: sub_state, + .. + } = Executor::new(problem.take_problem().unwrap(), self.linesearch.clone()) + .configure(|state| state.param(param.clone()).cost(line_cost)) + .ctrlc(false) + .run()?; + + // update new displacement vector + let displacement = &sub_state.get_best_param().unwrap().sub(¶m); + let displacement_magnitude = displacement.dot(&displacement).sqrt(); + new_displacement.add(displacement); + + //store index of lowest cost displacement vector + if best_direction.1 < displacement_magnitude { + best_direction.0 = i; + best_direction.1 = displacement_magnitude; + } + } + + // replace best performing search direction with new search direction + let _ = mem::replace( + &mut self.search_vectors[best_direction.0], + new_displacement.clone(), + ); + + // set new parameters + let param = param.add(&new_displacement); + let cost = problem.cost(¶m); + + Ok((state.param(param).cost(cost?), None)) + } +}