From 8feeb88605c4ddc25aca870f9529c5bac2e69430 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 3 Aug 2022 13:33:04 +0200 Subject: [PATCH 01/10] Add some convenience methods to Configuration --- src/framework/configuration.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/framework/configuration.rs b/src/framework/configuration.rs index b2797b16..09a2fc5e 100644 --- a/src/framework/configuration.rs +++ b/src/framework/configuration.rs @@ -20,6 +20,10 @@ impl Configuration

{ pub fn heuristic(&self) -> &dyn Component

{ self.0.as_ref() } + + pub fn into_inner(self) -> Box> { + self.0 + } } impl From> for Box> { @@ -44,6 +48,14 @@ impl ConfigurationBuilder

{ self } + pub fn do_if_some_(self, component: Option>>) -> Self { + if let Some(component) = component { + self.do_(component) + } else { + self + } + } + pub fn while_( self, condition: Box>, @@ -88,4 +100,8 @@ impl ConfigurationBuilder

{ pub fn build(self) -> Configuration

{ Configuration::new(Block::new(self.components)) } + + pub fn build_component(self) -> Box> { + Block::new(self.components) + } } From 74497472cc4e2bfd0ff38e6fb1ff883bd6902789 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 3 Aug 2022 13:33:16 +0200 Subject: [PATCH 02/10] Add a random bitstring initializer --- src/operators/initialization.rs | 66 ++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/operators/initialization.rs b/src/operators/initialization.rs index 21a6cb76..1d03e38f 100644 --- a/src/operators/initialization.rs +++ b/src/operators/initialization.rs @@ -1,7 +1,6 @@ //! Initialization methods -use rand::seq::SliceRandom; -use rand::{distributions::uniform::SampleUniform, Rng}; +use rand::{distributions::uniform::SampleUniform, seq::SliceRandom, Rng}; use serde::{Deserialize, Serialize}; use crate::{ @@ -149,3 +148,66 @@ where .collect() } } + +/// Generates new random binary string for binary problems. +#[derive(Serialize, Deserialize)] +pub struct RandomBitstring { + /// Size of the initial population. + pub initial_population_size: Option, + // Probability of generating a 1 / true. + pub p: f64, +} +impl RandomBitstring { + /// Initializes the component with p being the probability for a 1. + pub fn new_init

(initial_population_size: u32, p: f64) -> Box> + where + P: Problem> + VectorProblem, + { + Box::new(Initializer(Self { + initial_population_size: Some(initial_population_size), + p, + })) + } + + /// Initializes the component with uniform probability for 0 and 1. + pub fn new_uniform_init

(initial_population_size: u32) -> Box> + where + P: Problem> + VectorProblem, + { + Box::new(Initializer(Self { + initial_population_size: Some(initial_population_size), + p: 0.5, + })) + } + + pub(crate) fn random_bitstring

( + &self, + problem: &P, + rng: &mut Random, + population_size: u32, + ) -> Vec + where + P: Problem> + VectorProblem, + { + let mut population = Vec::new(); + for _ in 0..population_size { + let solution = (0..problem.dimension()) + .map(|_| rng.gen_bool(self.p)) + .collect::>(); + population.push(solution); + } + population + } +} +impl

Initialization

for RandomBitstring +where + P: Problem> + VectorProblem, +{ + 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() + .map(Individual::new_unevaluated) + .collect() + } +} From 3cd85745bc4e00ee446570760968333cc83d3219 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 3 Aug 2022 13:33:35 +0200 Subject: [PATCH 03/10] Refactor ls, ga and es --- src/heuristics/es.rs | 88 +++++++++++++++++++------- src/heuristics/ga.rs | 144 +++++++++++++++++++++++++++++++++++++------ src/heuristics/ls.rs | 120 ++++++++++++++++++++++++++---------- 3 files changed, 279 insertions(+), 73 deletions(-) diff --git a/src/heuristics/es.rs b/src/heuristics/es.rs index af02dd18..7ff594f0 100644 --- a/src/heuristics/es.rs +++ b/src/heuristics/es.rs @@ -1,38 +1,84 @@ -//! Evolutionary Strategy +//! Evolution Strategy use crate::{ - framework::Configuration, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -/// (μ+λ)-Evolutionary-Strategy -/// -/// # References -/// [doi.org/10.1023/A:1015059928466](https://doi.org/10.1023/A:1015059928466) -pub fn mu_plus_lambda

( +pub struct RealParameters { population_size: u32, lambda: u32, deviation: f64, - max_iterations: u32, +} + +/// An example single-objective (μ+λ)-Evolution-Strategy operating on a real search space. +/// Uses the [es] component internally. +/// +/// # References +/// [doi.org/10.1023/A:1015059928466](https://doi.org/10.1023/A:1015059928466) +pub fn real_mu_plus_lambda

( + params: RealParameters, + termination: Box>, + logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem - + LimitedVectorProblem - + 'static, + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { + let RealParameters { + population_size, + lambda, + deviation, + } = params; + Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::FullyRandom::new(lambda)) - .do_(generation::mutation::FixedDeviationDelta::new(deviation)) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(population_size)) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(es( + Parameters { + selection: selection::FullyRandom::new(lambda), + mutation: generation::mutation::FixedDeviationDelta::new(deviation), + archive: None, + replacement: replacement::MuPlusLambda::new(population_size), }, - ) + termination, + logger, + )) .build() } + +/// Basic building blocks of an Evolution Strategy. +pub struct Parameters

{ + pub selection: Box>, + pub mutation: Box>, + pub archive: Option>>, + pub replacement: Box>, +} + +/// A generic single-objective Evolution Strategy template. +pub fn es( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + selection, + mutation, + archive, + replacement, + } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(selection) + .do_(mutation) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_if_some_(archive) + .do_(replacement) + .do_(logger) + }) + .build_component() +} diff --git a/src/heuristics/ga.rs b/src/heuristics/ga.rs index 28f80d45..eefd91dd 100644 --- a/src/heuristics/ga.rs +++ b/src/heuristics/ga.rs @@ -1,32 +1,140 @@ //! Genetic Algorithm use crate::{ - framework::Configuration, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, - problems::{LimitedVectorProblem, Problem, VectorProblem}, + problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -/// Genetic Algorithm -/// -/// # References -/// [link.springer.com/10.1007/978-3-319-07153-4_28-1](http://link.springer.com/10.1007/978-3-319-07153-4_28-1) -pub fn ga

(population_size: u32, deviation: f64, pc: f64, max_iterations: u32) -> Configuration

+/// Parameters for [binary_ga]. +#[derive(Clone, Copy, Debug)] +pub struct BinaryParameters { + pub population_size: u32, + pub tournament_size: u32, + pub rm: f64, + pub pc: f64, +} + +/// An example single-objective Genetic Algorithm operating on a binary search space. +/// Uses the [ga] component internally. +pub fn binary_ga

( + params: BinaryParameters, + termination: Box>, + logger: Box>, +) -> Configuration

+where + P: SingleObjectiveProblem> + + VectorProblem + + LimitedVectorProblem, +{ + let BinaryParameters { + population_size, + tournament_size, + rm, + pc, + } = params; + + Configuration::builder() + .do_(initialization::RandomBitstring::new_uniform_init( + population_size, + )) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(ga( + Parameters { + selection: selection::Tournament::new(population_size, tournament_size), + crossover: generation::recombination::UniformCrossover::new(pc), + mutation: generation::mutation::BitflipMutation::new(rm), + archive: None, + replacement: replacement::Generational::new(population_size), + }, + termination, + logger, + )) + .build() +} + +/// Parameters for [real_ga]. +#[derive(Clone, Copy, Debug)] +pub struct RealParameters { + pub population_size: u32, + pub tournament_size: u32, + pub deviation: f64, + pub pc: f64, +} + +/// An example single-objective Genetic Algorithm operating on a real search space. +/// Uses the [ga] component internally. +pub fn real_ga

( + params: RealParameters, + termination: Box>, + logger: Box>, +) -> Configuration

where - P: Problem> + VectorProblem + LimitedVectorProblem + 'static, + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { + let RealParameters { + population_size, + tournament_size, + deviation, + pc, + } = params; + Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) .do_(evaluation::SerialEvaluator::new()) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::FullyRandom::new(population_size)) - .do_(generation::recombination::UniformCrossover::new(pc)) - .do_(generation::mutation::FixedDeviationDelta::new(deviation)) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::Generational::new(population_size)) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(ga( + Parameters { + selection: selection::Tournament::new(population_size, tournament_size), + crossover: generation::recombination::UniformCrossover::new(pc), + mutation: generation::mutation::FixedDeviationDelta::new(deviation), + archive: None, + replacement: replacement::Generational::new(population_size), }, - ) + termination, + logger, + )) .build() } + +/// Basic building blocks of a Genetic Algorithm. +pub struct Parameters

{ + pub selection: Box>, + pub crossover: Box>, + pub mutation: Box>, + pub archive: Option>>, + pub replacement: Box>, +} + +/// A generic single-objective Genetic Algorithm template. +/// +/// # References +/// [link.springer.com/10.1007/978-3-319-07153-4_28-1](http://link.springer.com/10.1007/978-3-319-07153-4_28-1) +pub fn ga( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + selection, + crossover, + mutation, + archive, + replacement, + } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(selection) + .do_(crossover) + .do_(mutation) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_if_some_(archive) + .do_(replacement) + .do_(logger) + }) + .build_component() +} diff --git a/src/heuristics/ls.rs b/src/heuristics/ls.rs index 7f41e812..b6868398 100644 --- a/src/heuristics/ls.rs +++ b/src/heuristics/ls.rs @@ -1,58 +1,110 @@ //! Local Search use crate::{ - framework::{components::Component, Configuration}, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -/// Local Search -pub fn local_search

( - max_iterations: u32, - n_neighbors: u32, - neighbors: Box>, +/// Parameters for [real_local_search]. +pub struct RealParameters { + pub n_neighbors: u32, + pub deviation: f64, +} + +/// An example single-objective Local Search operating on a real search space. +/// Uses the [local_search] component internally. +pub fn real_local_search

( + params: RealParameters, + termination: Box>, + logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem - + LimitedVectorProblem - + 'static, + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { + let RealParameters { + n_neighbors, + deviation, + } = params; + Configuration::builder() .do_(initialization::RandomSpread::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::DuplicateSingle::new(n_neighbors)) - .do_(neighbors) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(1)) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(local_search( + Parameters { + n_neighbors, + neighbors: generation::mutation::FixedDeviationDelta::new(deviation), }, - ) + termination, + logger, + )) .build() } -/// Local Permutation Search -pub fn local_permutation_search

( - max_iterations: u32, - n_neighbors: u32, - neighbors: Box>, +/// Parameters for [permutation_local_search]. +pub struct PermutationParameters { + pub n_neighbors: u32, + pub pm: f64, + pub n_swap: usize, +} + +/// An example single-objective Local Search operating on a permutation search space. +/// Uses the [local_search] component internally. +pub fn permutation_local_search

( + params: PermutationParameters, + termination: Box>, + logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> + VectorProblem + 'static, + P: SingleObjectiveProblem> + VectorProblem, { + let PermutationParameters { + n_neighbors, + pm, + n_swap, + } = params; + Configuration::builder() .do_(initialization::RandomPermutation::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::DuplicateSingle::new(n_neighbors)) - .do_(neighbors) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(1)) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(local_search( + Parameters { + n_neighbors, + neighbors: generation::mutation::SwapMutation::new(pm, n_swap), }, - ) + termination, + logger, + )) .build() } + +pub struct Parameters

{ + pub n_neighbors: u32, + pub neighbors: Box>, +} + +/// A generic single-objective Local Search template. +pub fn local_search( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + n_neighbors, + neighbors, + } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(selection::DuplicateSingle::new(n_neighbors)) + .do_(neighbors) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(replacement::MuPlusLambda::new(1)) + .do_(logger) + }) + .build_component() +} From 3f9930690e1f85e884280aa7d0e1157fd268a1f4 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 3 Aug 2022 13:33:46 +0200 Subject: [PATCH 04/10] Add Iterated Local Search --- src/heuristics/ils.rs | 122 ++++++++++++++++++++++++++++++++++++++++++ src/heuristics/mod.rs | 1 + 2 files changed, 123 insertions(+) create mode 100644 src/heuristics/ils.rs diff --git a/src/heuristics/ils.rs b/src/heuristics/ils.rs new file mode 100644 index 00000000..a35a4287 --- /dev/null +++ b/src/heuristics/ils.rs @@ -0,0 +1,122 @@ +//! Iterated Local Search + +use crate::{ + framework::{components::Component, conditions::Condition, Configuration}, + heuristics::ls, + operators::*, + problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, + tracking, +}; + +/// Parameters for [real_iterated_local_search]. +pub struct RealParameters

{ + pub local_search_params: ls::RealParameters, + pub local_search_termination: Box>, +} + +/// An example single-objective Iterated Local Search operating on a real search space. +/// Uses the [iterated_local_search] component internally. +pub fn real_iterated_local_search

( + params: RealParameters

, + termination: Box>, + logger: Box>, +) -> Configuration

+where + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, +{ + let RealParameters { + local_search_params, + local_search_termination, + } = params; + + Configuration::builder() + .do_(initialization::RandomSpread::new_init(1)) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(iterated_local_search( + Parameters { + perturbation: generation::RandomSpread::new_gen(), + local_search: ls::real_local_search( + local_search_params, + local_search_termination, + tracking::Logger::default(), + ) + .into_inner(), + }, + termination, + logger, + )) + .build() +} + +/// Parameters for [iterated_local_permutation_search]. +pub struct PermutationParameters

{ + pub local_search_params: ls::PermutationParameters, + pub local_search_termination: Box>, +} + +/// An example single-objective Iterated Local Search operating on a permutation search space. +/// Uses the [iterated_local_search] component internally. +pub fn permutation_iterated_local_search

( + params: PermutationParameters

, + termination: Box>, + logger: Box>, +) -> Configuration

+where + P: SingleObjectiveProblem> + + VectorProblem +{ + let PermutationParameters { + local_search_params, + local_search_termination, + } = params; + + Configuration::builder() + .do_(initialization::RandomPermutation::new_init(1)) + .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(iterated_local_search( + Parameters { + perturbation: generation::RandomPermutation::new_gen(), + local_search: ls::permutation_local_search( + local_search_params, + local_search_termination, + tracking::Logger::default(), + ) + .into_inner(), + }, + termination, + logger, + )) + .build() +} + +pub struct Parameters

{ + perturbation: Box>, + local_search: Box>, +} + +/// A generic single-objective Iterated Local Search template. +pub fn iterated_local_search( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + perturbation, + local_search, + } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(perturbation) + .do_(selection::All::new()) + .scope_(|builder| builder.do_(local_search)) + .do_(evaluation::UpdateBestIndividual::new()) + .do_(Noop::new()) + .do_(replacement::MuPlusLambda::new(1)) + .do_(logger) + }) + .build_component() +} diff --git a/src/heuristics/mod.rs b/src/heuristics/mod.rs index 44c8c842..b771b910 100644 --- a/src/heuristics/mod.rs +++ b/src/heuristics/mod.rs @@ -3,6 +3,7 @@ pub mod aco; pub mod es; pub mod ga; +pub mod ils; pub mod iwo; pub mod ls; pub mod pso; From 74b2afaac735ef2b03cc31770a691b897f5cb4f0 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 3 Aug 2022 13:33:54 +0200 Subject: [PATCH 05/10] Modify example --- examples/tsp.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/tsp.rs b/examples/tsp.rs index 44a48091..0f04101b 100644 --- a/examples/tsp.rs +++ b/examples/tsp.rs @@ -1,10 +1,18 @@ -use mahf::heuristics::aco; -use mahf::{framework, problems}; +use mahf::{framework, problems, heuristics::*, operators::*, tracking::Logger}; fn main() -> anyhow::Result<()> { let problem = problems::tsp::Instances::BERLIN52.load(); - let tau0 = 1. / problem.best_fitness().unwrap(); - let config = aco::min_max_ant_system(20, 1., 1., tau0, 0.1, 1., 0.1, 500); + // let tau0 = 1. / problem.best_fitness().unwrap(); + // let config = aco::min_max_ant_system(20, 1., 1., tau0, 0.1, 1., 0.1, 500); + + let config = ils::permutation_iterated_local_search(ils::PermutationParameters { + local_search_params: ls::PermutationParameters { + n_neighbors: 20, + pm: 0.7, + n_swap: 5 + }, + local_search_termination: termination::FixedIterations::new(100), + }, termination::FixedIterations::new(10), Logger::default()); let state = framework::run(&problem, &config, None); From cd4b855f69c0504eb39ec802d180c940471a6a06 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 17 Aug 2022 16:35:23 +0200 Subject: [PATCH 06/10] Convert heuristics into components and add misc operators --- src/framework/configuration.rs | 49 ++++++- src/heuristics/aco.rs | 209 ++++++++++++++------------- src/heuristics/es.rs | 15 +- src/heuristics/ga.rs | 12 +- src/heuristics/ils.rs | 20 +-- src/heuristics/iwo.rs | 83 ++++++++--- src/heuristics/ls.rs | 12 +- src/heuristics/pso.rs | 115 +++++++++------ src/heuristics/rs.rs | 87 +++++++---- src/heuristics/rw.rs | 116 +++++++++------ src/operators/custom_state.rs | 41 ++++++ src/operators/evaluation.rs | 4 +- src/operators/generation/mutation.rs | 1 + src/operators/misc.rs | 100 +++++++++++++ src/operators/mod.rs | 28 +--- src/operators/selection.rs | 1 + 16 files changed, 599 insertions(+), 294 deletions(-) create mode 100644 src/operators/misc.rs diff --git a/src/framework/configuration.rs b/src/framework/configuration.rs index 09a2fc5e..a0c9e260 100644 --- a/src/framework/configuration.rs +++ b/src/framework/configuration.rs @@ -2,8 +2,10 @@ use crate::{ framework::{ components::{Block, Branch, Component, Loop, Scope}, conditions::Condition, + state, }, - problems::Problem, + operators, + problems::{MultiObjectiveProblem, Problem, SingleObjectiveProblem}, }; pub struct Configuration(Box>); @@ -24,6 +26,10 @@ impl Configuration

{ pub fn into_inner(self) -> Box> { self.0 } + + pub fn into_builder(self) -> ConfigurationBuilder

{ + ConfigurationBuilder::new().do_(self.0) + } } impl From> for Box> { @@ -36,6 +42,7 @@ pub struct ConfigurationBuilder { components: Vec>>, } +// Basic functionality impl ConfigurationBuilder

{ fn new() -> Self { Self { @@ -105,3 +112,43 @@ impl ConfigurationBuilder

{ Block::new(self.components) } } + +// Convenience methods +impl ConfigurationBuilder

{ + /// Asserts the condition on [State][state::State]. + /// + /// Uses the [Debug][operators::misc::Debug] component internally. + pub fn assert(self, assert: impl Fn(&state::State) -> bool + Send + Sync + 'static) -> Self { + self.debug(move |_problem, state| assert!(assert(state))) + } + + /// Constructs a [Debug][operators::misc::Debug] component with the given behaviour. + pub fn debug(self, behaviour: impl Fn(&P, &mut state::State) + Send + Sync + 'static) -> Self { + self.do_(operators::misc::Debug::new(behaviour)) + } + + pub fn evaluate_serial(self) -> Self { + self.do_(operators::evaluation::SerialEvaluator::new()) + } +} + +impl ConfigurationBuilder

{ + pub fn update_best_individual(self) -> Self { + self.do_(operators::evaluation::UpdateBestIndividual::new()) + } +} + +impl ConfigurationBuilder

+where + P::Encoding: std::fmt::Debug, +{ + pub fn single_objective_summary(self) -> Self { + self.do_(operators::misc::PrintSingleObjectiveSummary::new()) + } +} + +impl ConfigurationBuilder

{ + pub fn update_pareto_front(self) -> Self { + self.do_(operators::evaluation::UpdateParetoFront::new()) + } +} diff --git a/src/heuristics/aco.rs b/src/heuristics/aco.rs index a7d88af3..381440f8 100644 --- a/src/heuristics/aco.rs +++ b/src/heuristics/aco.rs @@ -1,54 +1,60 @@ //! Ant Colony Optimization -use serde::Serialize; - use crate::{ - framework::{state::CustomState, Configuration}, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, - problems::tsp::SymmetricTsp, + problems::{tsp, SingleObjectiveProblem}, }; -/// Ant Colony Optimization - Ant System -/// -/// # References -/// Dorigo, Marco & Birattari, Mauro & Stützle, Thomas. (2006). Ant Colony Optimization. Computational Intelligence Magazine, IEEE. 1. 28-39. 10.1109/MCI.2006.329691. -pub fn ant_system( +/// Parameters for [ant_system]. +pub struct ASParameters { number_of_ants: usize, alpha: f64, beta: f64, default_pheromones: f64, evaporation: f64, decay_coefficient: f64, - max_iterations: u32, -) -> Configuration { +} + +/// Ant Colony Optimization - Ant System +/// Uses the [aco] component internally. +/// +/// # References +/// [doi.org/10.1109/MCI.2006.329691](https://doi.org/10.1109/MCI.2006.329691) +pub fn ant_system( + params: ASParameters, + termination: Box>, + logger: Box>, +) -> Configuration { + let ASParameters { + number_of_ants, + alpha, + beta, + default_pheromones, + evaporation, + decay_coefficient, + } = params; + Configuration::builder() .do_(initialization::Empty::new()) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(ant_ops::AcoGeneration::new( - number_of_ants, - alpha, - beta, - default_pheromones, - )) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) - .do_(ant_ops::AsPheromoneUpdate::new( - evaporation, - decay_coefficient, - )) + .do_(aco( + Parameters { + generation: ant_ops::AcoGeneration::new( + number_of_ants, + alpha, + beta, + default_pheromones, + ), + pheromone_update: ant_ops::AsPheromoneUpdate::new(evaporation, decay_coefficient), }, - ) + termination, + logger, + )) .build() } -/// Ant Colony Optimization - Ant System -/// -/// # References -/// Dorigo, Marco & Birattari, Mauro & Stützle, Thomas. (2006). Ant Colony Optimization. Computational Intelligence Magazine, IEEE. 1. 28-39. 10.1109/MCI.2006.329691. -pub fn min_max_ant_system( +/// Parameters for [max_min_ant_system]. +pub struct MMASParameters { number_of_ants: usize, alpha: f64, beta: f64, @@ -56,74 +62,77 @@ pub fn min_max_ant_system( evaporation: f64, max_pheromones: f64, min_pheromones: f64, - max_iterations: u32, -) -> Configuration { - assert!( - min_pheromones < max_pheromones, - "min_pheromones must be less than max_pheromones" - ); +} + +/// Ant Colony Optimization - MAX-MIN Ant System +/// Uses the [aco] component internally. +/// +/// # References +/// [doi.org/10.1109/MCI.2006.329691](https://doi.org/10.1109/MCI.2006.329691) +pub fn max_min_ant_system( + params: MMASParameters, + termination: Box>, + logger: Box>, +) -> Configuration { + let MMASParameters { + number_of_ants, + alpha, + beta, + default_pheromones, + evaporation, + max_pheromones, + min_pheromones, + } = params; + Configuration::builder() .do_(initialization::Empty::new()) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(ant_ops::AcoGeneration::new( - number_of_ants, - alpha, - beta, - default_pheromones, - )) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) - .do_(ant_ops::MinMaxPheromoneUpdate::new( - evaporation, - max_pheromones, - min_pheromones, - )) + .do_(aco( + Parameters { + generation: ant_ops::AcoGeneration::new( + number_of_ants, + alpha, + beta, + default_pheromones, + ), + pheromone_update: ant_ops::MinMaxPheromoneUpdate::new( + evaporation, + max_pheromones, + min_pheromones, + ), }, - ) + termination, + logger, + )) .build() } -#[derive(Clone, Serialize)] -struct PheromoneMatrix { - dimension: usize, - inner: Vec, -} -impl CustomState for PheromoneMatrix {} -impl PheromoneMatrix { - pub fn new(dimension: usize, initial_value: f64) -> Self { - PheromoneMatrix { - dimension, - inner: vec![initial_value; dimension * dimension], - } - } +/// Basic building blocks of Ant Colony Optimization. +pub struct Parameters

{ + generation: Box>, + pheromone_update: Box>, } -impl std::ops::Index for PheromoneMatrix { - type Output = [f64]; - fn index(&self, index: usize) -> &Self::Output { - assert!(index < self.dimension); - let start = index * self.dimension; - let end = start + self.dimension; - &self.inner[start..end] - } -} -impl std::ops::IndexMut for PheromoneMatrix { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - assert!(index < self.dimension); - let start = index * self.dimension; - let end = start + self.dimension; - &mut self.inner[start..end] - } -} -impl std::ops::MulAssign for PheromoneMatrix { - fn mul_assign(&mut self, rhs: f64) { - for x in &mut self.inner { - *x *= rhs; - } - } +/// A generic single-objective Ant Colony Optimization template. +pub fn aco( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + generation, + pheromone_update, + } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(generation) + .evaluate_serial() + .update_best_individual() + .do_(pheromone_update) + .do_(logger) + }) + .build_component() } mod ant_ops { @@ -131,11 +140,10 @@ mod ant_ops { use crate::{ framework::{components::*, state::State, Individual, Random, SingleObjective}, + operators::custom_state::PheromoneMatrix, problems::tsp::SymmetricTsp, }; - use super::PheromoneMatrix; - #[derive(serde::Serialize)] pub struct AcoGeneration { pub number_of_ants: usize, @@ -271,6 +279,10 @@ mod ant_ops { max_pheromones: f64, min_pheromones: f64, ) -> Box> { + assert!( + min_pheromones < max_pheromones, + "min_pheromones must be less than max_pheromones" + ); Box::new(Self { evaporation, max_pheromones, @@ -284,8 +296,9 @@ mod ant_ops { } fn execute(&self, _problem: &SymmetricTsp, state: &mut State) { - let population = state.population_stack_mut::().pop(); - let pm = state.get_mut::(); + let mut mut_state = state.get_states_mut(); + let pm = mut_state.get_mut::(); + let population = mut_state.population_stack::().current(); // Evaporation *pm *= 1.0 - self.evaporation; @@ -304,8 +317,6 @@ mod ant_ops { pm[a][b] = (pm[a][b] + delta).clamp(self.min_pheromones, self.max_pheromones); pm[b][a] = (pm[b][a] + delta).clamp(self.min_pheromones, self.max_pheromones); } - - state.population_stack_mut().push(population); } } } diff --git a/src/heuristics/es.rs b/src/heuristics/es.rs index 7ff594f0..4c23ceb4 100644 --- a/src/heuristics/es.rs +++ b/src/heuristics/es.rs @@ -6,10 +6,11 @@ use crate::{ problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; +/// Parameters for [real_mu_plus_lambda]. pub struct RealParameters { - population_size: u32, - lambda: u32, - deviation: f64, + pub population_size: u32, + pub lambda: u32, + pub deviation: f64, } /// An example single-objective (μ+λ)-Evolution-Strategy operating on a real search space. @@ -33,8 +34,8 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(es( Parameters { selection: selection::FullyRandom::new(lambda), @@ -74,8 +75,8 @@ pub fn es( builder .do_(selection) .do_(mutation) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_if_some_(archive) .do_(replacement) .do_(logger) diff --git a/src/heuristics/ga.rs b/src/heuristics/ga.rs index eefd91dd..f7b57a08 100644 --- a/src/heuristics/ga.rs +++ b/src/heuristics/ga.rs @@ -38,8 +38,8 @@ where .do_(initialization::RandomBitstring::new_uniform_init( population_size, )) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(ga( Parameters { selection: selection::Tournament::new(population_size, tournament_size), @@ -82,8 +82,8 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(ga( Parameters { selection: selection::Tournament::new(population_size, tournament_size), @@ -130,8 +130,8 @@ pub fn ga( .do_(selection) .do_(crossover) .do_(mutation) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_if_some_(archive) .do_(replacement) .do_(logger) diff --git a/src/heuristics/ils.rs b/src/heuristics/ils.rs index a35a4287..965ffe81 100644 --- a/src/heuristics/ils.rs +++ b/src/heuristics/ils.rs @@ -31,8 +31,8 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(1)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(iterated_local_search( Parameters { perturbation: generation::RandomSpread::new_gen(), @@ -63,8 +63,7 @@ pub fn permutation_iterated_local_search

( logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem + P: SingleObjectiveProblem> + VectorProblem, { let PermutationParameters { local_search_params, @@ -73,8 +72,8 @@ where Configuration::builder() .do_(initialization::RandomPermutation::new_init(1)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(iterated_local_search( Parameters { perturbation: generation::RandomPermutation::new_gen(), @@ -91,9 +90,10 @@ where .build() } +/// Basic building blocks of an Iterated Local Search. pub struct Parameters

{ - perturbation: Box>, - local_search: Box>, + pub perturbation: Box>, + pub local_search: Box>, } /// A generic single-objective Iterated Local Search template. @@ -111,10 +111,10 @@ pub fn iterated_local_search( .while_(termination, |builder| { builder .do_(perturbation) + .evaluate_serial() .do_(selection::All::new()) .scope_(|builder| builder.do_(local_search)) - .do_(evaluation::UpdateBestIndividual::new()) - .do_(Noop::new()) + .update_best_individual() .do_(replacement::MuPlusLambda::new(1)) .do_(logger) }) diff --git a/src/heuristics/iwo.rs b/src/heuristics/iwo.rs index 088c3fec..6691532b 100644 --- a/src/heuristics/iwo.rs +++ b/src/heuristics/iwo.rs @@ -6,8 +6,8 @@ use crate::{ problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -#[derive(Clone, Copy, Debug)] -pub struct Parameters { +#[derive(Clone, Debug)] +pub struct RealParameters { pub initial_population_size: u32, pub max_population_size: u32, pub min_number_of_seeds: u32, @@ -17,7 +17,8 @@ pub struct Parameters { pub modulation_index: u32, } -/// Invasive Weed Optimization +/// An example single-objective Invasive Weed Optimization operating on a real search space. +/// Uses the [iwo] component internally. /// /// # Requirements /// - initial_population_size <= max_population_size @@ -26,40 +27,80 @@ pub struct Parameters { /// /// # References /// [doi.org/10.1016/j.ecoinf.2006.07.003](https://doi.org/10.1016/j.ecoinf.2006.07.003) -pub fn iwo

( - params: Parameters, +pub fn real_iwo

( + params: RealParameters, termination: Box>, logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem - + LimitedVectorProblem - + 'static, + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - assert!(params.initial_population_size <= params.max_population_size); - assert!(params.min_number_of_seeds <= params.max_number_of_seeds); - assert!(params.final_deviation <= params.initial_deviation); + let RealParameters { + initial_population_size, + max_population_size, + min_number_of_seeds, + max_number_of_seeds, + initial_deviation, + final_deviation, + modulation_index, + } = params; + + assert!(initial_population_size <= max_population_size); Configuration::builder() .do_(initialization::RandomSpread::new_init( params.initial_population_size, )) + .do_(iwo( + Parameters { + max_population_size, + min_number_of_seeds, + max_number_of_seeds, + mutation: generation::mutation::IWOAdaptiveDeviationDelta::new( + initial_deviation, + final_deviation, + modulation_index, + ), + }, + termination, + logger, + )) + .build() +} + +/// Basic building blocks of Invasive Weed Optimization. +pub struct Parameters

{ + pub max_population_size: u32, + pub min_number_of_seeds: u32, + pub max_number_of_seeds: u32, + pub mutation: Box>, +} + +/// A generic single-objective Invasive Weed Optimization template. +pub fn iwo( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> { + let Parameters { + max_population_size, + min_number_of_seeds, + max_number_of_seeds, + mutation, + } = params; + + Configuration::builder() .do_(evaluation::SerialEvaluator::new()) .while_(termination, |builder| { builder .do_(selection::DeterministicFitnessProportional::new( - params.min_number_of_seeds, - params.max_number_of_seeds, - )) - .do_(generation::mutation::IWOAdaptiveDeviationDelta::new( - params.initial_deviation, - params.final_deviation, - params.modulation_index, + min_number_of_seeds, + max_number_of_seeds, )) + .do_(mutation) .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(params.max_population_size)) + .do_(replacement::MuPlusLambda::new(max_population_size)) .do_(logger) }) - .build() + .build_component() } diff --git a/src/heuristics/ls.rs b/src/heuristics/ls.rs index b6868398..a42b94c8 100644 --- a/src/heuristics/ls.rs +++ b/src/heuristics/ls.rs @@ -29,7 +29,8 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(1)) - .do_(evaluation::SerialEvaluator::new()) + .evaluate_serial() + .update_best_individual() .do_(evaluation::UpdateBestIndividual::new()) .do_(local_search( Parameters { @@ -67,8 +68,8 @@ where Configuration::builder() .do_(initialization::RandomPermutation::new_init(1)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(local_search( Parameters { n_neighbors, @@ -80,6 +81,7 @@ where .build() } +/// Basic building blocks of a Local Search. pub struct Parameters

{ pub n_neighbors: u32, pub neighbors: Box>, @@ -101,8 +103,8 @@ pub fn local_search( builder .do_(selection::DuplicateSingle::new(n_neighbors)) .do_(neighbors) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) + .evaluate_serial() + .update_best_individual() .do_(replacement::MuPlusLambda::new(1)) .do_(logger) }) diff --git a/src/heuristics/pso.rs b/src/heuristics/pso.rs index bdc6a6d9..9809e973 100644 --- a/src/heuristics/pso.rs +++ b/src/heuristics/pso.rs @@ -1,41 +1,89 @@ //! Particle Swarm Optimization use crate::{ - framework::Configuration, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, problems::{LimitedVectorProblem, SingleObjectiveProblem}, }; -pub fn pso

( - num_particles: u32, - a: f64, - b: f64, - c: f64, - v_max: f64, - max_iterations: u32, +/// Parameters for [real_pso]. +pub struct RealParameters { + pub num_particles: u32, + pub a: f64, + pub b: f64, + pub c: f64, + pub v_max: f64, +} + +/// An example single-objective Particle Swarm Optimization operating on a real search space. +/// Uses the [pso] component internally. +pub fn real_pso

( + params: RealParameters, + termination: Box>, + logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + LimitedVectorProblem + 'static, { + let RealParameters { + num_particles, + a, + b, + c, + v_max, + } = params; + Configuration::builder() .do_(initialization::RandomSpread::new_init(num_particles)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) - .do_(pso_ops::PsoStateInitialization::new(v_max)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(generation::swarm::PsoGeneration::new(a, b, c, v_max)) - .do_(evaluation::SerialEvaluator::new()) - .do_(evaluation::UpdateBestIndividual::new()) - .do_(pso_ops::PsoStateUpdate::new()) + .evaluate_serial() + .update_best_individual() + .do_(pso( + Parameters { + particle_init: pso_ops::PsoStateInitialization::new(v_max), + particle_update: generation::swarm::PsoGeneration::new(a, b, c, v_max), + state_update: pso_ops::PsoStateUpdate::new(), }, - ) - .do_(pso_ops::AddPsoStateGlobalBest::new()) + termination, + logger, + )) .build() } +/// Basic building blocks of Particle Swarm Optimization. +pub struct Parameters

{ + particle_init: Box>, + particle_update: Box>, + state_update: Box>, +} + +/// A generic single-objective Particle Swarm Optimization template. +pub fn pso

( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> +where + P: SingleObjectiveProblem, +{ + let Parameters { + particle_init, + particle_update, + state_update, + } = params; + + Configuration::builder() + .do_(particle_init) + .while_(termination, |builder| { + builder + .do_(particle_update) + .evaluate_serial() + .update_best_individual() + .do_(state_update) + .do_(logger) + }) + .build_component() +} + #[allow(clippy::new_ret_no_self)] mod pso_ops { use crate::problems::SingleObjectiveProblem; @@ -141,29 +189,4 @@ mod pso_ops { state.population_stack_mut().push(population); } } - - /// Adds the global best from [PsoState] to the population. - #[derive(Debug, serde::Serialize)] - pub struct AddPsoStateGlobalBest; - impl AddPsoStateGlobalBest { - pub fn new() -> Box> - where - P: Problem> + LimitedVectorProblem, - { - Box::new(Self) - } - } - impl

Component

for AddPsoStateGlobalBest - where - P: Problem> + LimitedVectorProblem, - { - fn initialize(&self, _problem: &P, state: &mut State) { - state.require::>(); - } - - fn execute(&self, _problem: &P, state: &mut State) { - let global_best = state.get::>().global_best.clone(); - state.population_stack_mut().current_mut().push(global_best); - } - } } diff --git a/src/heuristics/rs.rs b/src/heuristics/rs.rs index 708aeded..78ebe7d3 100644 --- a/src/heuristics/rs.rs +++ b/src/heuristics/rs.rs @@ -1,49 +1,82 @@ //! Random Search use crate::{ - framework::Configuration, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -/// Random Search -pub fn random_search

(max_iterations: u32) -> Configuration

+/// An example single-objective Random Search operating on a real search space. +/// Uses the [random_search] component internally. +pub fn real_random_search

( + termination: Box>, + logger: Box>, +) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem - + LimitedVectorProblem - + 'static, + P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { Configuration::builder() .do_(generation::RandomSpread::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::All::new()) - .do_(generation::RandomSpread::new_gen()) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(1)) + .evaluate_serial() + .update_best_individual() + .do_(random_search( + Parameters { + randomizer: generation::RandomSpread::new_gen(), }, - ) + termination, + logger, + )) .build() } -pub fn random_permutation_search

(max_iterations: u32) -> Configuration

+/// An example single-objective Random Search operating on a permutation search space. +/// Uses the [random_search] component internally. +pub fn permutation_random_search

( + termination: Box>, + logger: Box>, +) -> Configuration

where - P: SingleObjectiveProblem> + VectorProblem + 'static, + P: SingleObjectiveProblem> + VectorProblem, { Configuration::builder() .do_(generation::RandomPermutation::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(selection::All::new()) - .do_(generation::RandomPermutation::new_gen()) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::MuPlusLambda::new(1)) + .evaluate_serial() + .update_best_individual() + .do_(random_search( + Parameters { + randomizer: generation::RandomPermutation::new_gen(), }, - ) + termination, + logger, + )) .build() } + +/// Basic building blocks of an Random Search. +pub struct Parameters

{ + pub randomizer: Box>, +} + +/// A generic single-objective Random Search template. +pub fn random_search

( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> +where + P: SingleObjectiveProblem, +{ + let Parameters { randomizer } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(selection::All::new()) + .do_(randomizer) + .evaluate_serial() + .update_best_individual() + .do_(replacement::MuPlusLambda::new(1)) + .do_(logger) + }) + .build_component() +} diff --git a/src/heuristics/rw.rs b/src/heuristics/rw.rs index ac628c9c..0cff180c 100644 --- a/src/heuristics/rw.rs +++ b/src/heuristics/rw.rs @@ -1,66 +1,94 @@ //! Random Walk -use crate::problems::SingleObjectiveProblem; use crate::{ - framework::{components::Component, Configuration}, + framework::{components::Component, conditions::Condition, Configuration}, operators::*, - problems::{LimitedVectorProblem, VectorProblem}, + problems::{LimitedVectorProblem, SingleObjectiveProblem, VectorProblem}, }; -/// Random Walk -/// -/// # Arguments -/// -/// * mutation: The mutation method used to move in the search space. -pub fn random_walk

(max_iterations: u32, mutation: Box>) -> Configuration

+/// Parameters for [real_random_walk]. +pub struct RealParameters { + pub deviation: f64, +} + +/// An example single-objective Random Walk operating on a real search space. +/// Uses the [random_walk] component internally. +pub fn real_random_walk

( + params: RealParameters, + termination: Box>, + logger: Box>, +) -> Configuration

where - P: SingleObjectiveProblem> - + VectorProblem - + LimitedVectorProblem - + 'static, + P: SingleObjectiveProblem> + LimitedVectorProblem, { + let RealParameters { deviation } = params; + Configuration::builder() .do_(generation::RandomSpread::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(archive::ElitistArchive::new(1)) - .do_(selection::All::new()) - .do_(mutation) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::Generational::new(1)) + .do_(random_walk( + Parameters { + neighbor: generation::mutation::FixedDeviationDelta::new(deviation), }, - ) - .do_(archive::AddElitists::new()) + termination, + logger, + )) .build() } -/// Random Permutation Walk -/// -/// # Arguments -/// -/// * mutation: The mutation method used to move in the search space. -pub fn random_permutation_walk

( - max_iterations: u32, - mutation: Box>, +/// Parameters for [permutation_random_walk]. +pub struct PermutationParameters { + pub pm: f64, + pub n_swap: usize, +} + +/// An example single-objective Random Walk operating on a permutation search space. +/// Uses the [random_walk] component internally. +pub fn permutation_random_walk

( + params: PermutationParameters, + termination: Box>, + logger: Box>, ) -> Configuration

where - P: SingleObjectiveProblem> + VectorProblem + 'static, + P: SingleObjectiveProblem> + VectorProblem, { + let PermutationParameters { pm, n_swap } = params; + Configuration::builder() .do_(generation::RandomPermutation::new_init(1)) - .while_( - termination::FixedIterations::new(max_iterations), - |builder| { - builder - .do_(archive::ElitistArchive::new(1)) - .do_(selection::All::new()) - .do_(mutation) - .do_(evaluation::SerialEvaluator::new()) - .do_(replacement::Generational::new(1)) + .do_(random_walk( + Parameters { + neighbor: generation::mutation::SwapMutation::new(pm, n_swap), }, - ) - .do_(archive::AddElitists::new()) + termination, + logger, + )) .build() } + +pub struct Parameters

{ + pub neighbor: Box>, +} + +/// A generic single-objective Random Search template. +pub fn random_walk

( + params: Parameters

, + termination: Box>, + logger: Box>, +) -> Box> +where + P: SingleObjectiveProblem, +{ + let Parameters { neighbor } = params; + + Configuration::builder() + .while_(termination, |builder| { + builder + .do_(selection::All::new()) + .do_(neighbor) + .evaluate_serial() + .update_best_individual() + .do_(replacement::Generational::new(1)) + .do_(logger) + }) + .build_component() +} diff --git a/src/operators/custom_state.rs b/src/operators/custom_state.rs index 1622a4c5..abdfc46a 100644 --- a/src/operators/custom_state.rs +++ b/src/operators/custom_state.rs @@ -4,6 +4,7 @@ use crate::{ framework::{state::CustomState, Individual, SingleObjective}, problems::{Problem, SingleObjectiveProblem}, }; +use serde::Serialize; // Custom States for Specific Metaheuristics // @@ -17,6 +18,46 @@ pub struct PsoState { } impl CustomState for PsoState

{} +#[derive(Clone, Serialize)] +pub struct PheromoneMatrix { + dimension: usize, + inner: Vec, +} +impl PheromoneMatrix { + pub fn new(dimension: usize, initial_value: f64) -> Self { + PheromoneMatrix { + dimension, + inner: vec![initial_value; dimension * dimension], + } + } +} +impl std::ops::Index for PheromoneMatrix { + type Output = [f64]; + + fn index(&self, index: usize) -> &Self::Output { + assert!(index < self.dimension); + let start = index * self.dimension; + let end = start + self.dimension; + &self.inner[start..end] + } +} +impl std::ops::IndexMut for PheromoneMatrix { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + assert!(index < self.dimension); + let start = index * self.dimension; + let end = start + self.dimension; + &mut self.inner[start..end] + } +} +impl std::ops::MulAssign for PheromoneMatrix { + fn mul_assign(&mut self, rhs: f64) { + for x in &mut self.inner { + *x *= rhs; + } + } +} +impl CustomState for PheromoneMatrix {} + // Custom States for Operators // /// State required for Elitism. diff --git a/src/operators/evaluation.rs b/src/operators/evaluation.rs index 40d5aa7a..83fffa84 100644 --- a/src/operators/evaluation.rs +++ b/src/operators/evaluation.rs @@ -34,7 +34,9 @@ impl Component

for SerialEvaluator { } for individual in population.current_mut().iter_mut() { - problem.evaluate(individual); + if !individual.is_evaluated() { + problem.evaluate(individual); + } } // Update evaluations diff --git a/src/operators/generation/mutation.rs b/src/operators/generation/mutation.rs index e0e3227e..59ae5e74 100644 --- a/src/operators/generation/mutation.rs +++ b/src/operators/generation/mutation.rs @@ -78,6 +78,7 @@ impl IWOAdaptiveDeviationDelta { where P: Problem>, { + assert!(final_deviation <= initial_deviation); Box::new(Generator(Self { initial_deviation, final_deviation, diff --git a/src/operators/misc.rs b/src/operators/misc.rs new file mode 100644 index 00000000..af415b61 --- /dev/null +++ b/src/operators/misc.rs @@ -0,0 +1,100 @@ +use serde::{Serialize, Serializer}; + +use crate::{ + framework::{components::Component, state::State}, + problems::{Problem, SingleObjectiveProblem}, +}; + +/// Doesn't do anything. +/// +/// Note that this component is different from [initialization::Empty] as it doesn't modify +/// the state at all, while [Empty][initialization::Empty] pushes an empty population on the stack. +#[derive(Serialize)] +pub struct Noop; +impl Noop { + pub fn new

() -> Box> + where + P: Problem, + { + Box::new(Self) + } +} +impl Component

for Noop { + fn execute(&self, _problem: &P, _state: &mut State) { + // Noop + } +} + +/// Clears the current population, deleting all individuals. +#[derive(Serialize)] +pub struct ClearPopulation; +impl ClearPopulation { + pub fn new() -> Box> { + Box::new(Self) + } +} +impl Component

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

().current_mut().clear(); + } +} + +pub type DynCustomFunc

= dyn Fn(&P, &mut State) + Send + Sync + 'static; + +/// Allows for minor custom behaviour for debug purposes, e.g., asserts. +/// +/// The contents of the function passed to this component are **NOT** serialized. +/// +/// Note that this is for debug **ONLY**. +/// The recommended way of implementing larger custom functionality is to implement +/// [Component] for your struct. +pub struct Debug(Box>); +impl Debug

{ + pub fn new(custom: impl Fn(&P, &mut State) + Send + Sync + 'static) -> Box> { + Box::new(Self(Box::new(custom))) + } +} +impl Component

for Debug

{ + fn execute(&self, problem: &P, state: &mut State) { + self.0(problem, state); + } +} + +impl Serialize for Debug

{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_unit_struct("Debug") + } +} + +#[derive(Serialize)] +pub struct PrintSingleObjectiveSummary; +impl PrintSingleObjectiveSummary { + pub fn new() -> Box> + where + P::Encoding: std::fmt::Debug, + { + Box::new(Self) + } +} +impl Component

for PrintSingleObjectiveSummary +where + P::Encoding: std::fmt::Debug, +{ + 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::

() { + println!("Optimum found: {:?}", individual.solution()); + println!("Best objective value: {:?}", individual.objective()); + } else { + println!("No solution found.") + } + println!("{}", "-".repeat(heading.len())); + } +} diff --git a/src/operators/mod.rs b/src/operators/mod.rs index f0858ce7..e367bd3c 100644 --- a/src/operators/mod.rs +++ b/src/operators/mod.rs @@ -2,39 +2,13 @@ #![allow(clippy::new_ret_no_self)] -use serde::Serialize; - -use crate::{ - framework::{components::Component, state::State}, - problems::Problem, -}; - pub mod archive; pub mod custom_state; pub mod diversity; pub mod evaluation; pub mod generation; pub mod initialization; +pub mod misc; pub mod replacement; pub mod selection; pub mod termination; - -/// Doesn't do anything. -/// -/// Note that this component is different from [initialization::Empty] as it doesn't modify -/// the state at all, while [Empty][initialization::Empty] pushes an empty population on the stack. -#[derive(Serialize)] -pub struct Noop; -impl Noop { - pub fn new

() -> Box> - where - P: Problem, - { - Box::new(Self) - } -} -impl Component

for Noop { - fn execute(&self, _problem: &P, _state: &mut State) { - // Noop - } -} diff --git a/src/operators/selection.rs b/src/operators/selection.rs index 8b9119e9..da70727d 100644 --- a/src/operators/selection.rs +++ b/src/operators/selection.rs @@ -257,6 +257,7 @@ impl DeterministicFitnessProportional { min_offspring: u32, max_offspring: u32, ) -> Box> { + assert!(min_offspring <= max_offspring); Box::new(Selector(Self { min_offspring, max_offspring, From 0cbd1bb4a999f1c0f48287e27a0b60d67dd73352 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 17 Aug 2022 16:35:38 +0200 Subject: [PATCH 07/10] Add a prelude --- src/lib.rs | 1 + src/prelude.rs | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 src/prelude.rs diff --git a/src/lib.rs b/src/lib.rs index 9479d922..7f52071e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod framework; pub mod heuristics; pub mod operators; +pub mod prelude; pub mod problems; pub mod tracking; pub mod utils; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 00000000..c30125aa --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,3 @@ +//! The MAHF prelude imports the most relevant modules, structs and traits you may need for experiments. + +pub use crate::{framework, heuristics::*, operators::*, problems, tracking}; From cc01633dd1163afacb44fff0cb9df05e7a1fdac2 Mon Sep 17 00:00:00 2001 From: Saethox Date: Wed, 17 Aug 2022 16:35:56 +0200 Subject: [PATCH 08/10] Adjust the examples and param-study --- examples/bmf.rs | 23 ++++++++++++++------ examples/coco.rs | 17 ++++++--------- examples/tsp.rs | 37 ++++++++++++++++---------------- param-study/src/instances/iwo.rs | 4 ++-- param-study/src/instances/pso.rs | 19 ++++++++++------ 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/examples/bmf.rs b/examples/bmf.rs index a2b13db6..fbd38379 100644 --- a/examples/bmf.rs +++ b/examples/bmf.rs @@ -1,19 +1,30 @@ -use mahf::heuristics::pso; -use mahf::{framework, problems::bmf::BenchmarkFunction}; +use mahf::prelude::*; + +type P = problems::bmf::BenchmarkFunction; fn main() -> anyhow::Result<()> { - let problem = BenchmarkFunction::sphere(30); - let config = pso::pso(100, 1., 1., 1., 1., 500); + let problem = P::sphere(30); + let config = pso::real_pso( + pso::RealParameters { + num_particles: 100, + a: 1.0, + b: 1.0, + c: 1.0, + v_max: 1.0, + }, + termination::FixedIterations::new(500), + tracking::Logger::default(), + ); let state = framework::run(&problem, &config, None); println!( "Found Fitness: {:?}", - state.best_objective_value::().unwrap(), + state.best_objective_value::

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

().unwrap(), ); println!("Global Optimum: {}", problem.known_optimum()); diff --git a/examples/coco.rs b/examples/coco.rs index 23422ddd..ac007339 100644 --- a/examples/coco.rs +++ b/examples/coco.rs @@ -1,14 +1,11 @@ -use mahf::{ - heuristics::iwo, - operators::termination, - problems::coco_bound::{suits, CocoInstance}, - tracking::{self, trigger, LogSet}, -}; +use mahf::prelude::*; +use problems::coco_bound::{suits, CocoInstance}; +use tracking::{functions, trigger}; fn main() -> anyhow::Result<()> { let output = "data/coco/iwo"; - let config = iwo::iwo( - iwo::Parameters { + let config = iwo::real_iwo( + iwo::RealParameters { initial_population_size: 5, max_population_size: 20, min_number_of_seeds: 0, @@ -21,9 +18,9 @@ fn main() -> anyhow::Result<()> { tracking::Logger::builder() .log_common_sets() .log_set( - LogSet::new() + tracking::LogSet::new() .with_trigger(trigger::Iteration::new(50)) - .with_logger(tracking::functions::best_individual::), + .with_logger(functions::best_individual::), ) .build(), ); diff --git a/examples/tsp.rs b/examples/tsp.rs index 0f04101b..20fdb154 100644 --- a/examples/tsp.rs +++ b/examples/tsp.rs @@ -1,27 +1,28 @@ -use mahf::{framework, problems, heuristics::*, operators::*, tracking::Logger}; +use mahf::prelude::*; + +type P = problems::tsp::SymmetricTsp; fn main() -> anyhow::Result<()> { let problem = problems::tsp::Instances::BERLIN52.load(); - // let tau0 = 1. / problem.best_fitness().unwrap(); - // let config = aco::min_max_ant_system(20, 1., 1., tau0, 0.1, 1., 0.1, 500); - let config = ils::permutation_iterated_local_search(ils::PermutationParameters { - local_search_params: ls::PermutationParameters { - n_neighbors: 20, - pm: 0.7, - n_swap: 5 + let config = ils::permutation_iterated_local_search( + ils::PermutationParameters { + local_search_params: ls::PermutationParameters { + n_neighbors: 100, + pm: 0.9, + n_swap: 10, + }, + local_search_termination: termination::FixedIterations::new(100), }, - local_search_termination: termination::FixedIterations::new(100), - }, termination::FixedIterations::new(10), Logger::default()); - - let state = framework::run(&problem, &config, None); + termination::FixedIterations::new(10), + tracking::Logger::default(), + ) + .into_builder() + .assert(|state| state.population_stack::

().current().len() == 1) + .single_objective_summary() + .build(); - println!( - "Found Solution: {:?}", - state - .best_objective_value::() - .unwrap() - ); + framework::run(&problem, &config, None); Ok(()) } diff --git a/param-study/src/instances/iwo.rs b/param-study/src/instances/iwo.rs index 59426047..fa3f023d 100644 --- a/param-study/src/instances/iwo.rs +++ b/param-study/src/instances/iwo.rs @@ -31,8 +31,8 @@ pub fn run(setup: &Setup, args: &mut ArgsIter) { let problem = BenchmarkFunction::try_from(setup.instance.as_str()).unwrap(); - let config = iwo::iwo( - iwo::Parameters { + let config = iwo::real_iwo( + iwo::RealParameters { initial_population_size: params.initial_population_size, max_population_size: params.max_population_size, min_number_of_seeds: params.min_number_of_seeds, diff --git a/param-study/src/instances/pso.rs b/param-study/src/instances/pso.rs index c14135e8..657530d1 100644 --- a/param-study/src/instances/pso.rs +++ b/param-study/src/instances/pso.rs @@ -4,7 +4,9 @@ use mahf::{ float_eq::float_eq, framework::{self, Random}, heuristics::pso, + operators::termination, problems::bmf::BenchmarkFunction, + tracking, }; use crate::{ @@ -25,13 +27,16 @@ pub fn run(setup: &Setup, args: &mut ArgsIter) { let problem = BenchmarkFunction::try_from(setup.instance.as_str()).unwrap(); - let config = pso::pso( - params.population_size, - params.a, - params.b, - params.c, - params.v_max, - setup.cutoff_length, + let config = pso::real_pso( + pso::RealParameters { + num_particles: params.population_size, + a: params.a, + b: params.b, + c: params.c, + v_max: params.v_max, + }, + termination::FixedIterations::new(setup.cutoff_length), + tracking::Logger::default(), ); let rng = Random::seeded(setup.seed); From 361b3ea6bac401f3193cd56b0a1d2f453be77645 Mon Sep 17 00:00:00 2001 From: Saethox Date: Mon, 29 Aug 2022 15:55:25 +0200 Subject: [PATCH 09/10] Rename heuristic parameters --- examples/bmf.rs | 2 +- examples/coco.rs | 2 +- examples/tsp.rs | 4 ++-- param-study/src/instances/iwo.rs | 2 +- param-study/src/instances/pso.rs | 2 +- src/heuristics/es.rs | 6 +++--- src/heuristics/ga.rs | 12 ++++++------ src/heuristics/ils.rs | 16 ++++++++-------- src/heuristics/iwo.rs | 6 +++--- src/heuristics/ls.rs | 12 ++++++------ src/heuristics/pso.rs | 6 +++--- src/heuristics/rw.rs | 12 ++++++------ 12 files changed, 41 insertions(+), 41 deletions(-) diff --git a/examples/bmf.rs b/examples/bmf.rs index fbd38379..e8f5f8fc 100644 --- a/examples/bmf.rs +++ b/examples/bmf.rs @@ -5,7 +5,7 @@ type P = problems::bmf::BenchmarkFunction; fn main() -> anyhow::Result<()> { let problem = P::sphere(30); let config = pso::real_pso( - pso::RealParameters { + pso::RealProblemParameters { num_particles: 100, a: 1.0, b: 1.0, diff --git a/examples/coco.rs b/examples/coco.rs index ac007339..027dc439 100644 --- a/examples/coco.rs +++ b/examples/coco.rs @@ -5,7 +5,7 @@ use tracking::{functions, trigger}; fn main() -> anyhow::Result<()> { let output = "data/coco/iwo"; let config = iwo::real_iwo( - iwo::RealParameters { + iwo::RealProblemParameters { initial_population_size: 5, max_population_size: 20, min_number_of_seeds: 0, diff --git a/examples/tsp.rs b/examples/tsp.rs index 20fdb154..fc15e7d9 100644 --- a/examples/tsp.rs +++ b/examples/tsp.rs @@ -6,8 +6,8 @@ fn main() -> anyhow::Result<()> { let problem = problems::tsp::Instances::BERLIN52.load(); let config = ils::permutation_iterated_local_search( - ils::PermutationParameters { - local_search_params: ls::PermutationParameters { + ils::PermutationProblemParameters { + local_search_params: ls::PermutationProblemParameters { n_neighbors: 100, pm: 0.9, n_swap: 10, diff --git a/param-study/src/instances/iwo.rs b/param-study/src/instances/iwo.rs index fa3f023d..081d027c 100644 --- a/param-study/src/instances/iwo.rs +++ b/param-study/src/instances/iwo.rs @@ -32,7 +32,7 @@ pub fn run(setup: &Setup, args: &mut ArgsIter) { let problem = BenchmarkFunction::try_from(setup.instance.as_str()).unwrap(); let config = iwo::real_iwo( - iwo::RealParameters { + iwo::RealProblemParameters { initial_population_size: params.initial_population_size, max_population_size: params.max_population_size, min_number_of_seeds: params.min_number_of_seeds, diff --git a/param-study/src/instances/pso.rs b/param-study/src/instances/pso.rs index 657530d1..623f9652 100644 --- a/param-study/src/instances/pso.rs +++ b/param-study/src/instances/pso.rs @@ -28,7 +28,7 @@ pub fn run(setup: &Setup, args: &mut ArgsIter) { let problem = BenchmarkFunction::try_from(setup.instance.as_str()).unwrap(); let config = pso::real_pso( - pso::RealParameters { + pso::RealProblemParameters { num_particles: params.population_size, a: params.a, b: params.b, diff --git a/src/heuristics/es.rs b/src/heuristics/es.rs index 4c23ceb4..a827d452 100644 --- a/src/heuristics/es.rs +++ b/src/heuristics/es.rs @@ -7,7 +7,7 @@ use crate::{ }; /// Parameters for [real_mu_plus_lambda]. -pub struct RealParameters { +pub struct RealProblemParameters { pub population_size: u32, pub lambda: u32, pub deviation: f64, @@ -19,14 +19,14 @@ pub struct RealParameters { /// # References /// [doi.org/10.1023/A:1015059928466](https://doi.org/10.1023/A:1015059928466) pub fn real_mu_plus_lambda

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - let RealParameters { + let RealProblemParameters { population_size, lambda, deviation, diff --git a/src/heuristics/ga.rs b/src/heuristics/ga.rs index f7b57a08..608a27cd 100644 --- a/src/heuristics/ga.rs +++ b/src/heuristics/ga.rs @@ -8,7 +8,7 @@ use crate::{ /// Parameters for [binary_ga]. #[derive(Clone, Copy, Debug)] -pub struct BinaryParameters { +pub struct BinaryProblemParameters { pub population_size: u32, pub tournament_size: u32, pub rm: f64, @@ -18,7 +18,7 @@ pub struct BinaryParameters { /// An example single-objective Genetic Algorithm operating on a binary search space. /// Uses the [ga] component internally. pub fn binary_ga

( - params: BinaryParameters, + params: BinaryProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

@@ -27,7 +27,7 @@ where + VectorProblem + LimitedVectorProblem, { - let BinaryParameters { + let BinaryProblemParameters { population_size, tournament_size, rm, @@ -56,7 +56,7 @@ where /// Parameters for [real_ga]. #[derive(Clone, Copy, Debug)] -pub struct RealParameters { +pub struct RealProblemParameters { pub population_size: u32, pub tournament_size: u32, pub deviation: f64, @@ -66,14 +66,14 @@ pub struct RealParameters { /// An example single-objective Genetic Algorithm operating on a real search space. /// Uses the [ga] component internally. pub fn real_ga

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - let RealParameters { + let RealProblemParameters { population_size, tournament_size, deviation, diff --git a/src/heuristics/ils.rs b/src/heuristics/ils.rs index 965ffe81..f0aa456b 100644 --- a/src/heuristics/ils.rs +++ b/src/heuristics/ils.rs @@ -9,22 +9,22 @@ use crate::{ }; /// Parameters for [real_iterated_local_search]. -pub struct RealParameters

{ - pub local_search_params: ls::RealParameters, +pub struct RealProblemParameters

{ + pub local_search_params: ls::RealProblemParameters, pub local_search_termination: Box>, } /// An example single-objective Iterated Local Search operating on a real search space. /// Uses the [iterated_local_search] component internally. pub fn real_iterated_local_search

( - params: RealParameters

, + params: RealProblemParameters

, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - let RealParameters { + let RealProblemParameters { local_search_params, local_search_termination, } = params; @@ -50,22 +50,22 @@ where } /// Parameters for [iterated_local_permutation_search]. -pub struct PermutationParameters

{ - pub local_search_params: ls::PermutationParameters, +pub struct PermutationProblemParameters

{ + pub local_search_params: ls::PermutationProblemParameters, pub local_search_termination: Box>, } /// An example single-objective Iterated Local Search operating on a permutation search space. /// Uses the [iterated_local_search] component internally. pub fn permutation_iterated_local_search

( - params: PermutationParameters

, + params: PermutationProblemParameters

, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem, { - let PermutationParameters { + let PermutationProblemParameters { local_search_params, local_search_termination, } = params; diff --git a/src/heuristics/iwo.rs b/src/heuristics/iwo.rs index 6691532b..38e84400 100644 --- a/src/heuristics/iwo.rs +++ b/src/heuristics/iwo.rs @@ -7,7 +7,7 @@ use crate::{ }; #[derive(Clone, Debug)] -pub struct RealParameters { +pub struct RealProblemParameters { pub initial_population_size: u32, pub max_population_size: u32, pub min_number_of_seeds: u32, @@ -28,14 +28,14 @@ pub struct RealParameters { /// # References /// [doi.org/10.1016/j.ecoinf.2006.07.003](https://doi.org/10.1016/j.ecoinf.2006.07.003) pub fn real_iwo

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - let RealParameters { + let RealProblemParameters { initial_population_size, max_population_size, min_number_of_seeds, diff --git a/src/heuristics/ls.rs b/src/heuristics/ls.rs index a42b94c8..ef1ff64f 100644 --- a/src/heuristics/ls.rs +++ b/src/heuristics/ls.rs @@ -7,7 +7,7 @@ use crate::{ }; /// Parameters for [real_local_search]. -pub struct RealParameters { +pub struct RealProblemParameters { pub n_neighbors: u32, pub deviation: f64, } @@ -15,14 +15,14 @@ pub struct RealParameters { /// An example single-objective Local Search operating on a real search space. /// Uses the [local_search] component internally. pub fn real_local_search

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem + LimitedVectorProblem, { - let RealParameters { + let RealProblemParameters { n_neighbors, deviation, } = params; @@ -44,7 +44,7 @@ where } /// Parameters for [permutation_local_search]. -pub struct PermutationParameters { +pub struct PermutationProblemParameters { pub n_neighbors: u32, pub pm: f64, pub n_swap: usize, @@ -53,14 +53,14 @@ pub struct PermutationParameters { /// An example single-objective Local Search operating on a permutation search space. /// Uses the [local_search] component internally. pub fn permutation_local_search

( - params: PermutationParameters, + params: PermutationProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem, { - let PermutationParameters { + let PermutationProblemParameters { n_neighbors, pm, n_swap, diff --git a/src/heuristics/pso.rs b/src/heuristics/pso.rs index 9809e973..46ebe073 100644 --- a/src/heuristics/pso.rs +++ b/src/heuristics/pso.rs @@ -7,7 +7,7 @@ use crate::{ }; /// Parameters for [real_pso]. -pub struct RealParameters { +pub struct RealProblemParameters { pub num_particles: u32, pub a: f64, pub b: f64, @@ -18,14 +18,14 @@ pub struct RealParameters { /// An example single-objective Particle Swarm Optimization operating on a real search space. /// Uses the [pso] component internally. pub fn real_pso

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + LimitedVectorProblem + 'static, { - let RealParameters { + let RealProblemParameters { num_particles, a, b, diff --git a/src/heuristics/rw.rs b/src/heuristics/rw.rs index 0cff180c..0a5f9a3d 100644 --- a/src/heuristics/rw.rs +++ b/src/heuristics/rw.rs @@ -7,21 +7,21 @@ use crate::{ }; /// Parameters for [real_random_walk]. -pub struct RealParameters { +pub struct RealProblemParameters { pub deviation: f64, } /// An example single-objective Random Walk operating on a real search space. /// Uses the [random_walk] component internally. pub fn real_random_walk

( - params: RealParameters, + params: RealProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + LimitedVectorProblem, { - let RealParameters { deviation } = params; + let RealProblemParameters { deviation } = params; Configuration::builder() .do_(generation::RandomSpread::new_init(1)) @@ -36,7 +36,7 @@ where } /// Parameters for [permutation_random_walk]. -pub struct PermutationParameters { +pub struct PermutationProblemParameters { pub pm: f64, pub n_swap: usize, } @@ -44,14 +44,14 @@ pub struct PermutationParameters { /// An example single-objective Random Walk operating on a permutation search space. /// Uses the [random_walk] component internally. pub fn permutation_random_walk

( - params: PermutationParameters, + params: PermutationProblemParameters, termination: Box>, logger: Box>, ) -> Configuration

where P: SingleObjectiveProblem> + VectorProblem, { - let PermutationParameters { pm, n_swap } = params; + let PermutationProblemParameters { pm, n_swap } = params; Configuration::builder() .do_(generation::RandomPermutation::new_init(1)) From bcba36de7a95471d6cac6ece0498d2d229735541 Mon Sep 17 00:00:00 2001 From: Saethox Date: Tue, 30 Aug 2022 14:38:37 +0200 Subject: [PATCH 10/10] Address review comments --- src/framework/configuration.rs | 7 ++++--- src/heuristics/aco.rs | 2 +- src/heuristics/es.rs | 6 +++--- src/heuristics/ga.rs | 8 ++++---- src/heuristics/ils.rs | 6 +++--- src/heuristics/iwo.rs | 4 ++-- src/heuristics/ls.rs | 6 +++--- src/heuristics/pso.rs | 4 ++-- src/heuristics/rs.rs | 6 +++--- src/heuristics/rw.rs | 2 +- src/operators/evaluation.rs | 6 +++--- src/operators/misc.rs | 2 ++ 12 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/framework/configuration.rs b/src/framework/configuration.rs index a0c9e260..ca4525aa 100644 --- a/src/framework/configuration.rs +++ b/src/framework/configuration.rs @@ -55,7 +55,7 @@ impl ConfigurationBuilder

{ self } - pub fn do_if_some_(self, component: Option>>) -> Self { + pub fn do_optional_(self, component: Option>>) -> Self { if let Some(component) = component { self.do_(component) } else { @@ -118,6 +118,7 @@ impl ConfigurationBuilder

{ /// Asserts the condition on [State][state::State]. /// /// Uses the [Debug][operators::misc::Debug] component internally. + #[track_caller] pub fn assert(self, assert: impl Fn(&state::State) -> bool + Send + Sync + 'static) -> Self { self.debug(move |_problem, state| assert!(assert(state))) } @@ -127,8 +128,8 @@ impl ConfigurationBuilder

{ self.do_(operators::misc::Debug::new(behaviour)) } - pub fn evaluate_serial(self) -> Self { - self.do_(operators::evaluation::SerialEvaluator::new()) + pub fn evaluate_sequential(self) -> Self { + self.do_(operators::evaluation::SequentialEvaluator::new()) } } diff --git a/src/heuristics/aco.rs b/src/heuristics/aco.rs index 381440f8..82dd1c4b 100644 --- a/src/heuristics/aco.rs +++ b/src/heuristics/aco.rs @@ -127,7 +127,7 @@ pub fn aco( .while_(termination, |builder| { builder .do_(generation) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(pheromone_update) .do_(logger) diff --git a/src/heuristics/es.rs b/src/heuristics/es.rs index a827d452..1300278a 100644 --- a/src/heuristics/es.rs +++ b/src/heuristics/es.rs @@ -34,7 +34,7 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(es( Parameters { @@ -75,9 +75,9 @@ pub fn es( builder .do_(selection) .do_(mutation) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() - .do_if_some_(archive) + .do_optional_(archive) .do_(replacement) .do_(logger) }) diff --git a/src/heuristics/ga.rs b/src/heuristics/ga.rs index 608a27cd..578e3057 100644 --- a/src/heuristics/ga.rs +++ b/src/heuristics/ga.rs @@ -38,7 +38,7 @@ where .do_(initialization::RandomBitstring::new_uniform_init( population_size, )) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(ga( Parameters { @@ -82,7 +82,7 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(population_size)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(ga( Parameters { @@ -130,9 +130,9 @@ pub fn ga( .do_(selection) .do_(crossover) .do_(mutation) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() - .do_if_some_(archive) + .do_optional_(archive) .do_(replacement) .do_(logger) }) diff --git a/src/heuristics/ils.rs b/src/heuristics/ils.rs index f0aa456b..db310df3 100644 --- a/src/heuristics/ils.rs +++ b/src/heuristics/ils.rs @@ -31,7 +31,7 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(iterated_local_search( Parameters { @@ -72,7 +72,7 @@ where Configuration::builder() .do_(initialization::RandomPermutation::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(iterated_local_search( Parameters { @@ -111,7 +111,7 @@ pub fn iterated_local_search( .while_(termination, |builder| { builder .do_(perturbation) - .evaluate_serial() + .evaluate_sequential() .do_(selection::All::new()) .scope_(|builder| builder.do_(local_search)) .update_best_individual() diff --git a/src/heuristics/iwo.rs b/src/heuristics/iwo.rs index 38e84400..f6759dde 100644 --- a/src/heuristics/iwo.rs +++ b/src/heuristics/iwo.rs @@ -90,7 +90,7 @@ pub fn iwo( } = params; Configuration::builder() - .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::SequentialEvaluator::new()) .while_(termination, |builder| { builder .do_(selection::DeterministicFitnessProportional::new( @@ -98,7 +98,7 @@ pub fn iwo( max_number_of_seeds, )) .do_(mutation) - .do_(evaluation::SerialEvaluator::new()) + .do_(evaluation::SequentialEvaluator::new()) .do_(replacement::MuPlusLambda::new(max_population_size)) .do_(logger) }) diff --git a/src/heuristics/ls.rs b/src/heuristics/ls.rs index ef1ff64f..51f2acc9 100644 --- a/src/heuristics/ls.rs +++ b/src/heuristics/ls.rs @@ -29,7 +29,7 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(evaluation::UpdateBestIndividual::new()) .do_(local_search( @@ -68,7 +68,7 @@ where Configuration::builder() .do_(initialization::RandomPermutation::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(local_search( Parameters { @@ -103,7 +103,7 @@ pub fn local_search( builder .do_(selection::DuplicateSingle::new(n_neighbors)) .do_(neighbors) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(replacement::MuPlusLambda::new(1)) .do_(logger) diff --git a/src/heuristics/pso.rs b/src/heuristics/pso.rs index 46ebe073..1b8981c0 100644 --- a/src/heuristics/pso.rs +++ b/src/heuristics/pso.rs @@ -35,7 +35,7 @@ where Configuration::builder() .do_(initialization::RandomSpread::new_init(num_particles)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(pso( Parameters { @@ -76,7 +76,7 @@ where .while_(termination, |builder| { builder .do_(particle_update) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(state_update) .do_(logger) diff --git a/src/heuristics/rs.rs b/src/heuristics/rs.rs index 78ebe7d3..9043af9a 100644 --- a/src/heuristics/rs.rs +++ b/src/heuristics/rs.rs @@ -17,7 +17,7 @@ where { Configuration::builder() .do_(generation::RandomSpread::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(random_search( Parameters { @@ -40,7 +40,7 @@ where { Configuration::builder() .do_(generation::RandomPermutation::new_init(1)) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(random_search( Parameters { @@ -73,7 +73,7 @@ where builder .do_(selection::All::new()) .do_(randomizer) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(replacement::MuPlusLambda::new(1)) .do_(logger) diff --git a/src/heuristics/rw.rs b/src/heuristics/rw.rs index 0a5f9a3d..81a2e8ed 100644 --- a/src/heuristics/rw.rs +++ b/src/heuristics/rw.rs @@ -85,7 +85,7 @@ where builder .do_(selection::All::new()) .do_(neighbor) - .evaluate_serial() + .evaluate_sequential() .update_best_individual() .do_(replacement::Generational::new(1)) .do_(logger) diff --git a/src/operators/evaluation.rs b/src/operators/evaluation.rs index 83fffa84..33e6cc2a 100644 --- a/src/operators/evaluation.rs +++ b/src/operators/evaluation.rs @@ -9,15 +9,15 @@ use crate::{ }; #[derive(Serialize)] -pub struct SerialEvaluator; +pub struct SequentialEvaluator; -impl SerialEvaluator { +impl SequentialEvaluator { pub fn new() -> Box> { Box::new(Self) } } -impl Component

for SerialEvaluator { +impl Component

for SequentialEvaluator { fn initialize(&self, _problem: &P, state: &mut State) { state.require::>(); state.insert(common::Evaluations(0)); diff --git a/src/operators/misc.rs b/src/operators/misc.rs index af415b61..521b6325 100644 --- a/src/operators/misc.rs +++ b/src/operators/misc.rs @@ -69,6 +69,8 @@ impl Serialize for Debug

{ } } +/// Prints a summary of the current [State] for single-objective problems. +/// The summary includes statistics like number of iterations, evaluations and best solution found yet. #[derive(Serialize)] pub struct PrintSingleObjectiveSummary; impl PrintSingleObjectiveSummary {