From 7ceeb3f6dc6b8c8d8a7ed52580b40eb7cea5dbc6 Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sat, 1 Apr 2023 18:19:22 +0200 Subject: [PATCH 1/6] Do some renaming and refactors - Rename Population to Populations and rename some methods - Make State generic over the problem type - Switch initialization order of condition in Loop --- Cargo.toml | 2 +- src/components/constraints.rs | 18 ++- src/components/evaluation.rs | 26 ++-- src/components/generation/mod.rs | 32 ++--- src/components/generation/mutation.rs | 66 ++++----- src/components/generation/recombination.rs | 48 +++---- src/components/generation/swarm.rs | 10 +- src/components/initialization.rs | 14 +- src/components/misc.rs | 23 ++-- src/components/replacement.rs | 28 ++-- src/components/selection.rs | 114 +++++++--------- src/conditions/branching.rs | 22 +-- src/conditions/termination.rs | 38 +++--- src/framework/components.rs | 24 ++-- src/framework/conditions.rs | 18 +-- src/framework/configuration.rs | 18 +-- src/framework/objective.rs | 1 + src/heuristics/aco.rs | 24 ++-- src/lib.rs | 2 +- src/prelude.rs | 4 +- src/problems/coco_bound/mod.rs | 8 +- src/problems/coco_bound/suits.rs | 6 +- src/problems/mod.rs | 2 +- src/problems/tsp/symmetric.rs | 8 +- src/state/archive.rs | 20 +-- src/state/common.rs | 19 ++- src/state/container/many.rs | 19 +-- src/state/container/mod.rs | 150 ++++++++++++++------- src/state/cro.rs | 82 ++++++----- src/state/diversity.rs | 14 +- src/state/pso.rs | 49 +++---- src/testing.rs | 4 +- src/tracking/functions.rs | 15 ++- src/tracking/log.rs | 7 +- src/tracking/logger.rs | 4 +- src/tracking/set.rs | 12 +- src/tracking/trigger.rs | 26 ++-- 37 files changed, 512 insertions(+), 465 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af79e09f..99e014ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" anyhow = "1.0.51" ciborium = "0.2.0" coco-rs = "0.5" -derive_deref = "1.1.1" +derive_more = { version = "0.99.17", features = ["deref", "deref_mut"]} embed-doc-image = "0.1.4" erased-serde = "0.3.16" float_eq = "0.7.0" diff --git a/src/components/constraints.rs b/src/components/constraints.rs index 63d44bc5..23cb0728 100644 --- a/src/components/constraints.rs +++ b/src/components/constraints.rs @@ -13,7 +13,7 @@ use crate::{ /// /// Types implementing this trait can implement [Component] by wrapping the type in a [BoundaryConstrainer]. pub trait BoundaryConstraint { - fn constrain(&self, solution: &mut P::Encoding, problem: &P, state: &mut State); + fn constrain(&self, solution: &mut P::Encoding, problem: &P, state: &mut State

); } #[derive(serde::Serialize, Clone)] @@ -24,14 +24,12 @@ where P: Problem, T: AnyComponent + BoundaryConstraint

+ Serialize + Clone, { - fn execute(&self, problem: &P, state: &mut State) { - let mut population = state.population_stack_mut::

().pop(); - + fn execute(&self, problem: &P, state: &mut State

) { + let mut population = state.populations_mut().pop(); for individual in population.iter_mut() { self.0.constrain(individual.solution_mut(), problem, state); } - - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } @@ -47,7 +45,7 @@ impl Saturation { impl> + VectorProblem + LimitedVectorProblem> BoundaryConstraint

for Saturation { - fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State) { + fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State

) { for (d, x) in solution.iter_mut().enumerate() { let range = problem.range(d); *x = x.clamp(range.start, range.end); @@ -68,7 +66,7 @@ impl Toroidal { impl> + VectorProblem + LimitedVectorProblem> BoundaryConstraint

for Toroidal { - fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State) { + fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State

) { for (d, x) in solution.iter_mut().enumerate() { let range = problem.range(d); let a = range.start; @@ -97,7 +95,7 @@ impl Mirror { impl> + VectorProblem + LimitedVectorProblem> BoundaryConstraint

for Mirror { - fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State) { + fn constrain(&self, solution: &mut Vec, problem: &P, _state: &mut State

) { for (d, x) in solution.iter_mut().enumerate() { let range = problem.range(d); let a = range.start; @@ -133,7 +131,7 @@ impl CompleteOneTailedNormalCorrection { impl> + VectorProblem + LimitedVectorProblem> BoundaryConstraint

for CompleteOneTailedNormalCorrection { - fn constrain(&self, solution: &mut Vec, problem: &P, state: &mut State) { + fn constrain(&self, solution: &mut Vec, problem: &P, state: &mut State

) { for (d, x) in solution.iter_mut().enumerate() { let range = problem.range(d); let a = range.start; diff --git a/src/components/evaluation.rs b/src/components/evaluation.rs index ce108d8f..03b3ad8b 100644 --- a/src/components/evaluation.rs +++ b/src/components/evaluation.rs @@ -12,7 +12,7 @@ use crate::{ /// /// This component should be inserted after every generating component. /// -/// Only the head of the [common::Population] will be evaluated. +/// Only the head of the [common::Populations] will be evaluated. /// Requires either [common::EvaluatorInstance] to be present /// in the [State] or [Problem::default_evaluator] to be implemented. /// @@ -28,8 +28,8 @@ impl Evaluator { } impl Component

for Evaluator { - fn initialize(&self, problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, problem: &P, state: &mut State

) { + state.require::>(); state.insert(common::Evaluations(0)); if !state.has::>() { @@ -37,8 +37,8 @@ impl Component

for Evaluator { } } - fn execute(&self, problem: &P, state: &mut State) { - if let Some(mut population) = state.population_stack_mut().try_pop() { + fn execute(&self, problem: &P, state: &mut State

) { + if let Some(mut population) = state.populations_mut().try_pop() { state.holding::>(|evaluator_state, state| { evaluator_state .evaluator @@ -46,7 +46,7 @@ impl Component

for Evaluator { }); *state.get_value_mut::() += population.len() as u32; - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } } @@ -65,15 +65,15 @@ impl UpdateBestIndividual { } impl Component

for UpdateBestIndividual { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { state.insert(common::BestIndividual::

(None)); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); let best_individual = mut_state - .population_stack() + .populations() .current() .iter() .min_by_key(|i| i.objective()); @@ -100,14 +100,14 @@ impl UpdateParetoFront { } impl Component

for UpdateParetoFront { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { state.insert(common::ParetoFront::

::new()); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let front = mut_state.get_mut::>(); - front.update_multiple(mut_state.population_stack().current()); + let front = mut_state.pareto_front_mut(); + front.update_multiple(mut_state.populations().current()); } } diff --git a/src/components/generation/mod.rs b/src/components/generation/mod.rs index 56e6f948..5d7f0ff8 100644 --- a/src/components/generation/mod.rs +++ b/src/components/generation/mod.rs @@ -18,7 +18,7 @@ pub mod swarm; /// Specialized component trait to generate a new population from the current one. /// -/// This trait is especially useful for components which modify solutions independently. +/// This trait is especially useful for components that modify solutions independently. /// For combining multiple solutions, see [Recombination]. /// /// # Implementing [Component] @@ -29,7 +29,7 @@ pub trait Generation { &self, population: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ); } @@ -41,8 +41,8 @@ where P: Problem, T: AnyComponent + Generation

+ Serialize + Clone, { - fn execute(&self, problem: &P, state: &mut State) { - let population = state.population_stack_mut::

().pop(); + fn execute(&self, problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); let mut population = population .into_iter() .map(Individual::into_solution) @@ -52,13 +52,13 @@ where .into_iter() .map(Individual::

::new_unevaluated) .collect(); - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } /// Specialized component trait to generate a new population from the current one. /// -/// This trait is especially useful for components which combine multiple solutions. +/// This trait is especially useful for components that combine multiple solutions. /// For modifying solutions independently, see [Generation]. /// /// # Implementing [Component] @@ -70,7 +70,7 @@ pub trait Recombination { parents: Vec, offspring: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ); } @@ -83,8 +83,8 @@ where T: AnyComponent + Recombination

+ Serialize + Clone, D: Clone + PartialEq + 'static, { - fn execute(&self, problem: &P, state: &mut State) { - let population = state.population_stack_mut::

().pop(); + fn execute(&self, problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); let population = population .into_iter() .map(Individual::into_solution) @@ -96,7 +96,7 @@ where .into_iter() .map(Individual::

::new_unevaluated) .collect(); - state.population_stack_mut().push(offspring); + state.populations_mut().push(offspring); } } @@ -122,7 +122,7 @@ where &self, population: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ) { let population_size = population.len() as u32; *population = self.random_permutation(problem, state.random_mut(), population_size); @@ -151,7 +151,7 @@ where &self, population: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ) { let population_size = population.len() as u32; *population = self.random_spread(problem, state.random_mut(), population_size); @@ -194,7 +194,7 @@ where &self, population: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ) { let population_size = population.len() as u32; *population = self.random_bitstring(problem, state.random_mut(), population_size); @@ -209,11 +209,11 @@ impl DuplicatePopulation { } } impl Component

for DuplicatePopulation { - fn execute(&self, _problem: &P, state: &mut State) { - let population = state.population_stack_mut::

().pop(); + fn execute(&self, _problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); let duplicates = population.clone(); let population = population.into_iter().interleave(duplicates).collect(); - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } diff --git a/src/components/generation/mutation.rs b/src/components/generation/mutation.rs index dd6e1561..e19548f1 100644 --- a/src/components/generation/mutation.rs +++ b/src/components/generation/mutation.rs @@ -39,12 +39,12 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let distribution = rand_distr::Normal::new(0.0, self.deviation).unwrap(); - for solution in population.iter_mut() { - for x in solution.iter_mut() { + for solution in population { + for x in solution { *x += distribution.sample(state.random_mut()); } } @@ -99,13 +99,13 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let deviation = self.deviation(state.get_value::()); let distribution = rand_distr::Normal::new(0.0, deviation).unwrap(); - for solution in population.iter_mut() { - for x in solution.iter_mut() { + for solution in population { + for x in solution { *x += distribution.sample(state.random_mut()); } } @@ -155,7 +155,7 @@ where &self, population: &mut Vec, problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); @@ -180,7 +180,7 @@ mod uniform_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = UniformMutation { rm: 1.0 }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4], vec![0.2, 0.3, 0.6]]; let parents_length = population.len(); @@ -223,13 +223,13 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let distribution = rand_distr::Normal::new(0.0, self.deviation).unwrap(); let rng = state.random_mut(); - for solution in population.iter_mut() { - for x in solution.iter_mut() { + for solution in population { + for x in solution { if rng.gen_bool(self.rm) { *x += distribution.sample(rng); } @@ -252,7 +252,7 @@ mod gaussian_mutation { rm: 1.0, deviation: 0.1, }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4], vec![0.2, 0.3, 0.6]]; let parents_length = population.len(); @@ -292,12 +292,12 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); - for solution in population.iter_mut() { - for x in solution.iter_mut() { + for solution in population { + for x in solution { if rng.gen_bool(self.rm) { *x = !*x; } @@ -330,7 +330,7 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { assert!(self.n_swap > 1); let rng = state.random_mut(); @@ -364,7 +364,7 @@ mod swap_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = SwapMutation { n_swap: 2 }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4, 0.5, 0.9], vec![0.2, 0.3, 0.6, 0.7, 0.8]]; let parents_length = population.len(); @@ -401,7 +401,7 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); @@ -422,7 +422,7 @@ mod scramble_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = ScrambleMutation; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4, 0.5, 0.9], vec![0.2, 0.3, 0.6, 0.7, 0.8]]; let parents_length = population.len(); @@ -459,7 +459,7 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); @@ -481,7 +481,7 @@ mod insertion_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = InsertionMutation; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4, 0.5, 0.9], vec![0.2, 0.3, 0.6, 0.7, 0.8]]; let parents_length = population.len(); @@ -518,7 +518,7 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); for solution in population.iter_mut() { @@ -543,7 +543,7 @@ mod inversion_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = InversionMutation; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4, 0.5, 0.9], vec![0.2, 0.3, 0.6, 0.7, 0.8]]; let parents_length = population.len(); @@ -582,7 +582,7 @@ where &self, population: &mut Vec, _problem: &P, - state: &mut State, + state: &mut State

, ) { let rng = state.random_mut(); for solution in population.iter_mut() { @@ -614,7 +614,7 @@ mod translocation_mutation { fn all_mutated() { let problem = BenchmarkFunction::sphere(3); let comp = TranslocationMutation; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![vec![0.1, 0.2, 0.4, 0.5, 0.9], vec![0.2, 0.3, 0.6, 0.7, 0.8]]; let parents_length = population.len(); @@ -657,7 +657,7 @@ where &self, population: &mut Vec, problem: &P, - _state: &mut State, + _state: &mut State

, ) { assert_eq!(population.len() % (self.y * 2 + 1), 0); @@ -706,7 +706,7 @@ mod de_mutation { let problem = BenchmarkFunction::sphere(3); let y = 1; let comp = DEMutation { y, f: 1. }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let mut population = vec![ vec![0.1, 0.2, 0.4, 0.5, 0.9], @@ -743,15 +743,15 @@ where P: Problem>, D: Clone, { - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { self.mutation.initialize(problem, state); } - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { let mut partial_population = Vec::new(); let mut population_indices = Vec::new(); - let mut population: Vec> = state.population_stack_mut::

().pop(); + let mut population = state.populations_mut().pop(); // Decide which indices/dimensions to mutate, // and keep indices and solution from selected indices @@ -777,9 +777,9 @@ where } // Mutate the partial solutions - state.population_stack_mut::

().push(partial_population); + state.populations_mut().push(partial_population); self.mutation.execute(problem, state); - let partial_population = state.population_stack_mut::

().pop(); + let partial_population = state.populations_mut().pop(); // Insert mutated dimensions into original solutions for (indices, solution, partial) in @@ -792,6 +792,6 @@ where } // Push partially mutated population back - state.population_stack_mut::

().push(population); + state.populations_mut().push(population); } } diff --git a/src/components/generation/recombination.rs b/src/components/generation/recombination.rs index 8f568e74..32cd205e 100644 --- a/src/components/generation/recombination.rs +++ b/src/components/generation/recombination.rs @@ -61,7 +61,7 @@ where parents: Vec>, offspring: &mut Vec>, _problem: &P, - state: &mut State, + state: &mut State

, ) { let dim: usize = parents .iter() @@ -120,7 +120,7 @@ mod npoint_crossover { points: 3, keep_both: true, }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = vec![ vec![0.1, 0.2, 0.4, 0.5, 0.9], @@ -179,7 +179,7 @@ where parents: Vec>, offspring: &mut Vec>, _problem: &P, - state: &mut State, + state: &mut State

, ) { for pairs in parents.chunks(2) { if pairs.len() == 1 { @@ -226,7 +226,7 @@ mod uniform_crossover { pc: 1.0, keep_both: true, }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = vec![ vec![0.1, 0.2, 0.4, 0.5, 0.9], @@ -269,7 +269,7 @@ where parents: Vec>, offspring: &mut Vec>, _problem: &P, - state: &mut State, + state: &mut State

, ) { for pairs in parents.chunks(2) { if pairs.len() == 1 { @@ -325,7 +325,7 @@ mod cycle_crossover { fn all_recombined() { let problem = BenchmarkFunction::sphere(3); let comp = CycleCrossover { pc: 1.0 }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = vec![ vec![8.0, 4.0, 7.0, 3.0, 6.0, 2.0, 5.0, 1.0, 9.0, 0.0], @@ -357,12 +357,12 @@ impl

Component

for DEBinomialCrossover where P: Problem> + VectorProblem, { - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack = mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); - let mut mutations = stack.pop(); - let bases = stack.current(); + let mut mutations = populations.pop(); + let bases = populations.current(); let rng = mut_state.random_mut(); @@ -379,14 +379,14 @@ where } } - stack.push(mutations); + populations.push(mutations); } } #[cfg(test)] mod de_binomial_crossover { use crate::framework::{Individual, Random}; use crate::problems::bmf::BenchmarkFunction; - use crate::state::common::Population; + use crate::state::common::Populations; use super::*; @@ -394,10 +394,10 @@ mod de_binomial_crossover { fn all_recombined() { let problem = BenchmarkFunction::sphere(3); let comp = DEBinomialCrossover { pc: 1.0 }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); - let mut stack = Population::::new(); + let mut stack = Populations::::new(); stack.push( vec![ vec![8.0, 4.0, 7.0, 3.0, 6.0, 2.0, 5.0, 1.0, 9.0, 0.0], @@ -422,7 +422,7 @@ mod de_binomial_crossover { comp.initialize(&problem, &mut state); comp.execute(&problem, &mut state); - let stack = state.population_stack_mut::(); + let stack = state.populations_mut(); let offspring = stack.pop(); let parents = stack.current(); @@ -450,12 +450,12 @@ impl

Component

for DEExponentialCrossover where P: Problem> + VectorProblem, { - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack = mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); - let mut mutations = stack.pop(); - let bases = stack.current(); + let mut mutations = populations.pop(); + let bases = populations.current(); let rng = mut_state.random_mut(); @@ -476,14 +476,14 @@ where } } - stack.push(mutations); + populations.push(mutations); } } #[cfg(test)] mod de_exponential_crossover { use crate::framework::{Individual, Random}; use crate::problems::bmf::BenchmarkFunction; - use crate::state::common::Population; + use crate::state::common::Populations; use super::*; @@ -491,10 +491,10 @@ mod de_exponential_crossover { fn all_recombined() { let problem = BenchmarkFunction::sphere(3); let comp = DEExponentialCrossover { pc: 1.0 }; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); - let mut stack = Population::::new(); + let mut stack = Populations::::new(); stack.push( vec![ vec![8.0, 4.0, 7.0, 3.0, 6.0, 2.0, 5.0, 1.0, 9.0, 0.0], @@ -519,7 +519,7 @@ mod de_exponential_crossover { comp.initialize(&problem, &mut state); comp.execute(&problem, &mut state); - let stack = state.population_stack_mut::(); + let stack = state.populations_mut(); let offspring = stack.pop(); let parents = stack.current(); diff --git a/src/components/generation/swarm.rs b/src/components/generation/swarm.rs index 2713453d..8968261a 100644 --- a/src/components/generation/swarm.rs +++ b/src/components/generation/swarm.rs @@ -41,11 +41,11 @@ impl

Component

for PsoGeneration where P: SingleObjectiveProblem>, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let &Self { weight, c_one, @@ -54,7 +54,7 @@ where } = self; let mut offspring = Vec::new(); - let mut parents = state.population_stack_mut::

().pop(); + let mut parents = state.populations_mut().pop(); let rng = state.random_mut(); let rng_iter = |rng: &mut Random| { @@ -90,6 +90,6 @@ where offspring.push(Individual::

::new_unevaluated(x)); } - state.population_stack_mut().push(offspring); + state.populations_mut().push(offspring); } } diff --git a/src/components/initialization.rs b/src/components/initialization.rs index a3930cf7..c953fbff 100644 --- a/src/components/initialization.rs +++ b/src/components/initialization.rs @@ -15,7 +15,7 @@ use crate::{ /// /// Types implementing this trait can implement [Component] by wrapping the type in a [Initializer]. pub trait Initialization: AnyComponent { - fn initialize_population(&self, problem: &P, state: &mut State) -> Vec>; + fn initialize_population(&self, problem: &P, state: &mut State

) -> Vec>; } #[derive(Serialize, Clone)] @@ -26,9 +26,9 @@ where P: Problem, T: AnyComponent + Initialization

+ Serialize + Clone, { - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { let population = self.0.initialize_population(problem, state); - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } @@ -44,7 +44,7 @@ impl Empty { } } impl Initialization

for Empty { - fn initialize_population(&self, _problem: &P, _state: &mut State) -> Vec> { + fn initialize_population(&self, _problem: &P, _state: &mut State

) -> Vec> { Vec::new() } } @@ -94,7 +94,7 @@ where D: SampleUniform + Clone + PartialOrd + 'static, P: Problem> + LimitedVectorProblem, { - fn initialize_population(&self, problem: &P, state: &mut State) -> Vec> { + fn initialize_population(&self, problem: &P, state: &mut State

) -> Vec> { let population_size = self.initial_population_size.unwrap(); self.random_spread(problem, state.random_mut(), population_size) .into_iter() @@ -142,7 +142,7 @@ impl

Initialization

for RandomPermutation where P: Problem> + VectorProblem, { - fn initialize_population(&self, problem: &P, state: &mut State) -> Vec> { + fn initialize_population(&self, problem: &P, state: &mut State

) -> Vec> { let population_size = self.initial_population_size.unwrap(); self.random_permutation(problem, state.random_mut(), population_size) .into_iter() @@ -209,7 +209,7 @@ impl

Initialization

for RandomBitstring where P: Problem> + VectorProblem, { - fn initialize_population(&self, problem: &P, state: &mut State) -> Vec> { + fn initialize_population(&self, problem: &P, state: &mut State

) -> Vec> { let population_size = self.initial_population_size.unwrap(); self.random_bitstring(problem, state.random_mut(), population_size) .into_iter() diff --git a/src/components/misc.rs b/src/components/misc.rs index 248dfc27..a262cb77 100644 --- a/src/components/misc.rs +++ b/src/components/misc.rs @@ -22,7 +22,7 @@ impl Noop { } } impl Component

for Noop { - fn execute(&self, _problem: &P, _state: &mut State) { + fn execute(&self, _problem: &P, _state: &mut State

) { // Noop } } @@ -36,17 +36,20 @@ impl ClearPopulation { } } impl Component

for ClearPopulation { - fn execute(&self, _problem: &P, state: &mut State) { - state.population_stack_mut::

().current_mut().clear(); + fn execute(&self, _problem: &P, state: &mut State

) { + state.populations_mut().current_mut().clear(); } } /// Helper trait to allow cloning of debug functions. -pub trait DynCustomFunc: Fn(&P, &mut State) + Send + Sync + DynClone + 'static {} +pub trait DynCustomFunc: + Fn(&P, &mut State

) + Send + Sync + DynClone + 'static +{ +} dyn_clone::clone_trait_object!( DynCustomFunc

); impl DynCustomFunc

for F where - F: Fn(&P, &mut State) + Send + Sync + Clone + 'static + F: Fn(&P, &mut State

) + Send + Sync + Clone + 'static { } @@ -59,16 +62,16 @@ impl DynCustomFunc

for F where /// [Component] for your struct. #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""))] -pub struct Debug(Box>); +pub struct Debug(Box>); impl Debug

{ pub fn new( - custom: impl Fn(&P, &mut State) + Send + Sync + Clone + 'static, + custom: impl Fn(&P, &mut State

) + Send + Sync + Clone + 'static, ) -> Box> { Box::new(Self(Box::new(custom))) } } impl Component

for Debug

{ - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { self.0(problem, state); } } @@ -98,13 +101,13 @@ impl Component

for PrintSingleObjectiveSummary where P::Encoding: std::fmt::Debug, { - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let heading = "--- SUMMARY ---"; println!("{}", heading); println!("Iterations: {}", state.iterations()); println!("Evaluations: {}", state.evaluations()); - if let Some(individual) = state.best_individual::

() { + if let Some(individual) = state.best_individual() { println!("Optimum found: {:?}", individual.solution()); println!("Best objective value: {:?}", individual.objective()); } else { diff --git a/src/components/replacement.rs b/src/components/replacement.rs index f0b1c7f5..b3e607bf 100644 --- a/src/components/replacement.rs +++ b/src/components/replacement.rs @@ -20,7 +20,7 @@ pub trait Replacement { &self, parents: &mut Vec>, offspring: &mut Vec>, - state: &mut State, + state: &mut State

, ); } @@ -32,12 +32,12 @@ where P: Problem, T: AnyComponent + Replacement

+ Serialize + Clone, { - fn execute(&self, _problem: &P, state: &mut State) { - let mut offspring = state.population_stack_mut().pop(); - let mut parents = state.population_stack_mut().pop(); + fn execute(&self, _problem: &P, state: &mut State

) { + let mut offspring = state.populations_mut().pop(); + let mut parents = state.populations_mut().pop(); self.0 .replace_population(&mut parents, &mut offspring, state); - state.population_stack_mut().push(parents); + state.populations_mut().push(parents); } } @@ -54,7 +54,7 @@ impl Replacement

for Noop { &self, _parents: &mut Vec>, _offspring: &mut Vec>, - _state: &mut State, + _state: &mut State

, ) { } } @@ -77,7 +77,7 @@ impl Replacement

for MuPlusLambda { &self, parents: &mut Vec>, offspring: &mut Vec>, - _state: &mut State, + _state: &mut State

, ) { parents.append(offspring); parents.sort_unstable_by_key(|i| *i.objective()); @@ -94,7 +94,7 @@ mod mupluslambda { #[test] fn keeps_right_individuals() { - let mut state = State::new_root(); + let mut state = State::new(); let comp = MuPlusLambda { max_population_size: 3, }; @@ -125,7 +125,7 @@ impl Replacement

for Generational { &self, parents: &mut Vec>, offspring: &mut Vec>, - _state: &mut State, + _state: &mut State

, ) { parents.clear(); parents.append(offspring); @@ -140,7 +140,7 @@ mod generational { #[test] fn keeps_all_children() { - let mut state = State::new_root(); + let mut state = State::new(); let comp = Generational { max_population_size: 5, }; @@ -171,7 +171,7 @@ impl Replacement

for RandomReplacement { &self, parents: &mut Vec>, offspring: &mut Vec>, - state: &mut State, + state: &mut State

, ) { parents.append(offspring); parents.shuffle(state.random_mut()); @@ -187,7 +187,7 @@ mod random_replacement { #[test] fn keeps_right_amount_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let comp = RandomReplacement { max_population_size: 5, @@ -213,7 +213,7 @@ impl Replacement

for IndividualPlus { &self, parents: &mut Vec>, offspring: &mut Vec>, - _state: &mut State, + _state: &mut State

, ) { assert_eq!(parents.len(), offspring.len()); @@ -232,7 +232,7 @@ mod greedy_index { #[test] fn keeps_right_amount_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let comp = IndividualPlus; let mut population = new_test_population(&[1.0, 3.0, 5.0, 6.0, 7.0]); diff --git a/src/components/selection.rs b/src/components/selection.rs index 93331e5b..9886c5ed 100644 --- a/src/components/selection.rs +++ b/src/components/selection.rs @@ -22,7 +22,7 @@ pub trait Selection { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

>; } @@ -34,34 +34,25 @@ where P: Problem, T: AnyComponent + Selection

+ Serialize + Clone, { - fn execute(&self, _problem: &P, state: &mut State) { - let population = state.population_stack_mut().pop(); + fn execute(&self, _problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); let selection: Vec<_> = self .0 .select_offspring(&population, state) .into_iter() .cloned() .collect(); - state.population_stack_mut().push(population); - state.population_stack_mut().push(selection); + state.populations_mut().push(population); + state.populations_mut().push(selection); } } /// Helper function to obtain minimum and maximum objective ranges for normalization. fn get_objective_range(population: &[Individual

]) -> (f64, f64) { - let best = population - .iter() - .map(Individual::objective) - .min() - .unwrap() - .value(); - let worst = population - .iter() - .map(Individual::objective) - .max() - .unwrap() - .value(); - (worst, best) + let objectives: Vec<_> = population.iter().map(Individual::objective).collect(); + let min = objectives.iter().min().unwrap().value(); + let max = objectives.iter().max().unwrap().value(); + (max, min) } /// Helper function to calculate normalized fitness weights from the population, @@ -114,7 +105,7 @@ impl Selection

for All { fn select_offspring<'p>( &self, population: &'p [Individual

], - _state: &mut State, + _state: &mut State

, ) -> Vec<&'p Individual

> { population.iter().collect() } @@ -132,7 +123,7 @@ impl Selection

for None { fn select_offspring<'p>( &self, _population: &'p [Individual

], - _state: &mut State, + _state: &mut State

, ) -> Vec<&'p Individual

> { Vec::new() } @@ -153,7 +144,7 @@ impl Selection

for DuplicateSingle { fn select_offspring<'p>( &self, population: &'p [Individual

], - _state: &mut State, + _state: &mut State

, ) -> Vec<&'p Individual

> { assert_eq!(population.len(), 1); let single_solution = population.first().unwrap(); @@ -172,7 +163,7 @@ mod duplicate_single { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); // Note that the population contains exactly one element let population = new_test_population(&[1.0]); let comp = DuplicateSingle { offspring: 4 }; @@ -198,12 +189,11 @@ impl Selection

for FullyRandom { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { - let rng = state.random_mut(); let mut selection = Vec::new(); for _ in 0..self.offspring { - selection.push(population.choose(rng).unwrap()); + selection.push(population.choose(state.random_mut()).unwrap()); } selection } @@ -217,7 +207,7 @@ mod fully_random { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = FullyRandom { offspring: 4 }; @@ -243,11 +233,10 @@ impl Selection

for RandomWithoutRepetition { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { - let rng = state.random_mut(); population - .choose_multiple(rng, self.offspring as usize) + .choose_multiple(state.random_mut(), self.offspring as usize) .collect() } } @@ -260,7 +249,7 @@ mod random_without_repetition { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = RandomWithoutRepetition { offspring: 2 }; @@ -313,7 +302,7 @@ impl Selection

for DeterministicFitnessProportiona fn select_offspring<'p>( &self, population: &'p [Individual

], - _state: &mut State, + _state: &mut State

, ) -> Vec<&'p Individual

> { let (worst, best) = get_objective_range(population); @@ -352,7 +341,7 @@ mod deterministic_fitness_proportional { #[test] fn selects_right_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = DeterministicFitnessProportional { @@ -392,13 +381,12 @@ impl Selection

for RouletteWheel { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { - let rng = state.random_mut(); let weights_min = get_minimizing_weights(population); let wheel = WeightedIndex::new(weights_min).unwrap(); wheel - .sample_iter(rng) + .sample_iter(state.random_mut()) .take(self.offspring as usize) .map(|i| &population[i]) .collect() @@ -413,7 +401,7 @@ mod roulette_wheel { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = RouletteWheel { offspring: 4 }; @@ -440,7 +428,7 @@ impl Selection

for StochasticUniversalSampling { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { let rng = state.random_mut(); let weights_min = get_minimizing_weights(population); @@ -476,7 +464,7 @@ mod stochastic_universal_sampling { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = StochasticUniversalSampling { offspring: 4 }; @@ -504,25 +492,17 @@ impl Selection

for Tournament { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { assert!(population.len() >= self.size as usize); - let rng = state.random_mut(); let mut selection = Vec::new(); - // For each individual for _ in 0..self.offspring { - // choose size competitors in tournament - let mut tournament: Vec<&Individual

> = population - .choose_multiple(rng, self.size as usize) - .collect(); - // and evaluate them against each other, placing the winner first - tournament.sort_unstable_by(|x, y| { - (x.objective().value()) - .partial_cmp(&(y.objective().value())) - .unwrap() - }); - // Add winner (first) to selection - selection.push(tournament[0]); + // Choose `size` competitors in tournament and select the winner + let winner = population + .choose_multiple(state.random_mut(), self.size as usize) + .min_by_key(|&i| i.objective()) + .unwrap(); + selection.push(winner); } assert_eq!(selection.len(), self.offspring as usize); selection @@ -537,7 +517,7 @@ mod tournament { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = Tournament { @@ -566,14 +546,13 @@ impl Selection

for LinearRank { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { - let rng = state.random_mut(); let weights = get_ranking(population); let wheel = WeightedIndex::new(&weights).unwrap(); let mut selection = Vec::new(); for _ in 0..self.offspring { - selection.push(&population[wheel.sample(rng)]); + selection.push(&population[wheel.sample(state.random_mut())]); } assert_eq!(selection.len(), self.offspring as usize); selection @@ -588,7 +567,7 @@ mod linear_rank { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = LinearRank { offspring: 4 }; @@ -618,9 +597,8 @@ impl Selection

for ExponentialRank { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { - let rng = state.random_mut(); let ranking = get_ranking(population); // Weight ranking by exponential equation, worst has smallest weight let weights: Vec = ranking @@ -633,7 +611,7 @@ impl Selection

for ExponentialRank { let wheel = WeightedIndex::new(&weights).unwrap(); let mut selection = Vec::new(); for _ in 0..self.offspring { - selection.push(&population[wheel.sample(rng)]); + selection.push(&population[wheel.sample(state.random_mut())]); } assert_eq!(selection.len(), self.offspring as usize); selection @@ -648,7 +626,7 @@ mod exponential_rank { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let comp = ExponentialRank { @@ -679,7 +657,7 @@ impl Selection

for DERand { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { (0..population.len()) .flat_map(|_| population.choose_multiple(state.random_mut(), self.y * 2 + 1)) @@ -695,7 +673,7 @@ mod de_rand { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let y = 1; @@ -728,7 +706,7 @@ impl Selection

for DEBest { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { let mut offspring = Vec::new(); @@ -750,7 +728,7 @@ mod de_best { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let y = 1; @@ -779,7 +757,7 @@ impl Selection

for DECurrentToBest { fn select_offspring<'p>( &self, population: &'p [Individual

], - state: &mut State, + state: &mut State

, ) -> Vec<&'p Individual

> { let mut offspring = Vec::new(); @@ -809,7 +787,7 @@ mod de_current_to_best { #[test] fn selects_right_number_of_children() { - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Random::testing()); let population = new_test_population(&[1.0, 2.0, 3.0]); let y = 1; diff --git a/src/conditions/branching.rs b/src/conditions/branching.rs index 37bafe97..72745fcb 100644 --- a/src/conditions/branching.rs +++ b/src/conditions/branching.rs @@ -22,7 +22,7 @@ impl

Condition

for RandomChance where P: Problem, { - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { state.random_mut().gen_bool(self.p) } } @@ -44,8 +44,8 @@ impl

Condition

for LessThanNIndividuals where P: Problem, { - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { - state.population_stack::

().current().len() < self.n + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { + state.populations().current().len() < self.n } } @@ -67,13 +67,13 @@ impl

Condition

for DecompositionCriterion where P: Problem, { - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { let mut mut_state = state.get_states_mut(); let cro_state = mut_state.get::>(); - let stack = mut_state.population_stack::

(); + let populations = mut_state.populations(); - let selected = stack.peek(0).first().unwrap(); - let population = stack.peek(1); + let selected = populations.peek(0).first().unwrap(); + let population = populations.peek(1); let selected_index = population.iter().position(|i| i == selected).unwrap(); let molecule = &cro_state.molecules[selected_index]; @@ -100,13 +100,13 @@ impl

Condition

for SynthesisCriterion where P: Problem, { - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { let mut mut_state = state.get_states_mut(); let cro_state = mut_state.get::>(); - let stack = mut_state.population_stack::

(); + let populations = mut_state.populations(); - let [s1, s2] = TryInto::<&[_; 2]>::try_into(stack.peek(0)).unwrap(); - let population = stack.peek(1); + let [s1, s2] = TryInto::<&[_; 2]>::try_into(populations.peek(0)).unwrap(); + let population = populations.peek(1); let s1_index = population.iter().position(|i| i == s1).unwrap(); let s1_molecule = &cro_state.molecules[s1_index]; diff --git a/src/conditions/termination.rs b/src/conditions/termination.rs index 59ad1af7..a11d40f2 100644 --- a/src/conditions/termination.rs +++ b/src/conditions/termination.rs @@ -25,8 +25,8 @@ impl

Condition

for TargetHit where P: SingleObjectiveProblem + HasKnownTarget, { - fn evaluate(&self, problem: &P, state: &mut State) -> bool { - if let Some(fitness) = state.best_objective_value::

() { + fn evaluate(&self, problem: &P, state: &mut State

) -> bool { + if let Some(fitness) = state.best_objective_value() { !problem.target_hit(*fitness) } else { false @@ -54,12 +54,12 @@ impl

Condition

for FixedIterations where P: Problem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::(); state.insert(Progress(0.)); } - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { let iterations = state.iterations(); state.set_value::(iterations as f64 / self.max_iterations as f64); @@ -74,7 +74,7 @@ mod fixed_iterations { #[test] fn terminates() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Iterations(0)); let comp = FixedIterations { max_iterations: 200, @@ -89,7 +89,7 @@ mod fixed_iterations { #[test] fn updates_progress() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Iterations(0)); let comp = FixedIterations { max_iterations: 200, @@ -124,12 +124,12 @@ impl

Condition

for FixedEvaluations where P: Problem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::(); state.insert(Progress(0.)); } - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { let evaluations = state.evaluations(); state.set_value::(evaluations as f64 / self.max_evaluations as f64); evaluations < self.max_evaluations @@ -143,7 +143,7 @@ mod fixed_evaluations { #[test] fn terminates() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Evaluations(0)); let comp = FixedEvaluations { max_evaluations: 200, @@ -158,7 +158,7 @@ mod fixed_evaluations { #[test] fn updates_progress() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(Evaluations(0)); let comp = FixedEvaluations { max_evaluations: 200, @@ -194,8 +194,8 @@ impl Condition

for DistanceToOpt where P: Problem, { - fn evaluate(&self, problem: &P, state: &mut State) -> bool { - state.best_objective_value::

().unwrap().value() + fn evaluate(&self, problem: &P, state: &mut State

) -> bool { + state.best_objective_value().unwrap().value() >= problem.known_optimum().value() + self.distance } } @@ -208,7 +208,7 @@ mod distance_to_opt { #[test] fn terminates() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); state.insert(common::BestIndividual::::default()); let comp = DistanceToOpt { distance: 0.1 }; @@ -261,15 +261,15 @@ impl

Condition

for StepsWithoutImprovement where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { state.insert(FitnessImprovementState { current_steps: 0, current_objective: Default::default(), }) } - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { - let best_fitness = *state.best_objective_value::

().unwrap(); + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { + let best_fitness = *state.best_objective_value().unwrap(); let termination_state = state.get_mut::(); termination_state.update(&best_fitness); @@ -285,7 +285,7 @@ mod steps_without_improvement { #[test] fn terminates() { let problem = TestProblem; - let mut state = State::new_root(); + let mut state = State::new(); let comp = StepsWithoutImprovement { steps: 20 }; state.insert(FitnessImprovementState { current_steps: 0, diff --git a/src/framework/components.rs b/src/framework/components.rs index 48fc650e..5511f0a7 100644 --- a/src/framework/components.rs +++ b/src/framework/components.rs @@ -27,8 +27,8 @@ trait_set! { /// change during a run. All mutable state has to be stored in the [State]. pub trait Component: AnyComponent { #[allow(unused_variables)] - fn initialize(&self, problem: &P, state: &mut State) {} - fn execute(&self, problem: &P, state: &mut State); + fn initialize(&self, problem: &P, state: &mut State

) {} + fn execute(&self, problem: &P, state: &mut State

); } erased_serde::serialize_trait_object!( Component

); dyn_clone::clone_trait_object!( Component

); @@ -48,11 +48,11 @@ pub struct Scope { body: Box>, #[serde(skip)] - init: fn(&mut State), + init: fn(&mut State

), } impl Component

for Scope

{ - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { state.with_substate(|state| { (self.init)(state); self.body.initialize(problem, state); @@ -72,7 +72,7 @@ impl Scope

{ /// Creates a new [Scope] while overriding some state. pub fn new_with( - init: fn(&mut State), + init: fn(&mut State

), body: Vec>>, ) -> Box> { let body = Block::new(body); @@ -90,13 +90,13 @@ impl Scope

{ pub struct Block(Vec>>); impl Component

for Block

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { for component in &self.0 { component.initialize(problem, state); } } - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { for component in &self.0 { component.execute(problem, state); } @@ -124,14 +124,14 @@ pub struct Loop { } impl Component

for Loop

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { state.insert(common::Iterations(0)); - self.condition.initialize(problem, state); self.body.initialize(problem, state); + self.condition.initialize(problem, state); } - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { self.condition.initialize(problem, state); while self.condition.evaluate(problem, state) { self.body.execute(problem, state); @@ -161,7 +161,7 @@ pub struct Branch { } impl Component

for Branch

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { self.condition.initialize(problem, state); self.if_body.initialize(problem, state); if let Some(else_body) = &self.else_body { @@ -169,7 +169,7 @@ impl Component

for Branch

{ } } - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { if self.condition.evaluate(problem, state) { self.if_body.execute(problem, state); } else if let Some(else_body) = &self.else_body { diff --git a/src/framework/conditions.rs b/src/framework/conditions.rs index 05786f16..95b675d0 100644 --- a/src/framework/conditions.rs +++ b/src/framework/conditions.rs @@ -10,10 +10,10 @@ use crate::{framework::components::AnyComponent, problems::Problem, state::State /// but `evaluate` replaces `execute` and returns a `bool`. /// /// These can be combined using binary AND and OR (`|` and `&`). -pub trait Condition

: AnyComponent { +pub trait Condition: AnyComponent { #[allow(unused_variables)] - fn initialize(&self, problem: &P, state: &mut State) {} - fn evaluate(&self, problem: &P, state: &mut State) -> bool; + fn initialize(&self, problem: &P, state: &mut State

) {} + fn evaluate(&self, problem: &P, state: &mut State

) -> bool; } erased_serde::serialize_trait_object!( Condition

); dyn_clone::clone_trait_object!( Condition

); @@ -29,13 +29,13 @@ impl And

{ } } impl Condition

for And

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { for condition in self.0.iter() { condition.initialize(problem, state); } } - fn evaluate(&self, problem: &P, state: &mut State) -> bool { + fn evaluate(&self, problem: &P, state: &mut State

) -> bool { self.0 .iter() .all(|condition| condition.evaluate(problem, state)) @@ -60,13 +60,13 @@ impl Or

{ } } impl Condition

for Or

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { for condition in self.0.iter() { condition.initialize(problem, state); } } - fn evaluate(&self, problem: &P, state: &mut State) -> bool { + fn evaluate(&self, problem: &P, state: &mut State

) -> bool { self.0 .iter() .any(|condition| condition.evaluate(problem, state)) @@ -91,11 +91,11 @@ impl Not

{ } } impl Condition

for Not

{ - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { self.0.initialize(problem, state); } - fn evaluate(&self, problem: &P, state: &mut State) -> bool { + fn evaluate(&self, problem: &P, state: &mut State

) -> bool { !self.0.evaluate(problem, state) } } diff --git a/src/framework/configuration.rs b/src/framework/configuration.rs index ae34c175..3fbe4acd 100644 --- a/src/framework/configuration.rs +++ b/src/framework/configuration.rs @@ -47,13 +47,13 @@ impl Configuration

{ /// /// For initializing the state with custom state, /// see [optimize_with][Configuration::optimize_with]. - pub fn optimize(&self, problem: &P) -> state::State { + pub fn optimize(&self, problem: &P) -> state::State

{ let heuristic = self.heuristic(); - let mut state = state::State::new_root(); + let mut state = state::State::new(); state.insert(tracking::Log::new()); state.insert(Random::default()); - state.insert(state::common::Population::

::new()); + state.insert(state::common::Populations::

::new()); heuristic.initialize(problem, &mut state); heuristic.execute(problem, &mut state); @@ -71,13 +71,13 @@ impl Configuration

{ pub fn optimize_with<'a>( &self, problem: &P, - init_state: impl FnOnce(&mut state::State<'a>), - ) -> state::State<'a> { + init_state: impl FnOnce(&mut state::State<'a, P>), + ) -> state::State<'a, P> { let heuristic = self.heuristic(); - let mut state = state::State::new_root(); + let mut state = state::State::new(); state.insert(tracking::Log::new()); - state.insert(state::common::Population::

::new()); + state.insert(state::common::Populations::

::new()); init_state(&mut state); @@ -188,7 +188,7 @@ impl ConfigurationBuilder

{ #[track_caller] pub fn assert( self, - assert: impl Fn(&state::State) -> bool + Send + Sync + Clone + 'static, + assert: impl Fn(&state::State

) -> bool + Send + Sync + Clone + 'static, ) -> Self { self.debug(move |_problem, state| assert!(assert(state))) } @@ -196,7 +196,7 @@ impl ConfigurationBuilder

{ /// Constructs a [Debug][components::misc::Debug] component with the given behaviour. pub fn debug( self, - behaviour: impl Fn(&P, &mut state::State) + Send + Sync + Clone + 'static, + behaviour: impl Fn(&P, &mut state::State

) + Send + Sync + Clone + 'static, ) -> Self { self.do_(components::misc::Debug::new(behaviour)) } diff --git a/src/framework/objective.rs b/src/framework/objective.rs index e015d297..b1340f13 100644 --- a/src/framework/objective.rs +++ b/src/framework/objective.rs @@ -5,6 +5,7 @@ use std::fmt; use trait_set::trait_set; trait_set! { + /// Collection of traits required by every objective. pub trait AnyObjective = fmt::Debug + Clone + Eq + Any + PartialOrd + Send + Sync } diff --git a/src/heuristics/aco.rs b/src/heuristics/aco.rs index 971aeb8b..1a989233 100644 --- a/src/heuristics/aco.rs +++ b/src/heuristics/aco.rs @@ -135,7 +135,7 @@ pub fn aco( .build_component() } -mod ant_ops { +pub mod ant_ops { use crate::state::PheromoneMatrix; use crate::{ framework::{components::*, Individual, Random, SingleObjective}, @@ -167,14 +167,14 @@ mod ant_ops { } } impl Component for AcoGeneration { - fn initialize(&self, problem: &SymmetricTsp, state: &mut State) { + fn initialize(&self, problem: &SymmetricTsp, state: &mut State) { state.insert(PheromoneMatrix::new( problem.dimension, self.default_pheromones, )); } - fn execute(&self, problem: &SymmetricTsp, state: &mut State) { + fn execute(&self, problem: &SymmetricTsp, state: &mut State) { let (pm, rng) = state.get_multiple_mut::<(PheromoneMatrix, Random)>(); let mut routes = Vec::new(); @@ -222,7 +222,7 @@ mod ant_ops { .into_iter() .map(Individual::::new_unevaluated) .collect(); - *state.population_stack_mut().current_mut() = population; + *state.populations_mut().current_mut() = population; } } @@ -240,14 +240,14 @@ mod ant_ops { } } impl Component for AsPheromoneUpdate { - fn initialize(&self, _problem: &SymmetricTsp, state: &mut State) { - state.require::(); + fn initialize(&self, _problem: &SymmetricTsp, state: &mut State) { + state.require::(); } - fn execute(&self, _problem: &SymmetricTsp, state: &mut State) { + fn execute(&self, _problem: &SymmetricTsp, state: &mut State) { let mut mut_state = state.get_states_mut(); let pm = mut_state.get_mut::(); - let population = mut_state.population_stack::().current(); + let population = mut_state.populations().current(); // Evaporation *pm *= 1.0 - self.evaporation; @@ -289,14 +289,14 @@ mod ant_ops { } } impl Component for MinMaxPheromoneUpdate { - fn initialize(&self, _problem: &SymmetricTsp, state: &mut State) { - state.require::(); + fn initialize(&self, _problem: &SymmetricTsp, state: &mut State) { + state.require::(); } - fn execute(&self, _problem: &SymmetricTsp, state: &mut State) { + fn execute(&self, _problem: &SymmetricTsp, state: &mut State) { let mut mut_state = state.get_states_mut(); let pm = mut_state.get_mut::(); - let population = mut_state.population_stack::().current(); + let population = mut_state.populations().current(); // Evaporation *pm *= 1.0 - self.evaporation; diff --git a/src/lib.rs b/src/lib.rs index f87da5e9..91037ab4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub mod utils; pub mod testing; // re-exports -pub use derive_deref; +pub use derive_more; pub use float_eq; pub use rand; pub use rand_distr; diff --git a/src/prelude.rs b/src/prelude.rs index a95cc8f0..3a91962e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,5 +5,7 @@ pub use crate::{ conditions::*, framework::{self, Configuration, Random}, heuristics::*, - problems, tracking, + problems, + state::{self, State}, + tracking, }; diff --git a/src/problems/coco_bound/mod.rs b/src/problems/coco_bound/mod.rs index a719cecd..c84e3b6f 100644 --- a/src/problems/coco_bound/mod.rs +++ b/src/problems/coco_bound/mod.rs @@ -1,9 +1,9 @@ use std::ops::RangeInclusive; use crate::{ - framework::SingleObjective, + framework::{Individual, SingleObjective}, problems::{self, Evaluator}, - state::common::EvaluatorInstance, + state::{common::EvaluatorInstance, State}, }; pub use coco_rs::{Problem, Suite}; @@ -92,8 +92,8 @@ impl Evaluator for CocoEvaluator<'_> { fn evaluate( &mut self, _problem: &Self::Problem, - _state: &mut crate::state::State, - individuals: &mut [crate::framework::Individual], + _state: &mut State, + individuals: &mut [Individual], ) { for individual in individuals { let mut out = [0.0]; diff --git a/src/problems/coco_bound/suits.rs b/src/problems/coco_bound/suits.rs index 4683c9df..b43c1dd1 100644 --- a/src/problems/coco_bound/suits.rs +++ b/src/problems/coco_bound/suits.rs @@ -33,7 +33,7 @@ pub fn evaluate_suite( mut suite: Suite, configuration: Configuration, output_dir: &str, - setup: impl Fn(&mut State) + Send + Sync, + setup: impl Fn(&mut State) + Send + Sync, ) -> anyhow::Result<()> { #[allow(unused_variables)] let num_threads = 1; @@ -103,9 +103,7 @@ pub fn evaluate_suite( let log = state.get::(); files::write_log_file(log_file, log)?; - let target_hit = if let Some(fitness) = - state.best_objective_value::() - { + let target_hit = if let Some(fitness) = state.best_objective_value() { instance.target_hit(*fitness) } else { false diff --git a/src/problems/mod.rs b/src/problems/mod.rs index a3337564..8c44cf47 100644 --- a/src/problems/mod.rs +++ b/src/problems/mod.rs @@ -54,7 +54,7 @@ pub trait Evaluator: Send { fn evaluate( &mut self, problem: &Self::Problem, - state: &mut State, + state: &mut State, individuals: &mut [Individual], ); } diff --git a/src/problems/tsp/symmetric.rs b/src/problems/tsp/symmetric.rs index e16c694e..71de0ed8 100644 --- a/src/problems/tsp/symmetric.rs +++ b/src/problems/tsp/symmetric.rs @@ -1,12 +1,12 @@ //! This module contains instances of the symmetric traveling salesman problem. use crate::{ - framework::SingleObjective, + framework::{Individual, SingleObjective}, problems::{ tsp::{Coordinates, Dimension, DistanceMeasure, Edge, Route}, Evaluator, Problem, VectorProblem, }, - state::common::EvaluatorInstance, + state::{common::EvaluatorInstance, State}, }; use anyhow::{anyhow, Error, Result}; use pest_consume::Parser; @@ -319,8 +319,8 @@ impl Evaluator for SymmetricTspEvaluator { fn evaluate( &mut self, problem: &Self::Problem, - _state: &mut crate::state::State, - individuals: &mut [crate::framework::Individual], + _state: &mut State, + individuals: &mut [Individual], ) { for individual in individuals { individual.evaluate(problem.evaluate_solution(individual.solution())); diff --git a/src/state/archive.rs b/src/state/archive.rs index 1dd6c3ac..8ae0e9ee 100644 --- a/src/state/archive.rs +++ b/src/state/archive.rs @@ -55,16 +55,16 @@ impl ElitistArchive

{ where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.insert::>(ElitistArchive::new()); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.insert(ElitistArchive::

::new()); } - fn execute(&self, _problem: &P, state: &mut State) { - let population = state.population_stack_mut().pop(); + fn execute(&self, _problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); state .get_mut::>() .state_update(&population, self.n_elitists); - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } @@ -80,12 +80,12 @@ impl ElitistArchive

{ where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { - let mut population = state.population_stack_mut().pop(); + fn execute(&self, _problem: &P, state: &mut State

) { + let mut population = state.populations_mut().pop(); let elitism_state = state.get::>(); for elitist in elitism_state.elitists() { @@ -94,7 +94,7 @@ impl ElitistArchive

{ } } - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } diff --git a/src/state/common.rs b/src/state/common.rs index 6ec15a9d..bea8202c 100644 --- a/src/state/common.rs +++ b/src/state/common.rs @@ -8,7 +8,7 @@ use crate::{ problems::{MultiObjectiveProblem, SingleObjectiveProblem}, }; use better_any::Tid; -use derive_deref::{Deref, DerefMut}; +use derive_more::{Deref, DerefMut}; use serde::Serialize; /// Instance of an [Evaluator] stored in the state. @@ -34,8 +34,8 @@ impl<'a, P: Problem> EvaluatorInstance<'a, P> { /// Wraps a function as an evaluator. /// /// Good for simple, stateless evaluators. - pub fn functional(evaluation: fn(&P, &mut State, &mut [Individual

])) -> Self { - struct FunctionalEvaluator(fn(&P, &mut State, &mut [Individual

])); + pub fn functional(evaluation: fn(&P, &mut State

, &mut [Individual

])) -> Self { + struct FunctionalEvaluator(fn(&P, &mut State

, &mut [Individual

])); impl Evaluator for FunctionalEvaluator

{ type Problem = P; @@ -43,7 +43,7 @@ impl<'a, P: Problem> EvaluatorInstance<'a, P> { fn evaluate( &mut self, problem: &Self::Problem, - state: &mut crate::state::State, + state: &mut State

, individuals: &mut [Individual], ) { (self.0)(problem, state, individuals) @@ -137,16 +137,13 @@ impl Default for ParetoFront

{ } } -#[derive(Deref, DerefMut, Tid)] -pub struct Loop(pub bool); -impl CustomState<'_> for Loop {} - #[derive(Default, Tid)] -pub struct Population { +pub struct Populations { stack: Vec>>, } -impl CustomState<'_> for Population

{} -impl Population

{ + +impl CustomState<'_> for Populations

{} +impl Populations

{ pub fn new() -> Self { Self { stack: Vec::new() } } diff --git a/src/state/container/many.rs b/src/state/container/many.rs index 7243835c..90636963 100644 --- a/src/state/container/many.rs +++ b/src/state/container/many.rs @@ -1,7 +1,10 @@ use std::ops::{Deref, DerefMut}; use std::{any::TypeId, collections::HashSet}; -use crate::state::{CustomState, State}; +use crate::{ + problems::Problem, + state::{CustomState, State}, +}; /// Allows borrowing multiple [CustomState]'s mutable from [State] at the same time. /// It is meant to significantly simplify the definition of [Component][crate::framework::components::Component]'s @@ -16,12 +19,12 @@ use crate::state::{CustomState, State}; /// /// The only exception to this rule are [get_value][MutState::get_value] and [set_value][MutState::set_value], /// which can be called repeatedly using the same [CustomState], given that no reference to it already exists. -pub struct MutState<'a, 's> { - state: &'a mut State<'s>, +pub struct MutState<'a, 's, P> { + state: &'a mut State<'s, P>, borrowed: HashSet, } -impl<'a, 's> MutState<'a, 's> { - pub(super) fn new(state: &'a mut State<'s>) -> Self { +impl<'a, 's, P: Problem> MutState<'a, 's, P> { + pub(super) fn new(state: &'a mut State<'s, P>) -> Self { Self { state, borrowed: HashSet::new(), @@ -85,7 +88,7 @@ pub trait MultiStateTuple<'a, 's>: 'a { fn validate() -> bool; #[track_caller] - fn fetch(state: &'a mut State<'s>) -> Self::References; + fn fetch(state: &'a mut State<'s, P>) -> Self::References; } macro_rules! impl_multi_state_tuple { @@ -101,10 +104,10 @@ macro_rules! impl_multi_state_tuple { $(set.insert($item::id()))&&* } - fn fetch(state: &'a mut State<'s>) -> Self::References { + fn fetch(state: &'a mut State<'s, P>) -> Self::References { assert!(Self::validate(), "each type can only be borrowed once"); - let state = state as *mut State; + let state = state as *mut State

; unsafe { ($((*state).get_mut::<$item>()),*) } } } diff --git a/src/state/container/mod.rs b/src/state/container/mod.rs index 5d4a1284..60e5369b 100644 --- a/src/state/container/mod.rs +++ b/src/state/container/mod.rs @@ -23,31 +23,39 @@ pub(crate) use map::StateMap; pub trait CustomState<'a>: Tid<'a> + Send {} /// Container for storing and managing state. -#[derive(Default)] -pub struct State<'a> { - parent: Option>>, +pub struct State<'a, P> { + parent: Option>>, map: StateMap<'a>, + _phantom: std::marker::PhantomData

, } -impl<'a> State<'a> { +impl Default for State<'_, P> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, P: Problem> State<'a, P> { /// Creates a new state container. /// /// Only needed for tests. - pub fn new_root() -> Self { + pub fn new() -> Self { State { parent: None, map: StateMap::new(), + _phantom: std::marker::PhantomData, } } /// Runs a closure within a new scope. - pub fn with_substate(&mut self, fun: impl FnOnce(&mut State)) { - let mut substate: State = Self { + pub fn with_substate(&mut self, fun: impl FnOnce(&mut State

)) { + let mut substate: State

= Self { parent: Some(Box::new(std::mem::take(self))), map: StateMap::new(), + _phantom: std::marker::PhantomData, }; fun(&mut substate); - *self = *substate.parent.unwrap() + *self = *substate.parent.unwrap(); } /// Returns the parent state. @@ -65,7 +73,15 @@ impl<'a> State<'a> { self.map.insert(state); } - /// Tires to find an inner map containing T. + pub fn transmute(self: State<'a, P>) -> State<'a, O> { + State { + parent: self.parent.map(|parent| Box::new(parent.transmute::())), + map: self.map, + _phantom: std::marker::PhantomData::, + } + } + + /// Tries to find an inner map containing T. fn find>(&self) -> Option<&Self> { if self.map.has::() { Some(self) @@ -93,10 +109,11 @@ impl<'a> State<'a> { /// This is the recommended way to ensure the state /// is available in [Component::initialize](crate::framework::components::Component::initialize). #[track_caller] - pub fn require>(&self) { + pub fn require>(&self) { assert!( self.has::(), - "operator requires {} state", + "{} requires {} state", + std::any::type_name::(), std::any::type_name::() ); } @@ -178,7 +195,7 @@ impl<'a> State<'a> { /// ``` /// use mahf::{state::{State, common::Population}, framework::Random, problems::bmf::BenchmarkFunction}; /// let problem = BenchmarkFunction::sphere(3); - /// let mut state = State::new_root(); + /// let mut state = State::new(); /// state.insert(Random::testing()); /// state.insert(Population::::new()); /// @@ -188,7 +205,7 @@ impl<'a> State<'a> { /// /// // Do something with rng and population, or borrow additional types. /// ``` - pub fn get_states_mut<'b>(&'b mut self) -> MutState<'b, 'a> { + pub fn get_states_mut<'b>(&'b mut self) -> MutState<'b, 'a, P> { MutState::new(self) } @@ -236,7 +253,7 @@ impl<'a> State<'a> { /// ``` /// use mahf::{state::{State, common::Population}, framework::Random, problems::bmf::BenchmarkFunction}; /// let problem = BenchmarkFunction::sphere(3); - /// let mut state = State::new_root(); + /// let mut state = State::new(); /// state.insert(Random::testing()); /// state.insert(Population::::new()); /// @@ -250,70 +267,107 @@ impl<'a> State<'a> { } } -/// Convenience functions for often required state. -/// -/// If some state does not exist, the function will panic. -macro_rules! impl_convenience_functions { - ($l:lifetime, $t:ty) => { +macro_rules! impl_convenience_methods { + ($lifetime:lifetime, $self_type:ty) => { /// Returns [Iterations](common::Iterations) state. - pub fn iterations(self: $t) -> u32 { + pub fn iterations(self: $self_type) -> u32 { self.get_value::() } /// Returns [Evaluations](common::Evaluations) state. - pub fn evaluations(self: $t) -> u32 { + pub fn evaluations(self: $self_type) -> u32 { self.get_value::() } - /// Returns [BestIndividual](common::BestIndividual) state. - pub fn best_individual(self: $t) -> Option<&$l Individual

> { - self.get::>().as_ref() + /// Returns [Population](common::Populations) state. + pub fn populations(self: $self_type) -> &$lifetime common::Populations

{ + self.get::>() } - /// Returns the objective value of the [BestIndividual](common::BestIndividual). - pub fn best_objective_value( - self: $t, - ) -> Option<&SingleObjective> { - self.best_individual::

().map(|i| i.objective()) + /// Returns mutable [Population](common::Populations) state. + pub fn populations_mut(&mut self) -> &$lifetime mut common::Populations

{ + self.get_mut::>() } - /// Returns [ParetoFront](common::ParetoFront) state. - pub fn pareto_front(self: $t) -> &$l common::ParetoFront

{ - self.get::>() + /// Returns mutable [Random](random::Random) state. + pub fn random_mut(&mut self) -> &$lifetime mut Random { + self.get_mut::() + } + + /// Returns the [Log]. + pub fn log(self: $self_type) -> &$lifetime Log { + self.get::() + } + }; +} + +impl<'a, P: Problem> State<'a, P> { + // Uses '_ as 'self lifetime. + // This has to match the lifetime bounds of [State::get]. + impl_convenience_methods!('_, &Self); +} + +impl<'a, 's, P: Problem> MutState<'a, 's, P> { + // Uses 'a as the internal [State]s lifetime. + // This has to match the lifetime bounds of [MutState::get]. + impl_convenience_methods!('a, &mut Self); +} + +macro_rules! impl_single_objective_convenience_methods { + ($lifetime:lifetime, $self_type:ty) => { + /// Returns [BestIndividual](common::BestIndividual) state. + pub fn best_individual(self: $self_type) -> Option<&$lifetime Individual

> { + self.get::>().as_ref() } - /// Returns [Population](common::Population) state. - pub fn population_stack(self: $t) -> &$l common::Population

{ - self.get::>() + /// Returns [BestIndividual](common::BestIndividual) state. + pub fn best_individual_mut(&mut self) -> Option<&$lifetime mut Individual

> { + self.get_mut::>().as_mut() } - /// Returns mutable [Population](common::Population) state. - pub fn population_stack_mut(&mut self) -> &$l mut common::Population

{ - self.get_mut::>() + /// Returns the objective value of the [BestIndividual](common::BestIndividual). + pub fn best_objective_value(self: $self_type) -> Option<&$lifetime SingleObjective> { + self.best_individual().map(|i| i.objective()) } + }; +} - /// Returns mutable [Random](random::Random) state. - pub fn random_mut(&mut self) -> &$l mut Random { - self.get_mut::() +impl<'a, P: SingleObjectiveProblem> State<'a, P> { + // Uses '_ as 'self lifetime. + // This has to match the lifetime bounds of [State::get]. + impl_single_objective_convenience_methods!('_, &Self); +} + +impl<'a, 's, P: SingleObjectiveProblem> MutState<'a, 's, P> { + // Uses 'a as the internal [State]s lifetime. + // This has to match the lifetime bounds of [MutState::get]. + impl_single_objective_convenience_methods!('a, &mut Self); +} + +macro_rules! impl_multi_objective_convenience_methods { + ($lifetime:lifetime, $self_type:ty) => { + /// Returns [ParetoFront](common::ParetoFront) state. + pub fn pareto_front(self: $self_type) -> &$lifetime common::ParetoFront

{ + self.get::>() } - /// Returns the [Log]. - pub fn log(self: $t) -> &$l Log { - self.get::() + /// Returns [ParetoFront](common::ParetoFront) state. + pub fn pareto_front_mut(&mut self) -> &$lifetime mut common::ParetoFront

{ + self.get_mut::>() } }; } -impl<'a> State<'a> { +impl<'a, P: MultiObjectiveProblem> State<'a, P> { // Uses '_ as 'self lifetime. // This has to match the lifetime bounds of [State::get]. - impl_convenience_functions!('_, &Self); + impl_multi_objective_convenience_methods!('_, &Self); } -impl<'a, 's> MutState<'a, 's> { +impl<'a, 's, P: MultiObjectiveProblem> MutState<'a, 's, P> { // Uses 'a as the internal [State]s lifetime. // This has to match the lifetime bounds of [MutState::get]. - impl_convenience_functions!('a, &mut Self); + impl_multi_objective_convenience_methods!('a, &mut Self); } #[derive(Tid)] diff --git a/src/state/cro.rs b/src/state/cro.rs index 815efdf6..25e9d834 100644 --- a/src/state/cro.rs +++ b/src/state/cro.rs @@ -57,7 +57,7 @@ impl

Component

for CroStateInitialization where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { // Initialize with empty state to satisfy `state.require()` statements state.insert(CroState::

{ buffer: 0., @@ -65,8 +65,8 @@ where }) } - fn execute(&self, _problem: &P, state: &mut State) { - let population = state.population_stack::

().current(); + fn execute(&self, _problem: &P, state: &mut State

) { + let population = state.populations().current(); let molecules = population .iter() .map(|i| Molecule::new(self.initial_kinetic_energy, i)) @@ -88,21 +88,25 @@ impl

Component

for OnWallIneffectiveCollisionUpdate where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack = mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); let mut cro_state = mut_state.get_mut::>(); let rng = mut_state.random_mut(); // Get reaction reactant and product - let product = stack.pop().into_iter().next().unwrap(); - let reactant = stack.pop().into_iter().next().unwrap(); + let product = populations.pop().into_iter().next().unwrap(); + let reactant = populations.pop().into_iter().next().unwrap(); - let reactant_index = stack.current().iter().position(|i| i == &reactant).unwrap(); + let reactant_index = populations + .current() + .iter() + .position(|i| i == &reactant) + .unwrap(); cro_state.molecules[reactant_index].num_hit += 1; @@ -123,7 +127,7 @@ where // Replace individual cro_state.molecules[reactant_index].kinetic_energy = (total_reactant_energy - product_energy) * alpha; - stack.current_mut()[reactant_index] = product; + populations.current_mut()[reactant_index] = product; } } } @@ -135,21 +139,25 @@ impl

Component

for DecompositionUpdate where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack = mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); let mut cro_state = mut_state.get_mut::>(); let rng = mut_state.random_mut(); // Get reaction reactant and products p1, p2 - let [p1, p2] = TryInto::<[_; 2]>::try_into(stack.pop()).ok().unwrap(); - let reactant = stack.pop().into_iter().next().unwrap(); + let [p1, p2] = TryInto::<[_; 2]>::try_into(populations.pop()).ok().unwrap(); + let reactant = populations.pop().into_iter().next().unwrap(); - let reactant_index = stack.current().iter().position(|i| i == &reactant).unwrap(); + let reactant_index = populations + .current() + .iter() + .position(|i| i == &reactant) + .unwrap(); let total_reactant_energy = reactant.objective().value() + cro_state.molecules[reactant_index].kinetic_energy; @@ -183,7 +191,7 @@ where .molecules .push(Molecule::new(decomposition_energy * (1. - d3), &p2)); - let population = stack.current_mut(); + let population = populations.current_mut(); population[reactant_index] = p1; population.push(p2); } @@ -196,22 +204,22 @@ impl

Component

for IntermolecularIneffectiveCollisionUpdate where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack: &mut crate::state::common::Population

= mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); let cro_state = mut_state.get_mut::>(); let rng = mut_state.random_mut(); // Get reaction reactants r1, r2 and products p1, p2 - let [p1, p2] = TryInto::<[_; 2]>::try_into(stack.pop()).ok().unwrap(); - let [r1, r2] = TryInto::<[_; 2]>::try_into(stack.pop()).ok().unwrap(); + let [p1, p2] = TryInto::<[_; 2]>::try_into(populations.pop()).ok().unwrap(); + let [r1, r2] = TryInto::<[_; 2]>::try_into(populations.pop()).ok().unwrap(); - let r1_index = stack.current().iter().position(|i| i == &r1).unwrap(); - let r2_index = stack.current().iter().position(|i| i == &r2).unwrap(); + let r1_index = populations.current().iter().position(|i| i == &r1).unwrap(); + let r2_index = populations.current().iter().position(|i| i == &r2).unwrap(); if r1_index == r2_index { panic!("Molecule can't collide with itself."); @@ -243,7 +251,7 @@ where cro_state.molecules[r2_index].update_best(&p2); // Replace individual - let population = stack.current_mut(); + let population = populations.current_mut(); population[r1_index] = p1; population[r2_index] = p2; } @@ -257,21 +265,21 @@ impl

Component

for SynthesisUpdate where P: SingleObjectiveProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { + fn execute(&self, _problem: &P, state: &mut State

) { let mut mut_state = state.get_states_mut(); - let stack: &mut crate::state::common::Population

= mut_state.population_stack_mut::

(); + let populations = mut_state.populations_mut(); let cro_state = mut_state.get_mut::>(); // Get reaction reactants r1, r2 and product - let product = stack.pop().into_iter().next().unwrap(); - let [r1, r2] = TryInto::<[_; 2]>::try_into(stack.pop()).ok().unwrap(); + let product = populations.pop().into_iter().next().unwrap(); + let [r1, r2] = TryInto::<[_; 2]>::try_into(populations.pop()).ok().unwrap(); - let r1_index = stack.current().iter().position(|i| i == &r1).unwrap(); - let r2_index = stack.current().iter().position(|i| i == &r2).unwrap(); + let r1_index = populations.current().iter().position(|i| i == &r1).unwrap(); + let r2_index = populations.current().iter().position(|i| i == &r2).unwrap(); if r1_index == r2_index { panic!("Molecule can't collide with itself."); @@ -291,7 +299,7 @@ where cro_state.molecules.remove(r2_index); // Replace one individual and remove other - let population = stack.current_mut(); + let population = populations.current_mut(); population[r1_index] = product; population.remove(r2_index); } diff --git a/src/state/diversity.rs b/src/state/diversity.rs index 21eb0453..5296553d 100644 --- a/src/state/diversity.rs +++ b/src/state/diversity.rs @@ -8,7 +8,7 @@ use serde::Serialize; use crate::{ framework::components::*, problems::{Problem, VectorProblem}, - state::{common::Population, CustomState, State}, + state::{common::Populations, CustomState, State}, }; /// Specialized component trait to measure population diversity. @@ -28,15 +28,15 @@ where P: Problem, I: AnyComponent + DiversityMeasure

+ Serialize + Clone, { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { state.insert(DiversityState::::default()); } - fn execute(&self, problem: &P, state: &mut State) { - let (population_stack, diversity_state) = - state.get_multiple_mut::<(Population

, DiversityState)>(); + fn execute(&self, problem: &P, state: &mut State

) { + let (populations, diversity_state) = + state.get_multiple_mut::<(Populations

, DiversityState)>(); - let population = population_stack.current(); + let population = populations.current(); if population.is_empty() { diversity_state.diversity = 0.0; @@ -73,7 +73,6 @@ impl> + VectorProblem> DiversityMeasure< let d = problem.dimension(); (0..d) - .into_iter() .map(|k| { let xk = solutions.iter().map(|s| s[k]).sum::() / n; solutions.iter().map(|s| (s[k] - xk).abs()).sum::() / n @@ -132,7 +131,6 @@ impl> + VectorProblem> DiversityMeasure< let d = problem.dimension(); (0..d) - .into_iter() .map(|k| { let xk = solutions.iter().map(|s| s[k]).sum::() / n; let sum = solutions.iter().map(|i| i[k].powi(2)).sum::() / n; diff --git a/src/state/pso.rs b/src/state/pso.rs index f33161b2..8f69c6f7 100644 --- a/src/state/pso.rs +++ b/src/state/pso.rs @@ -35,7 +35,7 @@ where where P: SingleObjectiveProblem> + LimitedVectorProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { + fn initialize(&self, _problem: &P, state: &mut State

) { // Initialize with empty state to satisfy `state.require()` statements state.insert(PsoState { velocities: vec![], @@ -44,19 +44,17 @@ where }) } - fn execute(&self, problem: &P, state: &mut State) { - let population = state.population_stack_mut::

().pop(); + fn execute(&self, problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); let rng = state.random_mut(); - let velocities = population - .iter() - .map(|_| { - (0..problem.dimension()) - .into_iter() - .map(|_| rng.gen_range(-self.v_max..=self.v_max)) - .collect::>() - }) - .collect::>>(); + let velocities = std::iter::repeat_with(|| { + std::iter::repeat_with(|| rng.gen_range(-self.v_max..=self.v_max)) + .take(problem.dimension()) + .collect::>() + }) + .take(population.len()) + .collect::>(); let bests = population.to_vec(); @@ -66,7 +64,7 @@ where .cloned() .unwrap(); - state.population_stack_mut().push(population); + state.populations_mut().push(population); state.insert(PsoState { velocities, @@ -90,25 +88,28 @@ where where P: Problem> + LimitedVectorProblem, { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::>(); } - fn execute(&self, _problem: &P, state: &mut State) { - let population = state.population_stack_mut().pop(); - let mut pso_state = state.get_mut::>(); + fn execute(&self, _problem: &P, state: &mut State

) { + let population = state.populations_mut().pop(); + + let PsoState { + bests, global_best, .. + } = &mut state.get_mut::>(); - for (i, individual) in population.iter().enumerate() { - if pso_state.bests[i].objective() > individual.objective() { - pso_state.bests[i] = individual.clone(); + for (individual, best) in population.iter().zip(bests.iter_mut()) { + if best.objective() > individual.objective() { + *best = individual.clone(); - if pso_state.global_best.objective() > individual.objective() { - pso_state.global_best = individual.clone(); + if global_best.objective() > individual.objective() { + *global_best = individual.clone(); } } } - state.population_stack_mut().push(population); + state.populations_mut().push(population); } } diff --git a/src/testing.rs b/src/testing.rs index a935897f..11028d3e 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -3,7 +3,7 @@ use crate::{ framework::{Individual, SingleObjective}, problems::{Evaluator, HasKnownOptimum, Problem, SingleObjectiveProblem}, - state::common::EvaluatorInstance, + state::{common::EvaluatorInstance, State}, }; use std::borrow::Borrow; @@ -37,7 +37,7 @@ impl Evaluator for TestEvaluator { fn evaluate( &mut self, _problem: &Self::Problem, - _state: &mut crate::state::State, + _state: &mut State, individuals: &mut [Individual], ) { for individual in individuals { diff --git a/src/tracking/functions.rs b/src/tracking/functions.rs index 52223e1a..ffcfb4bb 100644 --- a/src/tracking/functions.rs +++ b/src/tracking/functions.rs @@ -1,5 +1,5 @@ use crate::{ - problems::SingleObjectiveProblem, + problems::{Problem, SingleObjectiveProblem}, state::{common, CustomState, State}, tracking::log::Entry, }; @@ -7,12 +7,13 @@ use serde::Serialize; use std::{any::type_name, ops::Deref}; /// A function to turn some state into an [Entry]. -pub type Extractor<'a> = fn(&State<'a>) -> Entry; +pub type Extractor<'a, P> = fn(&State<'a, P>) -> Entry; /// A function to log anything that implements [Clone] + [Serialize] -pub fn auto<'a, T>(state: &State<'a>) -> Entry +pub fn auto<'a, T, P>(state: &State<'a, P>) -> Entry where T: CustomState<'a> + Clone + Serialize + 'static, + P: Problem, { debug_assert!(state.has::(), "missing state: {}", type_name::()); @@ -24,8 +25,8 @@ where /// A function which logs the best individual. /// -/// Requires the [Problem::Encoding](crate::problems::Problem::Encoding) to implement [Clone] and [Serialize]. -pub fn best_individual

(state: &State) -> Entry +/// Requires the [Problem::Encoding](Problem::Encoding) to implement [Clone] and [Serialize]. +pub fn best_individual

(state: &State

) -> Entry where P: SingleObjectiveProblem, P::Encoding: Clone + Serialize + Sized + 'static, @@ -48,12 +49,12 @@ where Entry { name, value } } -pub fn best_objective_value

(state: &State) -> Entry +pub fn best_objective_value

(state: &State

) -> Entry where P: SingleObjectiveProblem, { Entry { name: "BestObjectiveValue", - value: Box::new(state.best_objective_value::

().cloned()), + value: Box::new(state.best_objective_value().cloned()), } } diff --git a/src/tracking/log.rs b/src/tracking/log.rs index cdefef17..c4de3cdb 100644 --- a/src/tracking/log.rs +++ b/src/tracking/log.rs @@ -1,4 +1,7 @@ -use crate::state::{common, CustomState, State}; +use crate::{ + problems::Problem, + state::{common, CustomState, State}, +}; use better_any::Tid; use erased_serde::Serialize as DynSerialize; use serde::Serialize; @@ -58,7 +61,7 @@ impl Step { /// Pushes the current iteration if it has not been logged yet. /// /// Will also ensure that the iteration is at index 0. - pub(crate) fn push_iteration(&mut self, state: &State) { + pub(crate) fn push_iteration(&mut self, state: &State

) { let name = type_name::(); if !self.contains(name) { diff --git a/src/tracking/logger.rs b/src/tracking/logger.rs index fc0e4256..4bb94922 100644 --- a/src/tracking/logger.rs +++ b/src/tracking/logger.rs @@ -28,7 +28,7 @@ impl Logger { } impl Component

for Logger { - fn initialize(&self, problem: &P, state: &mut State) { + fn initialize(&self, problem: &P, state: &mut State

) { if state.has::>() { state.holding::>(|sets, state| { for (trigger, _) in &sets.entries { @@ -38,7 +38,7 @@ impl Component

for Logger { } } - fn execute(&self, problem: &P, state: &mut State) { + fn execute(&self, problem: &P, state: &mut State

) { if state.has::>() { state.holding::>(|sets, state| { let mut step = Step::default(); diff --git a/src/tracking/set.rs b/src/tracking/set.rs index b4d04014..49327e8d 100644 --- a/src/tracking/set.rs +++ b/src/tracking/set.rs @@ -13,7 +13,7 @@ use serde::Serialize; /// A combination of [Trigger] and [LogFn]. #[derive(Default, Tid)] pub struct LogSet<'a, P: 'static> { - pub(crate) entries: Vec<(Box + 'a>, Extractor<'a>)>, + pub(crate) entries: Vec<(Box + 'a>, Extractor<'a, P>)>, } impl<'a, P> Clone for LogSet<'a, P> { @@ -38,7 +38,7 @@ impl<'a, P: Problem + 'static> LogSet<'a, P> { } } - pub fn with(mut self, trigger: Box + 'a>, extractor: Extractor<'a>) -> Self { + pub fn with(mut self, trigger: Box + 'a>, extractor: Extractor<'a, P>) -> Self { self.entries.push((trigger, extractor)); self } @@ -47,7 +47,7 @@ impl<'a, P: Problem + 'static> LogSet<'a, P> { where T: CustomState<'a> + Clone + Serialize + 'static, { - self.entries.push((trigger, functions::auto::)); + self.entries.push((trigger, functions::auto::)); self } @@ -55,11 +55,11 @@ impl<'a, P: Problem + 'static> LogSet<'a, P> { /// /// Every 10 [Iteration][common::Iterations], [common::Evaluations] and [common::Progress] are logged. pub fn with_common_extractors(self, trigger: Box + 'a>) -> Self { - self.with(trigger.clone(), functions::auto::) - .with(trigger.clone(), functions::auto::) + self.with(trigger.clone(), functions::auto::) + .with(trigger.clone(), functions::auto::) } - pub(crate) fn execute(&self, problem: &P, state: &mut State<'a>, step: &mut Step) { + pub(crate) fn execute(&self, problem: &P, state: &mut State<'a, P>, step: &mut Step) { for (trigger, extractor) in &self.entries { if trigger.evaluate(problem, state) { step.push((extractor)(state)); diff --git a/src/tracking/trigger.rs b/src/tracking/trigger.rs index 9eb2d83b..47f6421d 100644 --- a/src/tracking/trigger.rs +++ b/src/tracking/trigger.rs @@ -8,14 +8,14 @@ use crate::{ state::{common::Iterations, CustomState, State}, }; use better_any::{Tid, TidAble}; -use derive_deref::Deref; +use derive_more::Deref; use dyn_clone::DynClone; /// Like [Condition](crate::framework::conditions::Condition) but non-serializable. pub trait Trigger<'a, P>: DynClone + Send { #[allow(unused_variables)] - fn initialize(&self, problem: &P, state: &mut State<'a>) {} - fn evaluate(&self, problem: &P, state: &mut State<'a>) -> bool; + fn initialize(&self, problem: &P, state: &mut State<'a, P>) {} + fn evaluate(&self, problem: &P, state: &mut State<'a, P>) -> bool; } dyn_clone::clone_trait_object!(<'a, P> Trigger<'a, P>); @@ -24,23 +24,24 @@ dyn_clone::clone_trait_object!(<'a, P> Trigger<'a, P>); pub struct Iteration(u32); impl Iteration { - pub fn new<'a, P: 'static>(iterations: u32) -> Box> { + pub fn new<'a, P: Problem + 'static>(iterations: u32) -> Box> { Box::new(Iteration(iterations)) } } -impl<'a, P: 'static> Trigger<'a, P> for Iteration { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::(); +impl<'a, P: Problem + 'static> Trigger<'a, P> for Iteration { + fn initialize(&self, _problem: &P, state: &mut State

) { + state.require::(); } - fn evaluate(&self, _problem: &P, state: &mut State) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State

) -> bool { state.iterations() % self.0 == 0 } } #[derive(Deref, Tid)] struct Previous<'a, S: TidAble<'a> + Send> { + #[deref] pub inner: S, _phantom: PhantomData<&'a ()>, } @@ -112,17 +113,18 @@ where } } -impl<'s, S, P: 's> Trigger<'s, P> for Change<'s, S> +impl<'s, S, P> Trigger<'s, P> for Change<'s, S> where S: CustomState<'s> + TidAble<'s> + Clone, + P: Problem + 's, { - fn initialize(&self, _problem: &P, state: &mut State<'s>) { - state.require::(); + fn initialize(&self, _problem: &P, state: &mut State<'s, P>) { + state.require::(); let current = state.get::().clone(); state.insert(Previous::new(current)); } - fn evaluate(&self, _problem: &P, state: &mut State<'s>) -> bool { + fn evaluate(&self, _problem: &P, state: &mut State<'s, P>) -> bool { let previous = state.get::>(); let current = state.get::(); let changed = self.check.compare(previous, current); From 65a6c92644cb9f6d929f8c99fa958297809bfc69 Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sat, 1 Apr 2023 18:19:37 +0200 Subject: [PATCH 2/6] Update documentation and examples --- docs/framework.md | 4 ++-- docs/state.md | 4 ++-- examples/bmf.rs | 35 +++++++++++++---------------- examples/coco.rs | 2 +- examples/tsp.rs | 57 ++++++++++++++++++++++++++++++----------------- 5 files changed, 58 insertions(+), 44 deletions(-) diff --git a/docs/framework.md b/docs/framework.md index 9c1990dc..976a8734 100644 --- a/docs/framework.md +++ b/docs/framework.md @@ -61,9 +61,9 @@ Configuration::builder() Every component has to implement the [Component](components::Component) trait, which looks like this: ```ignore pub trait Component: AnyComponent { - fn execute(&self, problem: &P, state: &mut State); + fn execute(&self, problem: &P, state: &mut State

); - fn initialize(&self, problem: &P, state: &mut State) { ... } + fn initialize(&self, problem: &P, state: &mut State

) { ... } } ``` diff --git a/docs/state.md b/docs/state.md index 3951b4a2..b4483ed2 100644 --- a/docs/state.md +++ b/docs/state.md @@ -12,7 +12,7 @@ You would start by defining the state like this: ```rust use mahf::framework::state::CustomState; -use mahf::derive_deref::{Deref, DerefMut}; +use mahf::derive_more::{Deref, DerefMut}; use mahf::serde::Serialize; #[derive(Default, Debug, Deref, DerefMut, Serialize)] @@ -32,7 +32,7 @@ Now you can use it in your component: The [CustomState] trait serves as a marker for custom state. You can take a look at its documentation to get a list off all state types provided by MAHF. -If you have custom state representing a single value, it is recommended to also derive [Deref](derive_deref::Deref), [DerefMut](derive_deref::DerefMut) and [serde::Serialize]. +If you have custom state representing a single value, it is recommended to also derive [Deref](derive_more::Deref), [DerefMut](derive_more::DerefMut) and [serde::Serialize]. ## Mutable Access diff --git a/examples/bmf.rs b/examples/bmf.rs index 3ba5a656..69cca5bc 100644 --- a/examples/bmf.rs +++ b/examples/bmf.rs @@ -1,32 +1,29 @@ -use mahf::framework::Random; use mahf::prelude::*; +use problems::bmf::BenchmarkFunction; -type P = problems::bmf::BenchmarkFunction; - -fn main() -> anyhow::Result<()> { - let problem = P::sphere(10); - let config = pso::real_pso( +fn main() { + // Specify the problem: Sphere function with 10 dimensions. + let problem: BenchmarkFunction = BenchmarkFunction::sphere(/*dim: */ 10); + // Specify the metaheuristic: Particle Swarm Optimization (pre-implemented in MAHF). + let config: Configuration = pso::real_pso( + /*params: */ pso::RealProblemParameters { - num_particles: 100, + num_particles: 20, weight: 1.0, c_one: 1.0, c_two: 1.0, v_max: 1.0, }, - termination::FixedIterations::new(500), + /*termination: */ + termination::FixedIterations::new(/*max_iterations: */ 500) + & termination::DistanceToOpt::new(0.01), ); - let state = config.optimize_with(&problem, |state| state.insert(Random::seeded(0))); + // Execute the metaheuristic on the problem with a random seed. + let state: State = config.optimize(&problem); - println!( - "Found Fitness: {:?}", - state.best_objective_value::

().unwrap() - ); - println!( - "Found Individual: {:?}", - state.best_individual::

().unwrap(), - ); + // Print the results. + println!("Found Individual: {:?}", state.best_individual().unwrap()); + println!("This took {} iterations.", state.iterations()); println!("Global Optimum: {}", problem.known_optimum()); - - Ok(()) } diff --git a/examples/coco.rs b/examples/coco.rs index e1d70849..fc04be6b 100644 --- a/examples/coco.rs +++ b/examples/coco.rs @@ -24,7 +24,7 @@ fn main() -> anyhow::Result<()> { .with_common_extractors(trigger::Iteration::new(10)) .with( trigger::Change::::new(0.1), - functions::auto::, + functions::auto::, ) .with( trigger::Iteration::new(50), diff --git a/examples/tsp.rs b/examples/tsp.rs index afce9d15..28291b00 100644 --- a/examples/tsp.rs +++ b/examples/tsp.rs @@ -1,26 +1,43 @@ +use aco::ant_ops; use mahf::prelude::*; +use problems::tsp::{self, SymmetricTsp}; +use tracking::{files, functions, trigger}; -type P = problems::tsp::SymmetricTsp; - -fn main() -> anyhow::Result<()> { - let problem = problems::tsp::Instances::BERLIN52.load(); - - let config = ils::permutation_iterated_local_search( - ils::PermutationProblemParameters { - local_search_params: ls::PermutationProblemParameters { - n_neighbors: 100, - n_swap: 10, +fn main() { + // Specify the problem: TSPLIB instance Berlin52. + let problem: SymmetricTsp = tsp::Instances::BERLIN52.load(); + // Specify the metaheuristic: Ant System. + let config: Configuration = Configuration::builder() + .do_(initialization::Empty::new()) + .while_( + termination::FixedEvaluations::new(/*max_evaluations: */ 10_000), + |builder| { + builder + .do_(ant_ops::AcoGeneration::new( + /*num_ants: */ 20, /*alpha: */ 2.0, /*beta: */ 1.0, + /*initial_pheromones: */ 0.0, + )) + .evaluate() + .update_best_individual() + .do_(ant_ops::AsPheromoneUpdate::new( + /*evaporation: */ 0.2, /*decay_coefficient: */ 1.0, + )) + .do_(tracking::Logger::new()) }, - local_search_termination: termination::FixedIterations::new(100), - }, - termination::FixedIterations::new(10), - ) - .into_builder() - .assert(|state| state.population_stack::

().current().len() == 1) - .single_objective_summary() - .build(); + ) + .build(); - config.optimize(&problem); + // Execute the metaheuristic on the problem. + let state: State = config.optimize_with(&problem, |state| { + // Set the seed to 42. + state.insert(Random::seeded(42)); + // Log the best individual every 50 iterations. + state.insert( + tracking::LogSet::::new() + .with(trigger::Iteration::new(50), functions::best_individual), + ); + }); - Ok(()) + // Save the log to file "aco_berlin52.log". + files::write_log_file("aco_berlin52.log", state.log()).unwrap(); } From 1e13b980ec1f2f0f70050e35478a6c0517194668 Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sat, 1 Apr 2023 18:27:45 +0200 Subject: [PATCH 3/6] Revert loop change because of other conflict --- src/framework/components.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components.rs b/src/framework/components.rs index 5511f0a7..142a656f 100644 --- a/src/framework/components.rs +++ b/src/framework/components.rs @@ -127,8 +127,8 @@ impl Component

for Loop

{ fn initialize(&self, problem: &P, state: &mut State

) { state.insert(common::Iterations(0)); - self.body.initialize(problem, state); self.condition.initialize(problem, state); + self.body.initialize(problem, state); } fn execute(&self, problem: &P, state: &mut State

) { From 520673d0bc3704fdafebc6ade4a9f11e1370b2af Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sun, 2 Apr 2023 16:20:27 +0200 Subject: [PATCH 4/6] Add more documentation for some State methods --- src/state/container/mod.rs | 40 +++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/state/container/mod.rs b/src/state/container/mod.rs index 60e5369b..ce764869 100644 --- a/src/state/container/mod.rs +++ b/src/state/container/mod.rs @@ -26,7 +26,7 @@ pub trait CustomState<'a>: Tid<'a> + Send {} pub struct State<'a, P> { parent: Option>>, map: StateMap<'a>, - _phantom: std::marker::PhantomData

, + _phantom: PhantomData

, } impl Default for State<'_, P> { @@ -43,7 +43,7 @@ impl<'a, P: Problem> State<'a, P> { State { parent: None, map: StateMap::new(), - _phantom: std::marker::PhantomData, + _phantom: PhantomData, } } @@ -52,7 +52,7 @@ impl<'a, P: Problem> State<'a, P> { let mut substate: State

= Self { parent: Some(Box::new(std::mem::take(self))), map: StateMap::new(), - _phantom: std::marker::PhantomData, + _phantom: PhantomData, }; fun(&mut substate); *self = *substate.parent.unwrap(); @@ -73,11 +73,14 @@ impl<'a, P: Problem> State<'a, P> { self.map.insert(state); } + /// Changes the problem type of the state. + /// + /// Only needed for obscure use-cases when the state is reused for multiple problems. pub fn transmute(self: State<'a, P>) -> State<'a, O> { State { parent: self.parent.map(|parent| Box::new(parent.transmute::())), map: self.map, - _phantom: std::marker::PhantomData::, + _phantom: PhantomData::, } } @@ -104,10 +107,37 @@ impl<'a, P: Problem> State<'a, P> { self.find::().is_some() } - /// Panics if the state does not exist. + /// Panics if the state `T` does not exist, but is needed by the component `C`. /// /// This is the recommended way to ensure the state /// is available in [Component::initialize](crate::framework::components::Component::initialize). + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// use mahf::prelude::*; + /// use mahf::framework::components::Component; + /// use mahf::state::CustomState; + /// + /// #[derive(better_any::Tid)] + /// struct RequiredCustomState; + /// impl CustomState<'_> for RequiredCustomState {} + /// + /// #[derive(Clone, serde::Serialize)] + /// struct ExampleComponent; + /// + /// impl Component

for ExampleComponent { + /// fn initialize(&self, problem: &P, state: &mut State

) { + /// // Panics with an error message if `RequiredCustomState` is not present. + /// state.require::(); + /// } + /// + /// fn execute(&self, problem: &P, state: &mut State

) { + /// unimplemented!() + /// } + /// } + /// ``` #[track_caller] pub fn require>(&self) { assert!( From cbaec46eba1fe1853b6f6ace14d426d68351be8b Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sun, 2 Apr 2023 16:28:53 +0200 Subject: [PATCH 5/6] Run rustfmt --- src/tracking/set.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tracking/set.rs b/src/tracking/set.rs index 49327e8d..2bb6842b 100644 --- a/src/tracking/set.rs +++ b/src/tracking/set.rs @@ -38,7 +38,11 @@ impl<'a, P: Problem + 'static> LogSet<'a, P> { } } - pub fn with(mut self, trigger: Box + 'a>, extractor: Extractor<'a, P>) -> Self { + pub fn with( + mut self, + trigger: Box + 'a>, + extractor: Extractor<'a, P>, + ) -> Self { self.entries.push((trigger, extractor)); self } From 516bc05e6d4221f64cb140f513c931a84389552d Mon Sep 17 00:00:00 2001 From: Jonathan Wurth Date: Sun, 2 Apr 2023 19:36:44 +0200 Subject: [PATCH 6/6] Remove State::transmute --- src/state/container/mod.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/state/container/mod.rs b/src/state/container/mod.rs index ce764869..bced2cd2 100644 --- a/src/state/container/mod.rs +++ b/src/state/container/mod.rs @@ -73,17 +73,6 @@ impl<'a, P: Problem> State<'a, P> { self.map.insert(state); } - /// Changes the problem type of the state. - /// - /// Only needed for obscure use-cases when the state is reused for multiple problems. - pub fn transmute(self: State<'a, P>) -> State<'a, O> { - State { - parent: self.parent.map(|parent| Box::new(parent.transmute::())), - map: self.map, - _phantom: PhantomData::, - } - } - /// Tries to find an inner map containing T. fn find>(&self) -> Option<&Self> { if self.map.has::() {