├── .github └── workflows │ └── test.yaml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── optlib-testfunc ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── optlib ├── Cargo.toml ├── examples ├── genetic-paraboloid.rs ├── genetic-rastrigin.rs ├── genetic-rosenbrock-statistics.rs ├── genetic-rosenbrock.rs ├── genetic-schwefel-iterative.rs ├── genetic-schwefel-statistics.rs ├── genetic-schwefel.rs ├── particleswarm-paraboloid.rs ├── particleswarm-rastrigin-statistics-inertia.rs ├── particleswarm-rastrigin.rs ├── particleswarm-rosenbrock-statistics.rs ├── particleswarm-rosenbrock.rs ├── particleswarm-schwefel-iterative.rs ├── particleswarm-schwefel-statistics-full.rs ├── particleswarm-schwefel-statistics-inertia.rs ├── particleswarm-schwefel-statistics.rs └── particleswarm-schwefel.rs ├── src ├── genetic │ ├── creation │ │ ├── mod.rs │ │ └── vec_float.rs │ ├── cross │ │ └── mod.rs │ ├── mod.rs │ ├── mutation │ │ └── mod.rs │ ├── pairing │ │ └── mod.rs │ ├── pre_birth │ │ ├── mod.rs │ │ └── vec_float.rs │ └── selection │ │ ├── mod.rs │ │ └── vec_float.rs ├── lib.rs ├── particleswarm │ ├── initializing │ │ └── mod.rs │ ├── mod.rs │ ├── postmove │ │ └── mod.rs │ ├── postvelocitycalc │ │ └── mod.rs │ └── velocitycalc │ │ └── mod.rs └── tools │ ├── logging │ └── mod.rs │ ├── mod.rs │ ├── statistics │ └── mod.rs │ └── stopchecker │ └── mod.rs └── tests ├── genetic-paraboloid.rs ├── genetic-rastrigin.rs ├── genetic-rosenbrock.rs ├── genetic-schwefel.rs ├── genetic-stat-paraboloid.rs └── particleswarm-paraboloid.rs /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Build main 11 | run: cargo build --workspace --release 12 | - name: Build examples 13 | run: cargo build --examples --release 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Run tests 19 | run: cargo test --verbose --release 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | target 12 | 13 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 14 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 15 | Cargo.lock 16 | 17 | tmp 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Optlib changelog 2 | 3 | ## 0.4.0 4 | 5 | ### Particle swarm optimization 6 | 1. Add RandomTeleport structure to move particle to random point with given probability. 7 | 8 | ### Examples 9 | 1. Examples with statistics gathering use parallelism. 10 | 1. Add more examples. 11 | 12 | 13 | ## 0.3.0 14 | 15 | ### API 16 | 1. Some parameters of optimizer constructor moved from constructor parameters to separate setters. 17 | 1. Add logger for statistics gathering. 18 | 1. The next_iterations method move to base trait IterativeOptimizer. 19 | 20 | ### Particle swarm optimization 21 | 1. Add the structures for calculate particle velocity considering inertia. 22 | 1. Add the possibility for correcting velocity after calculating (used for limit the velocity) 23 | 1. Add the items for limit velocity by modulus and directions. 24 | 25 | ### Statistics 26 | 1. Add the items for gathering algorithms statistics. 27 | 1. Add structures for calculate average convergence (average value of goal function on the iteration number and after the algorithm is complete). 28 | 1. Add the items for calculate standard deviation value of goal function after the algorithm is complete 29 | 1. Add the items for calculate average solution if solution is of the type Vec<Float>. 30 | 1. Add the items for calculate standard deviation of solution if solution is of the type Vec<Float>. 31 | 1. Add the items for calculate a success rate by value of goal function. 32 | 1. Add the items for calculate a success rate by found solution if solution is of the type Vec<Float>. 33 | 1. Add the items for calculate goal function call count. 34 | 35 | ### Examples 36 | 1. Add new example with statistics gathering for genetic algorithm. 37 | 1. Add new examples with statistics gathering for particle swarm optimization. 38 | 39 | 40 | 41 | ## 0.2.0 42 | 43 | ### API 44 | 45 | 1. The loggers can save content with Write trait. 46 | 1. The Goal trait moved to optlib module. 47 | 1. The test functions moved to optlib-testfunc crate. 48 | 1. Add new integration tests with optimization. 49 | 50 | ### Particle swarm optimization 51 | 1. Add the structures for particle swarm optimization. 52 | 53 | ### Examples 54 | 1. Add new example for optimization of Schwefel function with particle swarm optimization. 55 | 1. Add new example for optimization of Rastrigin function with particle swarm optimization. 56 | 57 | 58 | ## 0.1.0 59 | 60 | 1. The first version 61 | 1. Add genetic algorithm implementation. 62 | 1. Add test Schwefel function. 63 | 64 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "optlib", 4 | "optlib-testfunc", 5 | ] 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Eugeniy Ilin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # optlib 2 | 3 | [![Current Version](https://img.shields.io/crates/v/optlib.svg)](https://crates.io/crates/optlib) 4 | [![Documentation](https://docs.rs/optlib/badge.svg)](https://docs.rs/optlib) 5 | [![License](https://img.shields.io/crates/l/optlib.svg)](https://crates.io/crates/optlib) 6 | 7 | Optimization algorithms implemented in Rust 8 | 9 | For now optlib provides genetic algorithm and partcile swarm algorithm. 10 | 11 | 12 | ## Example of optimization 13 | 14 | ```rust 15 | //! Example of optimizing the Schwefel function with genetic algorithm. 16 | //! 17 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 18 | //! Global minimum is x' = (420.9687, 420.9687, ...) for any xi lying in [-500.0; 500.0]. 19 | //! f(x') = 0 20 | //! 21 | //! # Terms 22 | //! * `Goal function` - the function for optimization. y = f(x). 23 | //! * `Gene` - a single value of xi. 24 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 25 | //! * `Individual` - union of x and value of goal function. 26 | //! * `Population` - set of the individuals. 27 | //! * `Generation` - a number of iteration of genetic algorithm. 28 | use std::io; 29 | 30 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 31 | use optlib::tools::logging; 32 | use optlib::tools::stopchecker; 33 | use optlib::{GoalFromFunction, Optimizer}; 34 | use optlib_testfunc; 35 | 36 | /// Gene type 37 | type Gene = f32; 38 | 39 | /// Chromosomes type 40 | type Chromosomes = Vec; 41 | 42 | fn main() { 43 | // General parameters 44 | 45 | // Search space. Any xi lies in [-500.0; 500.0] 46 | let minval: Gene = -500.0; 47 | let maxval: Gene = 500.0; 48 | 49 | // Count individuals in initial population 50 | let population_size = 500; 51 | 52 | // Count of xi in the chromosomes 53 | let chromo_count = 15; 54 | 55 | let intervals = vec![(minval, maxval); chromo_count]; 56 | 57 | // Make a trait object for goal function (Schwefel function) 58 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 59 | 60 | // Make the creator to create initial population. 61 | // RandomCreator will fill initial population with individuals with random chromosomes in a 62 | // given interval, 63 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 64 | 65 | // Make a trait object for the pairing. 66 | // Pairing is algorithm of selection individuals for crossbreeding. 67 | 68 | // Select random individuals from the population. 69 | // let pairing = pairing::RandomPairing::new(); 70 | 71 | // Tournament method. 72 | let families_count = population_size / 2; 73 | let rounds_count = 5; 74 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 75 | 76 | // Crossbreeding algorithm. 77 | // Make a Cross trait object. The bitwise crossing for float genes. 78 | let single_cross = cross::FloatCrossExp::new(); 79 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 80 | 81 | // Make a Mutation trait object. 82 | // Use bitwise mutation (change random bits with given probability). 83 | let mutation_probability = 15.0; 84 | let mutation_gene_count = 3; 85 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 86 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 87 | 88 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 89 | let pre_births: Vec>> = vec![Box::new( 90 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 91 | )]; 92 | 93 | // Stop checker. Stop criterion for genetic algorithm. 94 | // Stop algorithm if the value of goal function will become less of 1e-4 or 95 | // after 3000 generations (iterations). 96 | let stop_checker = stopchecker::CompositeAny::new(vec![ 97 | Box::new(stopchecker::Threshold::new(1e-4)), 98 | Box::new(stopchecker::MaxIterations::new(3000)), 99 | ]); 100 | 101 | // Make a trait object for selection. Selection is killing the worst individuals. 102 | // Kill all individuals if the value of goal function is NaN or Inf. 103 | // Kill the worst individuals to population size remained unchanged. 104 | let selections: Vec>> = vec![ 105 | Box::new(selection::KillFitnessNaN::new()), 106 | Box::new(selection::LimitPopulation::new(population_size)), 107 | ]; 108 | 109 | // Make a loggers trait objects 110 | let mut stdout_result = io::stdout(); 111 | let mut stdout_time = io::stdout(); 112 | 113 | let loggers: Vec>> = vec![ 114 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 115 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 116 | ]; 117 | 118 | // Construct main optimizer struct 119 | let mut optimizer = genetic::GeneticOptimizer::new( 120 | Box::new(goal), 121 | Box::new(stop_checker), 122 | Box::new(creator), 123 | Box::new(pairing), 124 | Box::new(cross), 125 | Box::new(mutation), 126 | selections, 127 | pre_births, 128 | ); 129 | optimizer.set_loggers(loggers); 130 | 131 | // Run genetic algorithm 132 | optimizer.find_min(); 133 | } 134 | 135 | ``` 136 | 137 | Build all crates: 138 | 139 | ``` 140 | cargo build --release --all 141 | ``` 142 | 143 | Run example: 144 | 145 | ``` 146 | cargo run --example genetic-schwefel --release 147 | ``` 148 | 149 | Work result: 150 | 151 | ``` 152 | Solution: 153 | 420.974975585937500 154 | 420.969146728515625 155 | 420.955078125000000 156 | 421.004760742187500 157 | 420.999511718750000 158 | 421.007263183593750 159 | 420.987487792968750 160 | 421.001800537109375 161 | 420.980499267578125 162 | 420.991180419921875 163 | 421.001068115234375 164 | 420.942718505859375 165 | 420.964080810546875 166 | 420.951721191406250 167 | 420.961029052734375 168 | 169 | 170 | Goal: 0.000488281250000 171 | Iterations count: 3000 172 | Time elapsed: 2352 ms 173 | ``` 174 | 175 | Also optlib library contains other optimization examples: 176 | 177 | * genetic-paraboloid 178 | * genetic-rastrigin 179 | * genetic-rosenbrock 180 | * genetic-schwefel 181 | * genetic-schwefel-iterative 182 | * genetic-schwefel-statistics 183 | * particleswarm-paraboloid 184 | * particleswarm-rastrigin 185 | * particleswarm-rastrigin-statistics-inertia 186 | * particleswarm-rosenbrock 187 | * particleswarm-schwefel 188 | * particleswarm-schwefel-iterative 189 | * particleswarm-schwefel-statistics 190 | * particleswarm-schwefel-statistics-full 191 | * particleswarm-schwefel-statistics-inertia 192 | -------------------------------------------------------------------------------- /optlib-testfunc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optlib-testfunc" 3 | version = "0.1.0" 4 | description = "The functions to test optimization algorithms." 5 | authors = ["Eugeniy Ilin "] 6 | edition = "2018" 7 | keywords = ["optimization", "math", "functions"] 8 | categories = ["algorithms", "science"] 9 | homepage = "https://github.com/Jenyay/rust-optimization" 10 | repository = "https://github.com/Jenyay/rust-optimization" 11 | license = "MIT" 12 | readme = "README.md" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | num = "0.2" 18 | 19 | [badges] 20 | maintenance = { status = "actively-developed" } 21 | -------------------------------------------------------------------------------- /optlib-testfunc/README.md: -------------------------------------------------------------------------------- 1 | # optlib-testfunc 2 | 3 | [![Current Version](https://img.shields.io/crates/v/optlib_testfunc.svg)](https://crates.io/crates/optlib-testfunc) 4 | [![Documentation](https://docs.rs/optlib-testfunc/badge.svg)](https://docs.rs/optlib-testfunc) 5 | [![License](https://img.shields.io/crates/l/optlib_testfunc.svg)](https://crates.io/crates/optlib-testfunc) 6 | 7 | The crate contains functions for optimization algorithms testing. All functions have one global minimum. 8 | 9 | Optlib-testfunc contains in this version follow function: 10 | 11 | * Paraboloid. y = (x0 - 1)^2 + (x1 - 2)^2 + (x2 - 3)^2 ... (xn - n)^2. For any x global minimum located in x' = (1.0, 2.0, ..., n). f(x') = 0. 12 | * The Schwefel function. For any x lies in [-500.0; 500.0] global minimum located in x' = (420.9687, 420.9687, ...). f(x') = 0. 13 | * The Rastrigin function. For any x lies in [-5.12; 5.12] global minimum located in x' = (0, 0, ...). f(x') = 0. 14 | * The Rosenbrock function. For any x lies in [-inf; inf] global minimum located in x' = (1, 1, ...). f(x') = 0. 15 | -------------------------------------------------------------------------------- /optlib-testfunc/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The module with functions to test optimization algorithms. 2 | 3 | use num::Float; 4 | 5 | 6 | /// Paraboloid. 7 | /// 8 | /// y = (x0 - 1)^2 + (x1 - 2)^2 + (x2 - 3)^2 ... (xn - n)^2 9 | /// The min val is 0.0 for point (1.0, 2.0, 3.0, ... n). 10 | /// 11 | /// ``` 12 | /// use optlib_testfunc::paraboloid; 13 | /// 14 | /// let x = vec![1.0, 2.0, 3.0, 4.0, 5.0]; 15 | /// let value = paraboloid(&x); 16 | /// 17 | /// assert!(value < 1e-5); 18 | /// assert!(value >= 0.0); 19 | /// ``` 20 | pub fn paraboloid(x: &Vec) -> f64 { 21 | let mut result = G::from(0.0).unwrap(); 22 | for (n, val) in x.iter().enumerate() { 23 | result = result + (*val - (G::from(n).unwrap() + G::from(1.0).unwrap())).powi(2); 24 | } 25 | 26 | result.to_f64().unwrap() 27 | } 28 | 29 | /// The Schwefel function 30 | /// 31 | /// # Parameters 32 | /// Any x lies in [-500.0; 500.0]. 33 | /// For any x lies in [-500.0; 500.0] global minimum located in x' = (420.9687, 420.9687, ...). 34 | /// f(x') = 0. 35 | /// 36 | /// ``` 37 | /// use optlib_testfunc::schwefel; 38 | /// 39 | /// let x = vec![420.9687, 420.9687, 420.9687, 420.9687]; 40 | /// let value = schwefel(&x); 41 | /// assert!(value.abs() < 1e-4); 42 | /// ``` 43 | pub fn schwefel(x: &Vec) -> f64 { 44 | let result = G::from(418.9829).unwrap() * G::from(x.len()).unwrap() - x.iter().fold(G::zero(), |acc, &xi| acc + xi * xi.abs().sqrt().sin()); 45 | 46 | result.to_f64().unwrap() 47 | } 48 | 49 | /// The Rastrigin function 50 | /// 51 | /// # Parameters 52 | /// Global minimum is x' = (0, 0, ...) for xn in (-5.12; +5.12) 53 | /// f(x') = 0 54 | /// 55 | /// ``` 56 | /// use optlib_testfunc::rastrigin; 57 | /// 58 | /// let x = vec![0.0_f32, 0.0_f32, 0.0_f32, 0.0_f32, 0.0_f32, 0.0_f32]; 59 | /// let value = rastrigin(&x); 60 | /// assert!(value.abs() < 1e-7); 61 | /// ``` 62 | pub fn rastrigin(x: &Vec) -> f64 { 63 | let a = G::from(10.0_f64).unwrap(); 64 | let pi = G::from(3.14159265358979_f64).unwrap(); 65 | let result = a * G::from(x.len()).unwrap() + 66 | x.iter().fold(G::zero(), |acc, &xi| acc + xi * xi - a * (G::from(2).unwrap() * pi * xi).cos()); 67 | 68 | result.to_f64().unwrap() 69 | } 70 | 71 | /// The Rosenbrock function 72 | /// 73 | /// # Parameters 74 | /// Global minimum is x' = (1, 1, ...) for xn in (-inf; +inf) 75 | /// f(x') = 0 76 | /// 77 | /// ``` 78 | /// use optlib_testfunc::rosenbrock; 79 | /// 80 | /// let x = vec![1.0_f32, 1.0_f32, 1.0_f32, 1.0_f32, 1.0_f32, 1.0_f32]; 81 | /// let value = rosenbrock(&x); 82 | /// assert!(value.abs() < 1e-7); 83 | /// ``` 84 | pub fn rosenbrock(x: &Vec) -> f64 { 85 | let mut sum = G::from(0.0).unwrap(); 86 | for n in 0..x.len() - 1 { 87 | sum = sum + G::from(100.0).unwrap() * ((x[n + 1] - x[n] * x[n]).powi(2)) + (G::from(1.0).unwrap() - x[n]).powi(2); 88 | } 89 | 90 | sum.to_f64().unwrap() 91 | } 92 | -------------------------------------------------------------------------------- /optlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optlib" 3 | version = "0.4.0" 4 | authors = ["Eugene Ilin "] 5 | edition = "2018" 6 | keywords = ["genetic", "optimization", "math", "algorithms"] 7 | categories = ["algorithms", "science"] 8 | description = "The optimization algorithms realized in Rust. In given time realized genetic and particle swarm algorithms." 9 | license = "MIT" 10 | readme = "../README.md" 11 | homepage = "https://github.com/Jenyay/rust-optimization" 12 | repository = "https://github.com/Jenyay/rust-optimization" 13 | 14 | [dependencies] 15 | num = "0.2.1" 16 | rand = "0.7.3" 17 | 18 | [dev-dependencies] 19 | optlib-testfunc = { path = "../optlib-testfunc", version = "0.1.0"} 20 | num_cpus = "1.12.0" 21 | 22 | [badges] 23 | maintenance = { status = "actively-developed" } 24 | -------------------------------------------------------------------------------- /optlib/examples/genetic-paraboloid.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 4 | use optlib::tools::logging; 5 | use optlib::tools::stopchecker; 6 | use optlib::{GoalFromFunction, Optimizer}; 7 | use optlib_testfunc; 8 | 9 | type Gene = f32; 10 | type Chromosomes = Vec; 11 | 12 | fn main() { 13 | // General parameters 14 | let minval: Gene = -100.0; 15 | let maxval: Gene = 100.0; 16 | let population_size = 200; 17 | let chromo_count = 5; 18 | let intervals = vec![(minval, maxval); chromo_count]; 19 | 20 | // Goal function 21 | let goal = GoalFromFunction::new(optlib_testfunc::paraboloid); 22 | 23 | // Creator 24 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 25 | 26 | // Pairing 27 | // let pairing = pairing::RandomPairing::new(); 28 | let families_count = population_size / 2; 29 | let partners_count = 2; 30 | let rounds_count = 2; 31 | let pairing = pairing::Tournament::new(families_count) 32 | .partners_count(partners_count) 33 | .rounds_count(rounds_count); 34 | 35 | // Cross 36 | let single_cross = cross::FloatCrossExp::new(); 37 | // let single_cross = cross::CrossBitwise::new(); 38 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 39 | 40 | // Mutation 41 | let mutation_probability = 15.0; 42 | let mutation_gene_count = 3; 43 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 44 | // let single_cross = cross::CrossMean::new(); 45 | // let single_cross = cross::FloatCrossGeometricMean::new(); 46 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 47 | 48 | // Pre birth 49 | let pre_births: Vec>> = vec![Box::new( 50 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 51 | )]; 52 | 53 | // Selection 54 | let selections: Vec>> = vec![ 55 | Box::new(selection::KillFitnessNaN::new()), 56 | Box::new(selection::LimitPopulation::new(population_size)), 57 | ]; 58 | 59 | // Stop checker 60 | // let change_max_iterations = 150; 61 | // let change_delta = 1e-7; 62 | let stop_checker = stopchecker::CompositeAny::new(vec![ 63 | Box::new(stopchecker::Threshold::new(1e-6)), 64 | // Box::new(stopchecker::GoalNotChange::new( 65 | // change_max_iterations, 66 | // change_delta, 67 | // )), 68 | Box::new(stopchecker::MaxIterations::new(3000)), 69 | ]); 70 | 71 | // Logger 72 | let mut stdout_verbose = io::stdout(); 73 | let mut stdout_result = io::stdout(); 74 | let mut stdout_time = io::stdout(); 75 | 76 | let loggers: Vec>> = vec![ 77 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 78 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 79 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 80 | ]; 81 | 82 | let mut optimizer = genetic::GeneticOptimizer::new( 83 | Box::new(goal), 84 | Box::new(stop_checker), 85 | Box::new(creator), 86 | Box::new(pairing), 87 | Box::new(cross), 88 | Box::new(mutation), 89 | selections, 90 | pre_births, 91 | ); 92 | optimizer.set_loggers(loggers); 93 | 94 | optimizer.find_min(); 95 | } 96 | -------------------------------------------------------------------------------- /optlib/examples/genetic-rastrigin.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rastrigin function. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (0.0, 0.0, ...) for any xi lying in [-5.12; 5.12]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::io; 15 | 16 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 17 | use optlib::tools::logging; 18 | use optlib::tools::stopchecker; 19 | use optlib::{GoalFromFunction, Optimizer}; 20 | use optlib_testfunc; 21 | 22 | /// Gene type 23 | type Gene = f32; 24 | 25 | /// Chromosomes type 26 | type Chromosomes = Vec; 27 | 28 | fn main() { 29 | // General parameters 30 | 31 | // Search space. Any xi lies in [-5.12; 5.12] 32 | let minval: Gene = -5.12; 33 | let maxval: Gene = 5.12; 34 | 35 | // Count individuals in initial population 36 | let population_size = 20; 37 | 38 | // Count of xi in the chromosomes 39 | let chromo_count = 30; 40 | 41 | let intervals = vec![(minval, maxval); chromo_count]; 42 | 43 | // Make a trait object for goal function (Schwefel function) 44 | let goal = GoalFromFunction::new(optlib_testfunc::rastrigin); 45 | 46 | // Make the creator to create initial population. 47 | // RandomCreator will fill initial population with individuals with random chromosomes in a 48 | // given interval, 49 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 50 | 51 | // Make a trait object for the pairing. 52 | // Pairing is algorithm of selection individuals for crossbreeding. 53 | 54 | // Select random individuals from the population. 55 | // let pairing = pairing::RandomPairing::new(); 56 | 57 | // Tournament method. 58 | let families_count = population_size / 2; 59 | let rounds_count = 5; 60 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 61 | 62 | // Crossbreeding algorithm. 63 | // Make a Cross trait object. The bitwise crossing for float genes. 64 | let single_cross = cross::FloatCrossExp::new(); 65 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 66 | 67 | // Make a Mutation trait object. 68 | // Use bitwise mutation (change random bits with given probability). 69 | let mutation_probability = 15.0; 70 | let mutation_gene_count = 3; 71 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 72 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 73 | 74 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 75 | let pre_births: Vec>> = vec![Box::new( 76 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 77 | )]; 78 | 79 | // Stop checker. Stop criterion for genetic algorithm. 80 | // let change_max_iterations = 200; 81 | // let change_delta = 1e-7; 82 | // let stop_checker = stopchecker::GoalNotChange::new(change_max_iterations, change_delta); 83 | // let stop_checker = stopchecker::MaxIterations::new(500); 84 | 85 | // Stop algorithm if the value of goal function will become less of 1e-4 or 86 | // after 3000 generations (iterations). 87 | let stop_checker = stopchecker::CompositeAny::new(vec![ 88 | Box::new(stopchecker::Threshold::new(1e-4)), 89 | // Box::new(stopchecker::GoalNotChange::new( 90 | // change_max_iterations, 91 | // change_delta, 92 | // )), 93 | Box::new(stopchecker::MaxIterations::new(3000)), 94 | ]); 95 | 96 | // Make a trait object for selection. Selection is killing the worst individuals. 97 | // Kill all individuals if the value of goal function is NaN or Inf. 98 | // Kill the worst individuals to population size remained unchanged. 99 | let selections: Vec>> = vec![ 100 | Box::new(selection::KillFitnessNaN::new()), 101 | Box::new(selection::LimitPopulation::new(population_size)), 102 | ]; 103 | 104 | // Make a loggers trait objects 105 | // let mut stdout_verbose = io::stdout(); 106 | let mut stdout_result = io::stdout(); 107 | let mut stdout_time = io::stdout(); 108 | 109 | let loggers: Vec>> = vec![ 110 | // Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 111 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 112 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 113 | ]; 114 | 115 | // Construct main optimizer struct 116 | let mut optimizer = genetic::GeneticOptimizer::new( 117 | Box::new(goal), 118 | Box::new(stop_checker), 119 | Box::new(creator), 120 | Box::new(pairing), 121 | Box::new(cross), 122 | Box::new(mutation), 123 | selections, 124 | pre_births, 125 | ); 126 | optimizer.set_loggers(loggers); 127 | 128 | // Run genetic algorithm 129 | optimizer.find_min(); 130 | } 131 | -------------------------------------------------------------------------------- /optlib/examples/genetic-rosenbrock-statistics.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rosenbrock function with genetic algorithm. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (1.0, 1.0, ...) for any xi lying in [-500.0; 500.0]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::fs::File; 15 | use std::io; 16 | use std::sync::mpsc; 17 | use std::thread; 18 | 19 | use optlib::genetic::{ 20 | self, creation, cross, mutation, pairing, pre_birth, selection, GeneticOptimizer, 21 | }; 22 | use optlib::tools::statistics::{ 23 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 24 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 25 | }; 26 | use optlib::tools::{logging, statistics, stopchecker}; 27 | use optlib::{Goal, GoalFromFunction, Optimizer}; 28 | use optlib_testfunc; 29 | 30 | /// Gene type 31 | type Gene = f32; 32 | 33 | /// Chromosomes type 34 | type Chromosomes = Vec; 35 | 36 | fn create_optimizer<'a>( 37 | chromo_count: usize, 38 | goal: Box + 'a>, 39 | ) -> GeneticOptimizer<'a, Chromosomes> { 40 | // General parameters 41 | 42 | // Search space. Any xi lies in [-500.0; 500.0] 43 | let minval: Gene = -2.0; 44 | let maxval: Gene = 2.0; 45 | 46 | // Count individuals in initial population 47 | let population_size = 700; 48 | 49 | let intervals = vec![(minval, maxval); chromo_count]; 50 | 51 | // Make the creator to create initial population. 52 | // RandomCreator will fill initial population with individuals with random chromosomes in a 53 | // given interval, 54 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 55 | 56 | // Make a trait object for the pairing. 57 | // Pairing is algorithm of selection individuals for crossbreeding. 58 | 59 | // Tournament method. 60 | let pairing = pairing::RandomPairing::new(); 61 | 62 | // Crossbreeding algorithm. 63 | // Make a Cross trait object. The bitwise crossing for float genes. 64 | let single_cross = cross::FloatCrossExp::new(); 65 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 66 | 67 | // Make a Mutation trait object. 68 | // Use bitwise mutation (change random bits with given probability). 69 | let mutation_probability = 80.0; 70 | let mutation_gene_count = 3; 71 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 72 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 73 | 74 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 75 | let pre_births: Vec>> = vec![Box::new( 76 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 77 | )]; 78 | 79 | // Stop checker. Stop criterion for genetic algorithm. 80 | // Stop algorithm after 3000 generation (iteration). 81 | let change_max_iterations = 2000; 82 | let change_delta = 1e-7; 83 | let stop_checker = stopchecker::CompositeAny::new(vec![ 84 | Box::new(stopchecker::Threshold::new(1e-6)), 85 | Box::new(stopchecker::GoalNotChange::new( 86 | change_max_iterations, 87 | change_delta, 88 | )), 89 | ]); 90 | 91 | // Make a trait object for selection. Selection is killing the worst individuals. 92 | // Kill all individuals if the value of goal function is NaN or Inf. 93 | // Kill the worst individuals to population size remained unchanged. 94 | let selections: Vec>> = vec![ 95 | Box::new(selection::KillFitnessNaN::new()), 96 | Box::new(selection::LimitPopulation::new(population_size)), 97 | ]; 98 | 99 | // Construct main optimizer struct 100 | let optimizer = genetic::GeneticOptimizer::new( 101 | goal, 102 | Box::new(stop_checker), 103 | Box::new(creator), 104 | Box::new(pairing), 105 | Box::new(cross), 106 | Box::new(mutation), 107 | selections, 108 | pre_births, 109 | ); 110 | 111 | optimizer 112 | } 113 | 114 | fn print_convergence_statistics( 115 | mut writer: &mut dyn io::Write, 116 | stat: &statistics::Statistics, 117 | ) { 118 | let average_convergence = stat.get_convergence().get_average_convergence(); 119 | for n in 0..average_convergence.len() { 120 | if let Some(goal_value) = average_convergence[n] { 121 | writeln!( 122 | &mut writer, 123 | "{n:<8}{value:15.10e}", 124 | n = n, 125 | value = goal_value 126 | ) 127 | .unwrap(); 128 | } 129 | } 130 | } 131 | 132 | fn print_solution(mut writer: &mut dyn io::Write, stat: &statistics::Statistics) { 133 | let run_count = stat.get_run_count(); 134 | 135 | // Print solutions for every running 136 | let results = stat.get_results(); 137 | for n in 0..run_count { 138 | if let Some((solution, goal)) = &results[n] { 139 | let mut result_str = String::new(); 140 | result_str = result_str + &format!("{:<8}", n); 141 | 142 | for x in solution { 143 | result_str = result_str + &format!(" {:<20.10}", x); 144 | } 145 | result_str = result_str + &format!(" {:20.10}", goal); 146 | 147 | writeln!(&mut writer, "{}", result_str).unwrap(); 148 | } else { 149 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 150 | } 151 | } 152 | } 153 | 154 | fn print_statistics( 155 | stat: &statistics::Statistics, 156 | call_count: &CallCountData, 157 | chromo_count: usize, 158 | ) { 159 | let valid_answer = vec![1.0; chromo_count]; 160 | let delta = vec![1e-2; chromo_count]; 161 | 162 | let success_rate_answer = stat 163 | .get_results() 164 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 165 | .unwrap(); 166 | let average_goal = stat.get_results().get_average_goal().unwrap(); 167 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 168 | 169 | println!("Run count{:15}", stat.get_run_count()); 170 | println!("Success rate:{:15.5}", success_rate_answer); 171 | println!("Average goal:{:15.5}", average_goal); 172 | println!( 173 | "Standard deviation for goal:{:15.5}", 174 | standard_deviation_goal 175 | ); 176 | println!( 177 | "Average goal function call count:{:15.5}", 178 | call_count.get_average_call_count().unwrap() 179 | ); 180 | } 181 | 182 | fn main() { 183 | let cpu = num_cpus::get(); 184 | let dimension = 3; 185 | 186 | // Running count per CPU 187 | let run_count = 100 / cpu; 188 | 189 | println!("CPUs:{:15}", cpu); 190 | println!("Run count per CPU:{:8}", run_count); 191 | print!("Run optimizations... "); 192 | 193 | // Statistics from all runnings 194 | let mut full_stat = statistics::Statistics::new(); 195 | let mut full_call_count = CallCountData::new(); 196 | 197 | let (tx, rx) = mpsc::channel(); 198 | 199 | for _ in 0..cpu { 200 | let current_tx = mpsc::Sender::clone(&tx); 201 | 202 | thread::spawn(move || { 203 | let mut local_full_stat = statistics::Statistics::new(); 204 | let mut local_full_call_count = CallCountData::new(); 205 | 206 | for _ in 0..run_count { 207 | // Statistics from single run 208 | let mut statistics_data = statistics::Statistics::new(); 209 | let mut call_count = CallCountData::new(); 210 | { 211 | // Make a trait object for goal function 212 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::rosenbrock); 213 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 214 | 215 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 216 | 217 | // Add logger to collect statistics 218 | let stat_logger = 219 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 220 | let loggers: Vec>> = vec![stat_logger]; 221 | optimizer.set_loggers(loggers); 222 | 223 | // Run optimization 224 | optimizer.find_min(); 225 | } 226 | 227 | // Add current running statistics to full statistics 228 | local_full_stat.unite(statistics_data); 229 | local_full_call_count.unite(call_count); 230 | } 231 | current_tx 232 | .send((local_full_stat, local_full_call_count)) 233 | .unwrap(); 234 | }); 235 | } 236 | 237 | // Collect data from threads 238 | for _ in 0..cpu { 239 | let (statistics_data, call_count) = rx.recv().unwrap(); 240 | full_stat.unite(statistics_data); 241 | full_call_count.unite(call_count); 242 | } 243 | 244 | println!("OK"); 245 | 246 | // Print out statistics 247 | let result_stat_fname = "result_stat.txt"; 248 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 249 | 250 | let convergence_stat_fname = "convergence_stat.txt"; 251 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 252 | print_solution(&mut result_stat_file, &full_stat); 253 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 254 | print_statistics(&full_stat, &full_call_count, dimension); 255 | } 256 | -------------------------------------------------------------------------------- /optlib/examples/genetic-rosenbrock.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rosenbrock function. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (1.0, 1.0, ...) for any xi. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::io; 15 | 16 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 17 | use optlib::tools::logging; 18 | use optlib::tools::stopchecker; 19 | use optlib::{GoalFromFunction, Optimizer}; 20 | use optlib_testfunc; 21 | 22 | /// Gene type 23 | type Gene = f32; 24 | 25 | /// Chromosomes type 26 | type Chromosomes = Vec; 27 | 28 | fn main() { 29 | // General parameters 30 | 31 | // Search space 32 | let minval: Gene = -2.0_f32; 33 | let maxval: Gene = 2.0_f32; 34 | 35 | // Count individuals in initial population 36 | let population_size = 600; 37 | 38 | // Count of xi in the chromosomes 39 | let chromo_count = 3; 40 | 41 | let intervals = vec![(minval, maxval); chromo_count]; 42 | 43 | // Make a trait object for goal function (Schwefel function) 44 | let goal = GoalFromFunction::new(optlib_testfunc::rosenbrock); 45 | 46 | // Make the creator to create initial population. 47 | // RandomCreator will fill initial population with individuals with random chromosomes in a 48 | // given interval, 49 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 50 | 51 | // Make a trait object for the pairing. 52 | // Pairing is algorithm of selection individuals for crossbreeding. 53 | 54 | // Select random individuals from the population. 55 | // let pairing = pairing::RandomPairing::new(); 56 | 57 | // Tournament method. 58 | let families_count = population_size / 2; 59 | let rounds_count = 5; 60 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 61 | 62 | // Crossbreeding algorithm. 63 | // Make a Cross trait object. The bitwise crossing for float genes. 64 | let single_cross = cross::FloatCrossExp::new(); 65 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 66 | 67 | // Make a Mutation trait object. 68 | // Use bitwise mutation (change random bits with given probability). 69 | let mutation_probability = 85.0; 70 | let mutation_gene_count = 3; 71 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 72 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 73 | 74 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 75 | let pre_births: Vec>> = vec![Box::new( 76 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 77 | )]; 78 | 79 | // Stop checker. Stop criterion for genetic algorithm. 80 | let change_max_iterations = 3000; 81 | let change_delta = 1e-9; 82 | // let stop_checker = stopchecker::GoalNotChange::new(change_max_iterations, change_delta); 83 | // let stop_checker = stopchecker::MaxIterations::new(500); 84 | 85 | // Stop algorithm if the value of goal function will become less of 1e-4 or 86 | // after 3000 generations (iterations). 87 | let stop_checker = stopchecker::CompositeAny::new(vec![ 88 | // Box::new(stopchecker::Threshold::new(1e-6)), 89 | Box::new(stopchecker::GoalNotChange::new( 90 | change_max_iterations, 91 | change_delta, 92 | )), 93 | // Box::new(stopchecker::MaxIterations::new(20000)), 94 | ]); 95 | 96 | // Make a trait object for selection. Selection is killing the worst individuals. 97 | // Kill all individuals if the value of goal function is NaN or Inf. 98 | // Kill the worst individuals to population size remained unchanged. 99 | let selections: Vec>> = vec![ 100 | Box::new(selection::KillFitnessNaN::new()), 101 | Box::new(selection::LimitPopulation::new(population_size)), 102 | ]; 103 | 104 | // Make a loggers trait objects 105 | let mut stdout_verbose = io::stdout(); 106 | let mut stdout_result = io::stdout(); 107 | let mut stdout_time = io::stdout(); 108 | 109 | let loggers: Vec>> = vec![ 110 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 111 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 112 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 113 | ]; 114 | 115 | // Construct main optimizer struct 116 | let mut optimizer = genetic::GeneticOptimizer::new( 117 | Box::new(goal), 118 | Box::new(stop_checker), 119 | Box::new(creator), 120 | Box::new(pairing), 121 | Box::new(cross), 122 | Box::new(mutation), 123 | selections, 124 | pre_births, 125 | ); 126 | optimizer.set_loggers(loggers); 127 | 128 | // Run genetic algorithm 129 | optimizer.find_min(); 130 | } 131 | -------------------------------------------------------------------------------- /optlib/examples/genetic-schwefel-iterative.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with genetic algorithm. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (420.9687, 420.9687, ...) for any xi lying in [-500.0; 500.0]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::io; 15 | 16 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 17 | use optlib::tools::logging; 18 | use optlib::tools::stopchecker; 19 | use optlib::{GoalFromFunction, IterativeOptimizer, Optimizer}; 20 | use optlib_testfunc; 21 | 22 | /// Gene type 23 | type Gene = f32; 24 | 25 | /// Chromosomes type 26 | type Chromosomes = Vec; 27 | 28 | fn main() { 29 | // General parameters 30 | 31 | // Search space. Any xi lies in [-500.0; 500.0] 32 | let minval: Gene = -500.0; 33 | let maxval: Gene = 500.0; 34 | 35 | // Count individuals in initial population 36 | let population_size = 500; 37 | 38 | // Count of xi in the chromosomes 39 | let chromo_count = 15; 40 | 41 | let intervals = vec![(minval, maxval); chromo_count]; 42 | 43 | // Make a trait object for goal function (Schwefel function) 44 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 45 | 46 | // Make the creator to create initial population. 47 | // RandomCreator will fill initial population with individuals with random chromosomes in a 48 | // given interval, 49 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 50 | 51 | // Make a trait object for the pairing. 52 | // Tournament method. 53 | let families_count = population_size / 2; 54 | let rounds_count = 5; 55 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 56 | 57 | // Crossbreeding algorithm. 58 | // Make a Cross trait object. The bitwise crossing for float genes. 59 | let single_cross = cross::FloatCrossExp::new(); 60 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 61 | 62 | // Make a Mutation trait object. 63 | // Use bitwise mutation (change random bits with given probability). 64 | let mutation_probability = 15.0; 65 | let mutation_gene_count = 3; 66 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 67 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 68 | 69 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 70 | let pre_births: Vec>> = vec![Box::new( 71 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 72 | )]; 73 | 74 | // Stop checker. Stop criterion for genetic algorithm. 75 | let stop_checker_1 = Box::new(stopchecker::MaxIterations::new(100)); 76 | let stop_checker_2 = Box::new(stopchecker::MaxIterations::new(500)); 77 | 78 | // Make a trait object for selection. Selection is killing the worst individuals. 79 | // Kill all individuals if the value of goal function is NaN or Inf. 80 | // Kill the worst individuals to population size remained unchanged. 81 | let selections: Vec>> = vec![ 82 | Box::new(selection::KillFitnessNaN::new()), 83 | Box::new(selection::LimitPopulation::new(population_size)), 84 | ]; 85 | 86 | // Make a loggers trait objects 87 | // let mut stdout_verbose = io::stdout(); 88 | let mut stdout_result = io::stdout(); 89 | let mut stdout_time = io::stdout(); 90 | 91 | let loggers: Vec>> = vec![ 92 | // Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 93 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 94 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 95 | ]; 96 | 97 | // Construct main optimizer struct 98 | let mut optimizer = genetic::GeneticOptimizer::new( 99 | Box::new(goal), 100 | stop_checker_1, 101 | Box::new(creator), 102 | Box::new(pairing), 103 | Box::new(cross), 104 | Box::new(mutation), 105 | selections, 106 | pre_births, 107 | ); 108 | optimizer.set_loggers(loggers); 109 | 110 | // Run genetic algorithm 111 | optimizer.find_min(); 112 | 113 | optimizer.set_stop_checker(stop_checker_2); 114 | optimizer.next_iterations(); 115 | } 116 | -------------------------------------------------------------------------------- /optlib/examples/genetic-schwefel-statistics.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with genetic algorithm. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (420.9687, 420.9687, ...) for any xi lying in [-500.0; 500.0]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::fs::File; 15 | use std::io; 16 | use std::sync::mpsc; 17 | use std::thread; 18 | 19 | use optlib::genetic::{ 20 | self, creation, cross, mutation, pairing, pre_birth, selection, GeneticOptimizer, 21 | }; 22 | use optlib::tools::statistics::{ 23 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 24 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 25 | }; 26 | use optlib::tools::{logging, statistics, stopchecker}; 27 | use optlib::{Goal, GoalFromFunction, Optimizer}; 28 | use optlib_testfunc; 29 | 30 | /// Gene type 31 | type Gene = f32; 32 | 33 | /// Chromosomes type 34 | type Chromosomes = Vec; 35 | 36 | fn create_optimizer<'a>( 37 | chromo_count: usize, 38 | goal: Box + 'a>, 39 | ) -> GeneticOptimizer<'a, Chromosomes> { 40 | // General parameters 41 | 42 | // Search space. Any xi lies in [-500.0; 500.0] 43 | let minval: Gene = -500.0; 44 | let maxval: Gene = 500.0; 45 | 46 | // Count individuals in initial population 47 | let population_size = 100; 48 | 49 | let intervals = vec![(minval, maxval); chromo_count]; 50 | 51 | // Make the creator to create initial population. 52 | // RandomCreator will fill initial population with individuals with random chromosomes in a 53 | // given interval, 54 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 55 | 56 | // Make a trait object for the pairing. 57 | // Pairing is algorithm of selection individuals for crossbreeding. 58 | 59 | // Tournament method. 60 | let families_count = population_size / 2; 61 | let rounds_count = 5; 62 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 63 | 64 | // Crossbreeding algorithm. 65 | // Make a Cross trait object. The bitwise crossing for float genes. 66 | let single_cross = cross::FloatCrossExp::new(); 67 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 68 | 69 | // Make a Mutation trait object. 70 | // Use bitwise mutation (change random bits with given probability). 71 | let mutation_probability = 10.0; 72 | let mutation_gene_count = 3; 73 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 74 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 75 | 76 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 77 | let pre_births: Vec>> = vec![Box::new( 78 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 79 | )]; 80 | 81 | // Stop checker. Stop criterion for genetic algorithm. 82 | // Stop algorithm after 3000 generation (iteration). 83 | // let stop_checker = 84 | // stopchecker::CompositeAny::new(vec![Box::new(stopchecker::MaxIterations::new(3000))]); 85 | 86 | // let change_max_iterations = 150; 87 | // let change_delta = 1e-7; 88 | let stop_checker = stopchecker::CompositeAny::new(vec![ 89 | // Box::new(stopchecker::Threshold::new(1e-6)), 90 | // Box::new(stopchecker::GoalNotChange::new( 91 | // change_max_iterations, 92 | // change_delta, 93 | // )), 94 | Box::new(stopchecker::MaxIterations::new(3000)), 95 | ]); 96 | 97 | // Make a trait object for selection. Selection is killing the worst individuals. 98 | // Kill all individuals if the value of goal function is NaN or Inf. 99 | // Kill the worst individuals to population size remained unchanged. 100 | let selections: Vec>> = vec![ 101 | Box::new(selection::KillFitnessNaN::new()), 102 | Box::new(selection::LimitPopulation::new(population_size)), 103 | ]; 104 | 105 | // Construct main optimizer struct 106 | let optimizer = genetic::GeneticOptimizer::new( 107 | goal, 108 | Box::new(stop_checker), 109 | Box::new(creator), 110 | Box::new(pairing), 111 | Box::new(cross), 112 | Box::new(mutation), 113 | selections, 114 | pre_births, 115 | ); 116 | 117 | optimizer 118 | } 119 | 120 | fn print_convergence_statistics( 121 | mut writer: &mut dyn io::Write, 122 | stat: &statistics::Statistics, 123 | ) { 124 | let average_convergence = stat.get_convergence().get_average_convergence(); 125 | for n in 0..average_convergence.len() { 126 | if let Some(goal_value) = average_convergence[n] { 127 | writeln!( 128 | &mut writer, 129 | "{n:<8}{value:15.10e}", 130 | n = n, 131 | value = goal_value 132 | ) 133 | .unwrap(); 134 | } 135 | } 136 | } 137 | 138 | fn print_solution(mut writer: &mut dyn io::Write, stat: &statistics::Statistics) { 139 | let run_count = stat.get_run_count(); 140 | 141 | // Print solutions for every running 142 | let results = stat.get_results(); 143 | for n in 0..run_count { 144 | if let Some((solution, goal)) = &results[n] { 145 | let mut result_str = String::new(); 146 | result_str = result_str + &format!("{:<8}", n); 147 | 148 | for x in solution { 149 | result_str = result_str + &format!(" {:<20.10}", x); 150 | } 151 | result_str = result_str + &format!(" {:20.10}", goal); 152 | 153 | writeln!(&mut writer, "{}", result_str).unwrap(); 154 | } else { 155 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 156 | } 157 | } 158 | } 159 | 160 | fn print_statistics( 161 | stat: &statistics::Statistics, 162 | call_count: &CallCountData, 163 | chromo_count: usize, 164 | ) { 165 | let valid_answer = vec![420.9687; chromo_count]; 166 | let delta = vec![1.0; chromo_count]; 167 | 168 | let success_rate_answer = stat 169 | .get_results() 170 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 171 | .unwrap(); 172 | let average_goal = stat.get_results().get_average_goal().unwrap(); 173 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 174 | 175 | println!("Run count{:15}", stat.get_run_count()); 176 | println!("Success rate:{:15.5}", success_rate_answer); 177 | println!("Average goal:{:15.5}", average_goal); 178 | println!( 179 | "Standard deviation for goal:{:15.5}", 180 | standard_deviation_goal 181 | ); 182 | println!( 183 | "Average goal function call count:{:15.5}", 184 | call_count.get_average_call_count().unwrap() 185 | ); 186 | } 187 | 188 | fn main() { 189 | let cpu = num_cpus::get(); 190 | let dimension = 3; 191 | 192 | // Running count per CPU 193 | let run_count = 1000 / cpu; 194 | 195 | println!("CPUs:{:15}", cpu); 196 | println!("Run count per CPU:{:8}", run_count); 197 | print!("Run optimizations... "); 198 | 199 | // Statistics from all runnings 200 | let mut full_stat = statistics::Statistics::new(); 201 | let mut full_call_count = CallCountData::new(); 202 | 203 | let (tx, rx) = mpsc::channel(); 204 | 205 | for _ in 0..cpu { 206 | let current_tx = mpsc::Sender::clone(&tx); 207 | 208 | thread::spawn(move || { 209 | let mut local_full_stat = statistics::Statistics::new(); 210 | let mut local_full_call_count = CallCountData::new(); 211 | 212 | for _ in 0..run_count { 213 | // Statistics from single run 214 | let mut statistics_data = statistics::Statistics::new(); 215 | let mut call_count = CallCountData::new(); 216 | { 217 | // Make a trait object for goal function 218 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::schwefel); 219 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 220 | 221 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 222 | 223 | // Add logger to collect statistics 224 | let stat_logger = 225 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 226 | let loggers: Vec>> = vec![stat_logger]; 227 | optimizer.set_loggers(loggers); 228 | 229 | // Run optimization 230 | optimizer.find_min(); 231 | } 232 | 233 | // Add current running statistics to full statistics 234 | local_full_stat.unite(statistics_data); 235 | local_full_call_count.unite(call_count); 236 | } 237 | current_tx 238 | .send((local_full_stat, local_full_call_count)) 239 | .unwrap(); 240 | }); 241 | } 242 | 243 | // Collect data from threads 244 | for _ in 0..cpu { 245 | let (statistics_data, call_count) = rx.recv().unwrap(); 246 | full_stat.unite(statistics_data); 247 | full_call_count.unite(call_count); 248 | } 249 | 250 | println!("OK"); 251 | 252 | // Print out statistics 253 | let result_stat_fname = "result_stat.txt"; 254 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 255 | 256 | let convergence_stat_fname = "convergence_stat.txt"; 257 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 258 | print_solution(&mut result_stat_file, &full_stat); 259 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 260 | print_statistics(&full_stat, &full_call_count, dimension); 261 | } 262 | -------------------------------------------------------------------------------- /optlib/examples/genetic-schwefel.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with genetic algorithm. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (420.9687, 420.9687, ...) for any xi lying in [-500.0; 500.0]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::io; 15 | 16 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 17 | use optlib::tools::logging; 18 | use optlib::tools::stopchecker; 19 | use optlib::{GoalFromFunction, Optimizer}; 20 | use optlib_testfunc; 21 | 22 | /// Gene type 23 | type Gene = f32; 24 | 25 | /// Chromosomes type 26 | type Chromosomes = Vec; 27 | 28 | fn main() { 29 | // General parameters 30 | 31 | // Search space. Any xi lies in [-500.0; 500.0] 32 | let minval: Gene = -500.0; 33 | let maxval: Gene = 500.0; 34 | 35 | // Count individuals in initial population 36 | let population_size = 500; 37 | 38 | // Count of xi in the chromosomes 39 | let chromo_count = 15; 40 | 41 | let intervals = vec![(minval, maxval); chromo_count]; 42 | 43 | // Make a trait object for goal function (Schwefel function) 44 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 45 | 46 | // Make the creator to create initial population. 47 | // RandomCreator will fill initial population with individuals with random chromosomes in a 48 | // given interval, 49 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 50 | 51 | // Make a trait object for the pairing. 52 | // Pairing is algorithm of selection individuals for crossbreeding. 53 | 54 | // Select random individuals from the population. 55 | // let pairing = pairing::RandomPairing::new(); 56 | 57 | // Tournament method. 58 | let families_count = population_size / 2; 59 | let rounds_count = 5; 60 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 61 | 62 | // Crossbreeding algorithm. 63 | // Make a Cross trait object. The bitwise crossing for float genes. 64 | let single_cross = cross::FloatCrossExp::new(); 65 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 66 | 67 | // Make a Mutation trait object. 68 | // Use bitwise mutation (change random bits with given probability). 69 | let mutation_probability = 15.0; 70 | let mutation_gene_count = 3; 71 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 72 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 73 | 74 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 75 | let pre_births: Vec>> = vec![Box::new( 76 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 77 | )]; 78 | 79 | // Stop checker. Stop criterion for genetic algorithm. 80 | // let change_max_iterations = 200; 81 | // let change_delta = 1e-7; 82 | // let stop_checker = stopchecker::GoalNotChange::new(change_max_iterations, change_delta); 83 | // let stop_checker = stopchecker::MaxIterations::new(500); 84 | 85 | // Stop algorithm if the value of goal function will become less of 1e-4 or 86 | // after 3000 generations (iterations). 87 | let stop_checker = stopchecker::CompositeAny::new(vec![ 88 | Box::new(stopchecker::Threshold::new(1e-4)), 89 | // Box::new(stopchecker::GoalNotChange::new( 90 | // change_max_iterations, 91 | // change_delta, 92 | // )), 93 | Box::new(stopchecker::MaxIterations::new(3000)), 94 | ]); 95 | 96 | // Make a trait object for selection. Selection is killing the worst individuals. 97 | // Kill all individuals if the value of goal function is NaN or Inf. 98 | // Kill the worst individuals to population size remained unchanged. 99 | let selections: Vec>> = vec![ 100 | Box::new(selection::KillFitnessNaN::new()), 101 | Box::new(selection::LimitPopulation::new(population_size)), 102 | ]; 103 | 104 | // Make a loggers trait objects 105 | // let mut stdout_verbose = io::stdout(); 106 | let mut stdout_result = io::stdout(); 107 | let mut stdout_time = io::stdout(); 108 | 109 | let loggers: Vec>> = vec![ 110 | // Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 111 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 112 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 113 | ]; 114 | 115 | // Construct main optimizer struct 116 | let mut optimizer = genetic::GeneticOptimizer::new( 117 | Box::new(goal), 118 | Box::new(stop_checker), 119 | Box::new(creator), 120 | Box::new(pairing), 121 | Box::new(cross), 122 | Box::new(mutation), 123 | selections, 124 | pre_births, 125 | ); 126 | optimizer.set_loggers(loggers); 127 | 128 | // Run genetic algorithm 129 | optimizer.find_min(); 130 | } 131 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-paraboloid.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::{ 4 | particleswarm::{ 5 | self, initializing, postmove, postvelocitycalc, velocitycalc, PostMove, PostVelocityCalc, 6 | }, 7 | tools::{logging, stopchecker}, 8 | GoalFromFunction, Optimizer, 9 | }; 10 | 11 | use optlib_testfunc; 12 | 13 | type Coordinate = f32; 14 | 15 | fn main() { 16 | // General parameters 17 | let minval: Coordinate = -100.0; 18 | let maxval: Coordinate = 100.0; 19 | let particles_count = 80; 20 | let dimension = 5; 21 | let intervals = vec![(minval, maxval); dimension]; 22 | // let phi_personal = 3e-6; 23 | // let phi_global = 1e-3; 24 | let phi_personal = 2.0; 25 | let phi_global = 6.0; 26 | let k = 0.2; 27 | 28 | // Goal function 29 | let goal = GoalFromFunction::new(optlib_testfunc::paraboloid); 30 | 31 | // Particles initializers 32 | let coord_initializer = 33 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 34 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 35 | 36 | // PostMove 37 | let post_moves: Vec>> = 38 | vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 39 | 40 | // Velocity calculator 41 | // let velocity_calculator = velocitycalc::ClassicVelocityCalculator::new(phi_personal, phi_global); 42 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 43 | 44 | // let max_velocity = vec![20.0_f32; dimension]; 45 | // let post_velocity_calc: Vec>> = 46 | // vec![Box::new(postvelocitycalc::MaxVelocityDimensions::new(max_velocity))]; 47 | let max_velocity = 10.0; 48 | let post_velocity_calc: Vec>> = 49 | vec![Box::new(postvelocitycalc::MaxVelocityAbs::new(max_velocity))]; 50 | 51 | // Stop checker 52 | let change_max_iterations = 150; 53 | let change_delta = 1e-7; 54 | let stop_checker = stopchecker::CompositeAny::new(vec![ 55 | Box::new(stopchecker::Threshold::new(1e-6)), 56 | Box::new(stopchecker::GoalNotChange::new( 57 | change_max_iterations, 58 | change_delta, 59 | )), 60 | Box::new(stopchecker::MaxIterations::new(3000)), 61 | ]); 62 | 63 | // Logger 64 | let mut stdout_verbose = io::stdout(); 65 | let mut stdout_result = io::stdout(); 66 | let mut stdout_time = io::stdout(); 67 | 68 | let loggers: Vec>>> = vec![ 69 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 70 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 71 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 72 | ]; 73 | 74 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 75 | Box::new(goal), 76 | Box::new(stop_checker), 77 | Box::new(coord_initializer), 78 | Box::new(velocity_initializer), 79 | Box::new(velocity_calculator), 80 | ); 81 | optimizer.set_loggers(loggers); 82 | optimizer.set_post_moves(post_moves); 83 | optimizer.set_post_velocity_calc(post_velocity_calc); 84 | 85 | optimizer.find_min(); 86 | } 87 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-rastrigin-statistics-inertia.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with particle sqwarm algorithm. 2 | use std::fs::File; 3 | use std::io; 4 | use std::sync::mpsc; 5 | use std::thread; 6 | 7 | use num_cpus; 8 | 9 | use optlib::particleswarm::{ 10 | self, initializing, postmove, postvelocitycalc, velocitycalc, ParticleSwarmOptimizer, PostMove, 11 | PostVelocityCalc, 12 | }; 13 | use optlib::tools::statistics::{ 14 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 15 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 16 | }; 17 | use optlib::tools::{logging, statistics, stopchecker}; 18 | use optlib::{Goal, GoalFromFunction, Optimizer}; 19 | use optlib_testfunc; 20 | 21 | /// Coordinates type 22 | type Coordinate = f32; 23 | 24 | fn create_optimizer<'a>( 25 | dimension: usize, 26 | goal: Box> + 'a>, 27 | ) -> ParticleSwarmOptimizer<'a, Coordinate> { 28 | // General parameters 29 | let minval: Coordinate = -5.12; 30 | let maxval: Coordinate = 5.12; 31 | let particles_count = 50; 32 | let intervals = vec![(minval, maxval); dimension]; 33 | 34 | let phi_personal = 2.2; 35 | let phi_global = 0.6; 36 | 37 | // Particles initializers 38 | let coord_initializer = 39 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 40 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 41 | 42 | let max_velocity = 700.0; 43 | let post_velocity_calc: Vec>> = 44 | vec![Box::new(postvelocitycalc::MaxVelocityAbs::new(max_velocity))]; 45 | 46 | // PostMove 47 | let post_moves: Vec>> = 48 | vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 49 | 50 | let inertia = Box::new(velocitycalc::ConstInertia::new(0.85)); 51 | // let inertia = Box::new(velocitycalc::LinearInertia::new(0.2, 0.9, 400)); 52 | 53 | // Velocity calculator 54 | let velocity_calculator = velocitycalc::InertiaVelocityCalculator::new( 55 | phi_personal, 56 | phi_global, 57 | inertia 58 | ); 59 | 60 | // Stop checker 61 | let change_max_iterations = 50; 62 | let change_delta = 1e-8; 63 | let stop_checker = stopchecker::CompositeAny::new(vec![ 64 | Box::new(stopchecker::Threshold::new(1e-6)), 65 | Box::new(stopchecker::GoalNotChange::new( 66 | change_max_iterations, 67 | change_delta, 68 | )), 69 | Box::new(stopchecker::MaxIterations::new(400)), 70 | ]); 71 | 72 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 73 | goal, 74 | Box::new(stop_checker), 75 | Box::new(coord_initializer), 76 | Box::new(velocity_initializer), 77 | Box::new(velocity_calculator), 78 | ); 79 | optimizer.set_post_moves(post_moves); 80 | optimizer.set_post_velocity_calc(post_velocity_calc); 81 | optimizer 82 | } 83 | 84 | fn print_convergence_statistics( 85 | mut writer: &mut dyn io::Write, 86 | stat: &statistics::Statistics>, 87 | ) { 88 | let average_convergence = stat.get_convergence().get_average_convergence(); 89 | for n in 0..average_convergence.len() { 90 | if let Some(goal_value) = average_convergence[n] { 91 | writeln!( 92 | &mut writer, 93 | "{n:<8}{value:15.10e}", 94 | n = n, 95 | value = goal_value 96 | ) 97 | .unwrap(); 98 | } 99 | } 100 | } 101 | 102 | fn print_solution( 103 | mut writer: &mut dyn io::Write, 104 | stat: &statistics::Statistics>, 105 | ) { 106 | let run_count = stat.get_run_count(); 107 | 108 | // Print solutions for every running 109 | let results = stat.get_results(); 110 | for n in 0..run_count { 111 | if let Some((solution, goal)) = &results[n] { 112 | let mut result_str = String::new(); 113 | result_str = result_str + &format!("{:<8}", n); 114 | 115 | for x in solution { 116 | result_str = result_str + &format!(" {:<20.10}", x); 117 | } 118 | result_str = result_str + &format!(" {:20.10}", goal); 119 | 120 | writeln!(&mut writer, "{}", result_str).unwrap(); 121 | } else { 122 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 123 | } 124 | } 125 | } 126 | 127 | fn print_statistics( 128 | stat: &statistics::Statistics>, 129 | call_count: &CallCountData, 130 | dimension: usize, 131 | ) { 132 | let valid_answer = vec![0.0; dimension]; 133 | let delta = vec![0.1; dimension]; 134 | 135 | let success_rate_answer = stat 136 | .get_results() 137 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 138 | .unwrap(); 139 | let average_goal = stat.get_results().get_average_goal().unwrap(); 140 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 141 | 142 | println!("Run count{:15}", stat.get_run_count()); 143 | println!("Success rate:{:15.5}", success_rate_answer); 144 | println!("Average goal:{:15.5}", average_goal); 145 | println!( 146 | "Standard deviation for goal:{:15.5}", 147 | standard_deviation_goal 148 | ); 149 | println!( 150 | "Average goal function call count:{:15.5}", 151 | call_count.get_average_call_count().unwrap() 152 | ); 153 | } 154 | 155 | fn main() { 156 | let cpu = num_cpus::get(); 157 | let dimension = 3; 158 | 159 | // Running count per CPU 160 | let run_count = 1000 / cpu; 161 | 162 | println!("CPUs:{:15}", cpu); 163 | println!("Run count per CPU:{:8}", run_count); 164 | print!("Run optimizations... "); 165 | 166 | // Statistics from all runnings 167 | let mut full_stat = statistics::Statistics::new(); 168 | let mut full_call_count = CallCountData::new(); 169 | 170 | let (tx, rx) = mpsc::channel(); 171 | 172 | for _ in 0..cpu { 173 | let current_tx = mpsc::Sender::clone(&tx); 174 | 175 | thread::spawn(move || { 176 | let mut local_full_stat = statistics::Statistics::new(); 177 | let mut local_full_call_count = CallCountData::new(); 178 | 179 | for _ in 0..run_count { 180 | // Statistics from single run 181 | let mut statistics_data = statistics::Statistics::new(); 182 | let mut call_count = CallCountData::new(); 183 | { 184 | // Make a trait object for goal function 185 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::rastrigin); 186 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 187 | 188 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 189 | 190 | // Add logger to collect statistics 191 | let stat_logger = 192 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 193 | let loggers: Vec>>> = vec![stat_logger]; 194 | optimizer.set_loggers(loggers); 195 | 196 | // Run optimization 197 | optimizer.find_min(); 198 | } 199 | 200 | // Add current running statistics to full statistics 201 | local_full_stat.unite(statistics_data); 202 | local_full_call_count.unite(call_count); 203 | } 204 | current_tx 205 | .send((local_full_stat, local_full_call_count)) 206 | .unwrap(); 207 | }); 208 | } 209 | 210 | // Collect data from threads 211 | for _ in 0..cpu { 212 | let (statistics_data, call_count) = rx.recv().unwrap(); 213 | full_stat.unite(statistics_data); 214 | full_call_count.unite(call_count); 215 | } 216 | 217 | println!("OK"); 218 | 219 | // Print out statistics 220 | let result_stat_fname = "result_stat.txt"; 221 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 222 | 223 | let convergence_stat_fname = "convergence_stat.txt"; 224 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 225 | print_solution(&mut result_stat_file, &full_stat); 226 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 227 | print_statistics(&full_stat, &full_call_count, dimension); 228 | } 229 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-rastrigin.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::{ 4 | tools::{logging, stopchecker}, 5 | GoalFromFunction, Optimizer, 6 | particleswarm::{ 7 | self, 8 | initializing, 9 | postmove, 10 | velocitycalc, 11 | PostMove, 12 | }, 13 | }; 14 | 15 | use optlib_testfunc; 16 | 17 | type Coordinate = f32; 18 | 19 | fn main() { 20 | // General parameters 21 | let minval: Coordinate = -5.12; 22 | let maxval: Coordinate = 5.12; 23 | let particles_count = 500; 24 | let dimension = 3; 25 | let intervals = vec![(minval, maxval); dimension]; 26 | let phi_personal = 2.0; 27 | let phi_global = 5.0; 28 | let k = 0.2; 29 | 30 | // Goal function 31 | let goal = GoalFromFunction::new(optlib_testfunc::rastrigin); 32 | 33 | // Particles initializers 34 | let coord_initializer = initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 35 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 36 | 37 | // PostMove 38 | let post_moves: Vec>> = vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 39 | 40 | // Velocity calculator 41 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 42 | 43 | // Stop checker 44 | let change_max_iterations = 150; 45 | let change_delta = 1e-8; 46 | let stop_checker = stopchecker::CompositeAny::new(vec![ 47 | Box::new(stopchecker::Threshold::new(1e-6)), 48 | Box::new(stopchecker::GoalNotChange::new( 49 | change_max_iterations, 50 | change_delta, 51 | )), 52 | Box::new(stopchecker::MaxIterations::new(3000)), 53 | ]); 54 | 55 | // Logger 56 | let mut stdout_verbose = io::stdout(); 57 | let mut stdout_result = io::stdout(); 58 | let mut stdout_time = io::stdout(); 59 | 60 | let loggers: Vec>>> = vec![ 61 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 62 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 63 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 64 | ]; 65 | 66 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 67 | Box::new(goal), 68 | Box::new(stop_checker), 69 | Box::new(coord_initializer), 70 | Box::new(velocity_initializer), 71 | Box::new(velocity_calculator), 72 | ); 73 | optimizer.set_loggers(loggers); 74 | optimizer.set_post_moves(post_moves); 75 | 76 | optimizer.find_min(); 77 | } 78 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-rosenbrock-statistics.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rosenbrock function with particle swarm algorithm. 2 | use std::fs::File; 3 | use std::io; 4 | use std::sync::mpsc; 5 | use std::thread; 6 | 7 | use num_cpus; 8 | 9 | use optlib::particleswarm::{ 10 | self, initializing, postmove, postvelocitycalc, velocitycalc, ParticleSwarmOptimizer, PostMove, 11 | PostVelocityCalc, 12 | }; 13 | use optlib::tools::statistics::{ 14 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 15 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 16 | }; 17 | use optlib::tools::{logging, statistics, stopchecker}; 18 | use optlib::{Goal, GoalFromFunction, Optimizer}; 19 | use optlib_testfunc; 20 | 21 | /// Coordinates type 22 | type Coordinate = f32; 23 | 24 | fn create_optimizer<'a>( 25 | dimension: usize, 26 | goal: Box> + 'a>, 27 | ) -> ParticleSwarmOptimizer<'a, Coordinate> { 28 | // General parameters 29 | let minval: Coordinate = -2.0; 30 | let maxval: Coordinate = 2.0; 31 | let particles_count = 20; 32 | let intervals = vec![(minval, maxval); dimension]; 33 | let phi_personal = 3.4; 34 | let phi_global = 1.0; 35 | let k = 0.9; 36 | 37 | // Particles initializers 38 | let coord_initializer = 39 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 40 | let velocity_initializer = 41 | initializing::ZeroVelocityInitializer::new(dimension, particles_count); 42 | 43 | let max_velocity = 2.0; 44 | let post_velocity_calc: Vec>> = vec![Box::new( 45 | postvelocitycalc::MaxVelocityAbs::new(max_velocity), 46 | )]; 47 | 48 | // PostMove 49 | let teleport_probability = 0.0; 50 | let post_moves: Vec>> = vec![ 51 | Box::new(postmove::RandomTeleport::new( 52 | intervals.clone(), 53 | teleport_probability, 54 | )), 55 | Box::new(postmove::MoveToBoundary::new(intervals.clone())), 56 | ]; 57 | 58 | // Velocity calculator 59 | let velocity_calculator = 60 | velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 61 | 62 | // Stop checker 63 | let change_max_iterations = 500; 64 | let change_delta = 1e-10; 65 | let stop_checker = stopchecker::CompositeAny::new(vec![ 66 | Box::new(stopchecker::Threshold::new(1e-8)), 67 | Box::new(stopchecker::GoalNotChange::new( 68 | change_max_iterations, 69 | change_delta, 70 | )), 71 | // Box::new(stopchecker::MaxIterations::new(5000)), 72 | ]); 73 | 74 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 75 | goal, 76 | Box::new(stop_checker), 77 | Box::new(coord_initializer), 78 | Box::new(velocity_initializer), 79 | Box::new(velocity_calculator), 80 | ); 81 | optimizer.set_post_moves(post_moves); 82 | optimizer.set_post_velocity_calc(post_velocity_calc); 83 | optimizer 84 | } 85 | 86 | fn print_convergence_statistics( 87 | mut writer: &mut dyn io::Write, 88 | stat: &statistics::Statistics>, 89 | ) { 90 | let average_convergence = stat.get_convergence().get_average_convergence(); 91 | for n in 0..average_convergence.len() { 92 | if let Some(goal_value) = average_convergence[n] { 93 | writeln!( 94 | &mut writer, 95 | "{n:<8}{value:15.10e}", 96 | n = n, 97 | value = goal_value 98 | ) 99 | .unwrap(); 100 | } 101 | } 102 | } 103 | 104 | fn print_solution(mut writer: &mut dyn io::Write, stat: &statistics::Statistics>) { 105 | let run_count = stat.get_run_count(); 106 | 107 | // Print solutions for every running 108 | let results = stat.get_results(); 109 | for n in 0..run_count { 110 | if let Some((solution, goal)) = &results[n] { 111 | let mut result_str = String::new(); 112 | result_str = result_str + &format!("{:<8}", n); 113 | 114 | for x in solution { 115 | result_str = result_str + &format!(" {:<20.10}", x); 116 | } 117 | result_str = result_str + &format!(" {:20.10}", goal); 118 | 119 | writeln!(&mut writer, "{}", result_str).unwrap(); 120 | } else { 121 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 122 | } 123 | } 124 | } 125 | 126 | fn print_statistics( 127 | stat: &statistics::Statistics>, 128 | call_count: &CallCountData, 129 | dimension: usize, 130 | ) { 131 | let valid_answer = vec![1.0; dimension]; 132 | let delta = vec![1e-2; dimension]; 133 | 134 | let success_rate_answer = stat 135 | .get_results() 136 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 137 | .unwrap(); 138 | let average_goal = stat.get_results().get_average_goal().unwrap(); 139 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 140 | 141 | println!("Run count{:15}", stat.get_run_count()); 142 | println!("Success rate:{:15.5}", success_rate_answer); 143 | println!("Average goal:{:15.5}", average_goal); 144 | println!( 145 | "Standard deviation for goal:{:15.5}", 146 | standard_deviation_goal 147 | ); 148 | println!( 149 | "Average goal function call count:{:15.5}", 150 | call_count.get_average_call_count().unwrap() 151 | ); 152 | } 153 | 154 | fn main() { 155 | let cpu = num_cpus::get(); 156 | let dimension = 3; 157 | 158 | // Running count per CPU 159 | let run_count = 1000 / cpu; 160 | 161 | println!("CPUs:{:15}", cpu); 162 | println!("Run count per CPU:{:8}", run_count); 163 | print!("Run optimizations... "); 164 | 165 | // Statistics from all runnings 166 | let mut full_stat = statistics::Statistics::new(); 167 | let mut full_call_count = CallCountData::new(); 168 | 169 | let (tx, rx) = mpsc::channel(); 170 | 171 | for _ in 0..cpu { 172 | let current_tx = mpsc::Sender::clone(&tx); 173 | 174 | thread::spawn(move || { 175 | let mut local_full_stat = statistics::Statistics::new(); 176 | let mut local_full_call_count = CallCountData::new(); 177 | 178 | for _ in 0..run_count { 179 | // Statistics from single run 180 | let mut statistics_data = statistics::Statistics::new(); 181 | let mut call_count = CallCountData::new(); 182 | { 183 | // Make a trait object for goal function 184 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::rosenbrock); 185 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 186 | 187 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 188 | 189 | // Add logger to collect statistics 190 | let stat_logger = 191 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 192 | let loggers: Vec>>> = vec![stat_logger]; 193 | optimizer.set_loggers(loggers); 194 | 195 | // Run optimization 196 | optimizer.find_min(); 197 | } 198 | 199 | // Add current running statistics to full statistics 200 | local_full_stat.unite(statistics_data); 201 | local_full_call_count.unite(call_count); 202 | } 203 | current_tx 204 | .send((local_full_stat, local_full_call_count)) 205 | .unwrap(); 206 | }); 207 | } 208 | 209 | // Collect data from threads 210 | for _ in 0..cpu { 211 | let (statistics_data, call_count) = rx.recv().unwrap(); 212 | full_stat.unite(statistics_data); 213 | full_call_count.unite(call_count); 214 | } 215 | 216 | println!("OK"); 217 | 218 | // Print out statistics 219 | let result_stat_fname = "result_stat.txt"; 220 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 221 | 222 | let convergence_stat_fname = "convergence_stat.txt"; 223 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 224 | print_solution(&mut result_stat_file, &full_stat); 225 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 226 | print_statistics(&full_stat, &full_call_count, dimension); 227 | } 228 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-rosenbrock.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::{ 4 | tools::{logging, stopchecker}, 5 | GoalFromFunction, Optimizer, 6 | particleswarm::{ 7 | self, 8 | initializing, 9 | postmove, 10 | velocitycalc, 11 | PostMove, 12 | }, 13 | }; 14 | 15 | use optlib_testfunc; 16 | 17 | type Coordinate = f32; 18 | 19 | fn main() { 20 | // General parameters 21 | let minval: Coordinate = -2.0; 22 | let maxval: Coordinate = 2.0; 23 | let particles_count = 1000; 24 | let dimension = 3; 25 | let intervals = vec![(minval, maxval); dimension]; 26 | let phi_personal = 1.0; 27 | let phi_global = 8.0; 28 | let k = 0.2; 29 | 30 | // Goal function 31 | let goal = GoalFromFunction::new(optlib_testfunc::rosenbrock); 32 | 33 | // Particles initializers 34 | let coord_initializer = initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 35 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 36 | 37 | // PostMove 38 | let post_moves: Vec>> = vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 39 | 40 | // Velocity calculator 41 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 42 | 43 | // Stop checker 44 | let change_max_iterations = 150; 45 | let change_delta = 1e-8; 46 | let stop_checker = stopchecker::CompositeAny::new(vec![ 47 | Box::new(stopchecker::Threshold::new(1e-6)), 48 | Box::new(stopchecker::GoalNotChange::new( 49 | change_max_iterations, 50 | change_delta, 51 | )), 52 | Box::new(stopchecker::MaxIterations::new(3000)), 53 | ]); 54 | 55 | // Logger 56 | let mut stdout_verbose = io::stdout(); 57 | let mut stdout_result = io::stdout(); 58 | let mut stdout_time = io::stdout(); 59 | 60 | let loggers: Vec>>> = vec![ 61 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 62 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 63 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 64 | ]; 65 | 66 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 67 | Box::new(goal), 68 | Box::new(stop_checker), 69 | Box::new(coord_initializer), 70 | Box::new(velocity_initializer), 71 | Box::new(velocity_calculator), 72 | ); 73 | optimizer.set_loggers(loggers); 74 | optimizer.set_post_moves(post_moves); 75 | 76 | optimizer.find_min(); 77 | } 78 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-schwefel-iterative.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::{ 4 | particleswarm::{ 5 | self, initializing, postmove, postvelocitycalc, velocitycalc, PostMove, PostVelocityCalc, 6 | }, 7 | tools::{logging, stopchecker}, 8 | GoalFromFunction, IterativeOptimizer, Optimizer, 9 | }; 10 | 11 | use optlib_testfunc; 12 | 13 | type Coordinate = f32; 14 | 15 | fn main() { 16 | // General parameters 17 | let minval: Coordinate = -500.0; 18 | let maxval: Coordinate = 500.0; 19 | let particles_count = 100; 20 | let dimension = 3; 21 | let intervals = vec![(minval, maxval); dimension]; 22 | let phi_personal = 3.2; 23 | let phi_global = 1.0; 24 | let k = 0.9; 25 | 26 | // Goal function 27 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 28 | 29 | // Particles initializers 30 | let coord_initializer = 31 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 32 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 33 | 34 | // PostMove 35 | let post_moves: Vec>> = 36 | vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 37 | 38 | // Velocity calculator 39 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 40 | 41 | let max_velocity = 700.0; 42 | let post_velocity_calc: Vec>> = 43 | vec![Box::new(postvelocitycalc::MaxVelocityAbs::new(max_velocity))]; 44 | 45 | // Stop checker 46 | let stop_checker_1 = Box::new(stopchecker::MaxIterations::new(50)); 47 | let stop_checker_2 = Box::new(stopchecker::MaxIterations::new(150)); 48 | 49 | // Logger 50 | let mut stdout_verbose = io::stdout(); 51 | let mut stdout_result = io::stdout(); 52 | let mut stdout_time = io::stdout(); 53 | 54 | let loggers: Vec>>> = vec![ 55 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 56 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 57 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 58 | ]; 59 | 60 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 61 | Box::new(goal), 62 | stop_checker_1, 63 | Box::new(coord_initializer), 64 | Box::new(velocity_initializer), 65 | Box::new(velocity_calculator), 66 | ); 67 | optimizer.set_loggers(loggers); 68 | optimizer.set_post_moves(post_moves); 69 | optimizer.set_post_velocity_calc(post_velocity_calc); 70 | 71 | optimizer.find_min(); 72 | 73 | optimizer.set_stop_checker(stop_checker_2); 74 | optimizer.next_iterations(); 75 | } 76 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-schwefel-statistics-full.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with particle sqwarm algorithm. 2 | use std::fs::File; 3 | use std::io; 4 | use std::sync::mpsc; 5 | use std::thread; 6 | 7 | use optlib::particleswarm::{ 8 | self, initializing, postmove, postvelocitycalc, velocitycalc, ParticleSwarmOptimizer, PostMove, 9 | PostVelocityCalc, 10 | }; 11 | use optlib::tools::statistics::{ 12 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 13 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 14 | }; 15 | use optlib::tools::{logging, statistics, stopchecker}; 16 | use optlib::{Goal, GoalFromFunction, Optimizer}; 17 | use optlib_testfunc; 18 | 19 | /// Coordinates type 20 | type Coordinate = f32; 21 | 22 | fn create_optimizer<'a>( 23 | dimension: usize, 24 | goal: Box> + 'a>, 25 | ) -> ParticleSwarmOptimizer<'a, Coordinate> { 26 | // General parameters 27 | let minval: Coordinate = -500.0; 28 | let maxval: Coordinate = 500.0; 29 | let particles_count = 50; 30 | let intervals = vec![(minval, maxval); dimension]; 31 | 32 | let phi_best_personal = 3.2; 33 | let phi_best_current = 0.0; 34 | let phi_best_global = 1.0; 35 | 36 | let phi_worst_personal = 0.0; 37 | let phi_worst_current = 0.0; 38 | let phi_worst_global = 0.0; 39 | 40 | let phi_t = phi_best_personal + phi_best_current + phi_best_global + phi_worst_current; 41 | 42 | let alpha = 0.9; 43 | let xi = 2.0 * alpha / (phi_t - 2.0); 44 | 45 | // Particles initializers 46 | let coord_initializer = 47 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 48 | let velocity_initializer = 49 | initializing::ZeroVelocityInitializer::new(dimension, particles_count); 50 | 51 | let max_velocity = 700.0; 52 | let post_velocity_calc: Vec>> = vec![Box::new( 53 | postvelocitycalc::MaxVelocityAbs::new(max_velocity), 54 | )]; 55 | 56 | // PostMove 57 | let post_moves: Vec>> = 58 | vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 59 | 60 | // Velocity calculator 61 | let velocity_calculator = velocitycalc::NegativeReinforcement::new( 62 | phi_best_personal, 63 | phi_best_current, 64 | phi_best_global, 65 | phi_worst_personal, 66 | phi_worst_current, 67 | phi_worst_global, 68 | xi, 69 | ); 70 | 71 | // Stop checker 72 | let change_max_iterations = 50; 73 | let change_delta = 1e-8; 74 | let stop_checker = stopchecker::CompositeAny::new(vec![ 75 | Box::new(stopchecker::Threshold::new(1e-6)), 76 | Box::new(stopchecker::GoalNotChange::new( 77 | change_max_iterations, 78 | change_delta, 79 | )), 80 | Box::new(stopchecker::MaxIterations::new(1000)), 81 | ]); 82 | 83 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 84 | goal, 85 | Box::new(stop_checker), 86 | Box::new(coord_initializer), 87 | Box::new(velocity_initializer), 88 | Box::new(velocity_calculator), 89 | ); 90 | optimizer.set_post_moves(post_moves); 91 | optimizer.set_post_velocity_calc(post_velocity_calc); 92 | optimizer 93 | } 94 | 95 | fn print_convergence_statistics( 96 | mut writer: &mut dyn io::Write, 97 | stat: &statistics::Statistics>, 98 | ) { 99 | let average_convergence = stat.get_convergence().get_average_convergence(); 100 | for n in 0..average_convergence.len() { 101 | if let Some(goal_value) = average_convergence[n] { 102 | writeln!( 103 | &mut writer, 104 | "{n:<8}{value:15.10e}", 105 | n = n, 106 | value = goal_value 107 | ) 108 | .unwrap(); 109 | } 110 | } 111 | } 112 | 113 | fn print_solution(mut writer: &mut dyn io::Write, stat: &statistics::Statistics>) { 114 | let run_count = stat.get_run_count(); 115 | 116 | // Print solutions for every running 117 | let results = stat.get_results(); 118 | for n in 0..run_count { 119 | if let Some((solution, goal)) = &results[n] { 120 | let mut result_str = String::new(); 121 | result_str = result_str + &format!("{:<8}", n); 122 | 123 | for x in solution { 124 | result_str = result_str + &format!(" {:<20.10}", x); 125 | } 126 | result_str = result_str + &format!(" {:20.10}", goal); 127 | 128 | writeln!(&mut writer, "{}", result_str).unwrap(); 129 | } else { 130 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 131 | } 132 | } 133 | } 134 | 135 | fn print_statistics( 136 | stat: &statistics::Statistics>, 137 | call_count: &CallCountData, 138 | dimension: usize, 139 | ) { 140 | let valid_answer = vec![420.9687; dimension]; 141 | let delta = vec![1.0; dimension]; 142 | 143 | let success_rate_answer = stat 144 | .get_results() 145 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 146 | .unwrap(); 147 | let average_goal = stat.get_results().get_average_goal().unwrap(); 148 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 149 | 150 | println!("Run count{:15}", stat.get_run_count()); 151 | println!("Success rate:{:15.5}", success_rate_answer); 152 | println!("Average goal:{:15.5}", average_goal); 153 | println!( 154 | "Standard deviation for goal:{:15.5}", 155 | standard_deviation_goal 156 | ); 157 | println!( 158 | "Average goal function call count:{:15.5}", 159 | call_count.get_average_call_count().unwrap() 160 | ); 161 | } 162 | 163 | fn main() { 164 | let cpu = num_cpus::get(); 165 | let dimension = 3; 166 | 167 | // Running count per CPU 168 | let run_count = 1000 / cpu; 169 | 170 | println!("CPUs:{:15}", cpu); 171 | println!("Run count per CPU:{:8}", run_count); 172 | print!("Run optimizations... "); 173 | 174 | // Statistics from all runnings 175 | let mut full_stat = statistics::Statistics::new(); 176 | let mut full_call_count = CallCountData::new(); 177 | 178 | let (tx, rx) = mpsc::channel(); 179 | 180 | for _ in 0..cpu { 181 | let current_tx = mpsc::Sender::clone(&tx); 182 | 183 | thread::spawn(move || { 184 | let mut local_full_stat = statistics::Statistics::new(); 185 | let mut local_full_call_count = CallCountData::new(); 186 | 187 | for _ in 0..run_count { 188 | // Statistics from single run 189 | let mut statistics_data = statistics::Statistics::new(); 190 | let mut call_count = CallCountData::new(); 191 | { 192 | // Make a trait object for goal function 193 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::schwefel); 194 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 195 | 196 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 197 | 198 | // Add logger to collect statistics 199 | let stat_logger = 200 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 201 | let loggers: Vec>>> = vec![stat_logger]; 202 | optimizer.set_loggers(loggers); 203 | 204 | // Run optimization 205 | optimizer.find_min(); 206 | } 207 | 208 | // Add current running statistics to full statistics 209 | local_full_stat.unite(statistics_data); 210 | local_full_call_count.unite(call_count); 211 | } 212 | current_tx 213 | .send((local_full_stat, local_full_call_count)) 214 | .unwrap(); 215 | }); 216 | } 217 | 218 | // Collect data from threads 219 | for _ in 0..cpu { 220 | let (statistics_data, call_count) = rx.recv().unwrap(); 221 | full_stat.unite(statistics_data); 222 | full_call_count.unite(call_count); 223 | } 224 | 225 | println!("OK"); 226 | 227 | // Print out statistics 228 | let result_stat_fname = "result_stat.txt"; 229 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 230 | 231 | let convergence_stat_fname = "convergence_stat.txt"; 232 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 233 | print_solution(&mut result_stat_file, &full_stat); 234 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 235 | print_statistics(&full_stat, &full_call_count, dimension); 236 | } 237 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-schwefel-statistics-inertia.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with particle sqwarm algorithm. 2 | use std::fs::File; 3 | use std::io; 4 | use std::sync::mpsc; 5 | use std::thread; 6 | 7 | use optlib::particleswarm::{ 8 | self, initializing, postmove, postvelocitycalc, velocitycalc, ParticleSwarmOptimizer, PostMove, 9 | PostVelocityCalc, 10 | }; 11 | use optlib::tools::statistics::{ 12 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 13 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 14 | }; 15 | use optlib::tools::{logging, statistics, stopchecker}; 16 | use optlib::{Goal, GoalFromFunction, Optimizer}; 17 | use optlib_testfunc; 18 | 19 | /// Coordinates type 20 | type Coordinate = f32; 21 | 22 | fn create_optimizer<'a>( 23 | dimension: usize, 24 | goal: Box> + 'a>, 25 | ) -> ParticleSwarmOptimizer<'a, Coordinate> { 26 | // General parameters 27 | let minval: Coordinate = -500.0; 28 | let maxval: Coordinate = 500.0; 29 | let particles_count = 50; 30 | let intervals = vec![(minval, maxval); dimension]; 31 | 32 | let phi_personal = 2.2; 33 | let phi_global = 0.6; 34 | 35 | // Particles initializers 36 | let coord_initializer = 37 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 38 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 39 | 40 | let max_velocity = 700.0; 41 | let post_velocity_calc: Vec>> = 42 | vec![Box::new(postvelocitycalc::MaxVelocityAbs::new(max_velocity))]; 43 | 44 | // PostMove 45 | let post_moves: Vec>> = 46 | vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 47 | 48 | let inertia = Box::new(velocitycalc::ConstInertia::new(0.85)); 49 | // let inertia = Box::new(velocitycalc::LinearInertia::new(0.2, 0.9, 400)); 50 | 51 | // Velocity calculator 52 | let velocity_calculator = velocitycalc::InertiaVelocityCalculator::new( 53 | phi_personal, 54 | phi_global, 55 | inertia 56 | ); 57 | 58 | // Stop checker 59 | let change_max_iterations = 50; 60 | let change_delta = 1e-8; 61 | let stop_checker = stopchecker::CompositeAny::new(vec![ 62 | Box::new(stopchecker::Threshold::new(1e-6)), 63 | Box::new(stopchecker::GoalNotChange::new( 64 | change_max_iterations, 65 | change_delta, 66 | )), 67 | Box::new(stopchecker::MaxIterations::new(400)), 68 | ]); 69 | 70 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 71 | goal, 72 | Box::new(stop_checker), 73 | Box::new(coord_initializer), 74 | Box::new(velocity_initializer), 75 | Box::new(velocity_calculator), 76 | ); 77 | optimizer.set_post_moves(post_moves); 78 | optimizer.set_post_velocity_calc(post_velocity_calc); 79 | optimizer 80 | } 81 | 82 | fn print_convergence_statistics( 83 | mut writer: &mut dyn io::Write, 84 | stat: &statistics::Statistics>, 85 | ) { 86 | let average_convergence = stat.get_convergence().get_average_convergence(); 87 | for n in 0..average_convergence.len() { 88 | if let Some(goal_value) = average_convergence[n] { 89 | writeln!( 90 | &mut writer, 91 | "{n:<8}{value:15.10e}", 92 | n = n, 93 | value = goal_value 94 | ) 95 | .unwrap(); 96 | } 97 | } 98 | } 99 | 100 | fn print_solution( 101 | mut writer: &mut dyn io::Write, 102 | stat: &statistics::Statistics>, 103 | ) { 104 | let run_count = stat.get_run_count(); 105 | 106 | // Print solutions for every running 107 | let results = stat.get_results(); 108 | for n in 0..run_count { 109 | if let Some((solution, goal)) = &results[n] { 110 | let mut result_str = String::new(); 111 | result_str = result_str + &format!("{:<8}", n); 112 | 113 | for x in solution { 114 | result_str = result_str + &format!(" {:<20.10}", x); 115 | } 116 | result_str = result_str + &format!(" {:20.10}", goal); 117 | 118 | writeln!(&mut writer, "{}", result_str).unwrap(); 119 | } else { 120 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 121 | } 122 | } 123 | } 124 | 125 | fn print_statistics( 126 | stat: &statistics::Statistics>, 127 | call_count: &CallCountData, 128 | dimension: usize, 129 | ) { 130 | let valid_answer = vec![420.9687; dimension]; 131 | let delta = vec![1.0; dimension]; 132 | 133 | let success_rate_answer = stat 134 | .get_results() 135 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 136 | .unwrap(); 137 | let average_goal = stat.get_results().get_average_goal().unwrap(); 138 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 139 | 140 | println!("Run count{:15}", stat.get_run_count()); 141 | println!("Success rate:{:15.5}", success_rate_answer); 142 | println!("Average goal:{:15.5}", average_goal); 143 | println!( 144 | "Standard deviation for goal:{:15.5}", 145 | standard_deviation_goal 146 | ); 147 | println!( 148 | "Average goal function call count:{:15.5}", 149 | call_count.get_average_call_count().unwrap() 150 | ); 151 | } 152 | 153 | fn main() { 154 | let cpu = num_cpus::get(); 155 | let dimension = 3; 156 | 157 | // Running count per CPU 158 | let run_count = 1000 / cpu; 159 | 160 | println!("CPUs:{:15}", cpu); 161 | println!("Run count per CPU:{:8}", run_count); 162 | print!("Run optimizations... "); 163 | 164 | // Statistics from all runnings 165 | let mut full_stat = statistics::Statistics::new(); 166 | let mut full_call_count = CallCountData::new(); 167 | 168 | let (tx, rx) = mpsc::channel(); 169 | 170 | for _ in 0..cpu { 171 | let current_tx = mpsc::Sender::clone(&tx); 172 | 173 | thread::spawn(move || { 174 | let mut local_full_stat = statistics::Statistics::new(); 175 | let mut local_full_call_count = CallCountData::new(); 176 | 177 | for _ in 0..run_count { 178 | // Statistics from single run 179 | let mut statistics_data = statistics::Statistics::new(); 180 | let mut call_count = CallCountData::new(); 181 | { 182 | // Make a trait object for goal function 183 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::schwefel); 184 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 185 | 186 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 187 | 188 | // Add logger to collect statistics 189 | let stat_logger = 190 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 191 | let loggers: Vec>>> = vec![stat_logger]; 192 | optimizer.set_loggers(loggers); 193 | 194 | // Run optimization 195 | optimizer.find_min(); 196 | } 197 | 198 | // Add current running statistics to full statistics 199 | local_full_stat.unite(statistics_data); 200 | local_full_call_count.unite(call_count); 201 | } 202 | current_tx 203 | .send((local_full_stat, local_full_call_count)) 204 | .unwrap(); 205 | }); 206 | } 207 | 208 | // Collect data from threads 209 | for _ in 0..cpu { 210 | let (statistics_data, call_count) = rx.recv().unwrap(); 211 | full_stat.unite(statistics_data); 212 | full_call_count.unite(call_count); 213 | } 214 | 215 | println!("OK"); 216 | 217 | // Print out statistics 218 | let result_stat_fname = "result_stat.txt"; 219 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 220 | 221 | let convergence_stat_fname = "convergence_stat.txt"; 222 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 223 | print_solution(&mut result_stat_file, &full_stat); 224 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 225 | print_statistics(&full_stat, &full_call_count, dimension); 226 | } 227 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-schwefel-statistics.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with particle swarm algorithm. 2 | use std::fs::File; 3 | use std::io; 4 | use std::sync::mpsc; 5 | use std::thread; 6 | 7 | use num_cpus; 8 | 9 | use optlib::particleswarm::{ 10 | self, initializing, postmove, postvelocitycalc, velocitycalc, ParticleSwarmOptimizer, PostMove, 11 | PostVelocityCalc, 12 | }; 13 | use optlib::tools::statistics::{ 14 | get_predicate_success_vec_solution, CallCountData, GoalCalcStatistics, 15 | StatFunctionsConvergence, StatFunctionsGoal, StatFunctionsSolution, 16 | }; 17 | use optlib::tools::{logging, statistics, stopchecker}; 18 | use optlib::{Goal, GoalFromFunction, Optimizer}; 19 | use optlib_testfunc; 20 | 21 | /// Coordinates type 22 | type Coordinate = f32; 23 | 24 | fn create_optimizer<'a>( 25 | dimension: usize, 26 | goal: Box> + 'a>, 27 | ) -> ParticleSwarmOptimizer<'a, Coordinate> { 28 | // General parameters 29 | let minval: Coordinate = -500.0; 30 | let maxval: Coordinate = 500.0; 31 | let particles_count = 30; 32 | let intervals = vec![(minval, maxval); dimension]; 33 | let phi_personal = 3.2; 34 | let phi_global = 1.0; 35 | let k = 0.9; 36 | 37 | // Particles initializers 38 | let coord_initializer = 39 | initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 40 | let velocity_initializer = 41 | initializing::ZeroVelocityInitializer::new(dimension, particles_count); 42 | 43 | let max_velocity = 700.0; 44 | let post_velocity_calc: Vec>> = vec![Box::new( 45 | postvelocitycalc::MaxVelocityAbs::new(max_velocity), 46 | )]; 47 | 48 | // PostMove 49 | let teleport_probability = 0.05; 50 | let post_moves: Vec>> = vec![ 51 | Box::new(postmove::RandomTeleport::new( 52 | intervals.clone(), 53 | teleport_probability, 54 | )), 55 | Box::new(postmove::MoveToBoundary::new(intervals.clone())), 56 | ]; 57 | 58 | // Velocity calculator 59 | let velocity_calculator = 60 | velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 61 | 62 | // Stop checker 63 | // let change_max_iterations = 300; 64 | // let change_delta = 1e-10; 65 | let stop_checker = stopchecker::CompositeAny::new(vec![ 66 | Box::new(stopchecker::Threshold::new(1e-8)), 67 | // Box::new(stopchecker::GoalNotChange::new( 68 | // change_max_iterations, 69 | // change_delta, 70 | // )), 71 | Box::new(stopchecker::MaxIterations::new(3000)), 72 | ]); 73 | 74 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 75 | goal, 76 | Box::new(stop_checker), 77 | Box::new(coord_initializer), 78 | Box::new(velocity_initializer), 79 | Box::new(velocity_calculator), 80 | ); 81 | optimizer.set_post_moves(post_moves); 82 | optimizer.set_post_velocity_calc(post_velocity_calc); 83 | optimizer 84 | } 85 | 86 | fn print_convergence_statistics( 87 | mut writer: &mut dyn io::Write, 88 | stat: &statistics::Statistics>, 89 | ) { 90 | let average_convergence = stat.get_convergence().get_average_convergence(); 91 | for n in 0..average_convergence.len() { 92 | if let Some(goal_value) = average_convergence[n] { 93 | writeln!( 94 | &mut writer, 95 | "{n:<8}{value:15.10e}", 96 | n = n, 97 | value = goal_value 98 | ) 99 | .unwrap(); 100 | } 101 | } 102 | } 103 | 104 | fn print_solution(mut writer: &mut dyn io::Write, stat: &statistics::Statistics>) { 105 | let run_count = stat.get_run_count(); 106 | 107 | // Print solutions for every running 108 | let results = stat.get_results(); 109 | for n in 0..run_count { 110 | if let Some((solution, goal)) = &results[n] { 111 | let mut result_str = String::new(); 112 | result_str = result_str + &format!("{:<8}", n); 113 | 114 | for x in solution { 115 | result_str = result_str + &format!(" {:<20.10}", x); 116 | } 117 | result_str = result_str + &format!(" {:20.10}", goal); 118 | 119 | writeln!(&mut writer, "{}", result_str).unwrap(); 120 | } else { 121 | writeln!(&mut writer, "{n:<8} Failed", n = n).unwrap(); 122 | } 123 | } 124 | } 125 | 126 | fn print_statistics( 127 | stat: &statistics::Statistics>, 128 | call_count: &CallCountData, 129 | dimension: usize, 130 | ) { 131 | let valid_answer = vec![420.9687; dimension]; 132 | let delta = vec![1.0; dimension]; 133 | 134 | let success_rate_answer = stat 135 | .get_results() 136 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 137 | .unwrap(); 138 | let average_goal = stat.get_results().get_average_goal().unwrap(); 139 | let standard_deviation_goal = stat.get_results().get_standard_deviation_goal().unwrap(); 140 | 141 | println!("Run count{:15}", stat.get_run_count()); 142 | println!("Success rate:{:15.5}", success_rate_answer); 143 | println!("Average goal:{:15.5}", average_goal); 144 | println!( 145 | "Standard deviation for goal:{:15.5}", 146 | standard_deviation_goal 147 | ); 148 | println!( 149 | "Average goal function call count:{:15.5}", 150 | call_count.get_average_call_count().unwrap() 151 | ); 152 | } 153 | 154 | fn main() { 155 | let cpu = num_cpus::get(); 156 | let dimension = 3; 157 | 158 | // Running count per CPU 159 | let run_count = 1000 / cpu; 160 | 161 | println!("CPUs:{:15}", cpu); 162 | println!("Run count per CPU:{:8}", run_count); 163 | print!("Run optimizations... "); 164 | 165 | // Statistics from all runnings 166 | let mut full_stat = statistics::Statistics::new(); 167 | let mut full_call_count = CallCountData::new(); 168 | 169 | let (tx, rx) = mpsc::channel(); 170 | 171 | for _ in 0..cpu { 172 | let current_tx = mpsc::Sender::clone(&tx); 173 | 174 | thread::spawn(move || { 175 | let mut local_full_stat = statistics::Statistics::new(); 176 | let mut local_full_call_count = CallCountData::new(); 177 | 178 | for _ in 0..run_count { 179 | // Statistics from single run 180 | let mut statistics_data = statistics::Statistics::new(); 181 | let mut call_count = CallCountData::new(); 182 | { 183 | // Make a trait object for goal function 184 | let mut goal_object = GoalFromFunction::new(optlib_testfunc::schwefel); 185 | let goal = GoalCalcStatistics::new(&mut goal_object, &mut call_count); 186 | 187 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 188 | 189 | // Add logger to collect statistics 190 | let stat_logger = 191 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 192 | let loggers: Vec>>> = vec![stat_logger]; 193 | optimizer.set_loggers(loggers); 194 | 195 | // Run optimization 196 | optimizer.find_min(); 197 | } 198 | 199 | // Add current running statistics to full statistics 200 | local_full_stat.unite(statistics_data); 201 | local_full_call_count.unite(call_count); 202 | } 203 | current_tx 204 | .send((local_full_stat, local_full_call_count)) 205 | .unwrap(); 206 | }); 207 | } 208 | 209 | // Collect data from threads 210 | for _ in 0..cpu { 211 | let (statistics_data, call_count) = rx.recv().unwrap(); 212 | full_stat.unite(statistics_data); 213 | full_call_count.unite(call_count); 214 | } 215 | 216 | println!("OK"); 217 | 218 | // Print out statistics 219 | let result_stat_fname = "result_stat.txt"; 220 | let mut result_stat_file = File::create(result_stat_fname).unwrap(); 221 | 222 | let convergence_stat_fname = "convergence_stat.txt"; 223 | let mut convergence_stat_file = File::create(convergence_stat_fname).unwrap(); 224 | print_solution(&mut result_stat_file, &full_stat); 225 | print_convergence_statistics(&mut convergence_stat_file, &full_stat); 226 | print_statistics(&full_stat, &full_call_count, dimension); 227 | } 228 | -------------------------------------------------------------------------------- /optlib/examples/particleswarm-schwefel.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use optlib::{ 4 | tools::{logging, stopchecker}, 5 | GoalFromFunction, Optimizer, 6 | particleswarm::{ 7 | self, 8 | initializing, 9 | postmove, 10 | velocitycalc, 11 | PostMove, 12 | postvelocitycalc, 13 | PostVelocityCalc, 14 | }, 15 | }; 16 | 17 | use optlib_testfunc; 18 | 19 | type Coordinate = f32; 20 | 21 | fn main() { 22 | // General parameters 23 | let minval: Coordinate = -500.0; 24 | let maxval: Coordinate = 500.0; 25 | let particles_count = 100; 26 | let dimension = 3; 27 | let intervals = vec![(minval, maxval); dimension]; 28 | let phi_personal = 3.2; 29 | let phi_global = 1.0; 30 | let k = 0.9; 31 | 32 | // Goal function 33 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 34 | 35 | // Particles initializers 36 | let coord_initializer = initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 37 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 38 | 39 | // PostMove 40 | let post_moves: Vec>> = vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 41 | 42 | // Velocity calculator 43 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 44 | 45 | let max_velocity = 700.0; 46 | let post_velocity_calc: Vec>> = 47 | vec![Box::new(postvelocitycalc::MaxVelocityAbs::new(max_velocity))]; 48 | 49 | // Stop checker 50 | let change_max_iterations = 150; 51 | let change_delta = 1e-8; 52 | let stop_checker = stopchecker::CompositeAny::new(vec![ 53 | Box::new(stopchecker::Threshold::new(1e-6)), 54 | Box::new(stopchecker::GoalNotChange::new( 55 | change_max_iterations, 56 | change_delta, 57 | )), 58 | Box::new(stopchecker::MaxIterations::new(3000)), 59 | ]); 60 | 61 | // Logger 62 | let mut stdout_verbose = io::stdout(); 63 | let mut stdout_result = io::stdout(); 64 | let mut stdout_time = io::stdout(); 65 | 66 | let loggers: Vec>>> = vec![ 67 | Box::new(logging::VerboseLogger::new(&mut stdout_verbose, 15)), 68 | Box::new(logging::ResultOnlyLogger::new(&mut stdout_result, 15)), 69 | Box::new(logging::TimeLogger::new(&mut stdout_time)), 70 | ]; 71 | 72 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 73 | Box::new(goal), 74 | Box::new(stop_checker), 75 | Box::new(coord_initializer), 76 | Box::new(velocity_initializer), 77 | Box::new(velocity_calculator), 78 | ); 79 | optimizer.set_loggers(loggers); 80 | optimizer.set_post_moves(post_moves); 81 | optimizer.set_post_velocity_calc(post_velocity_calc); 82 | 83 | optimizer.find_min(); 84 | } 85 | -------------------------------------------------------------------------------- /optlib/src/genetic/creation/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with algorithms with initial creation of individuals 2 | pub mod vec_float; 3 | -------------------------------------------------------------------------------- /optlib/src/genetic/creation/vec_float.rs: -------------------------------------------------------------------------------- 1 | //! The module with Creators for the case when chromosomes are Vec (vector of the genes). 2 | //! Gene - single value in chromosome. 3 | //! The Creators used to create the first generation of individuals. 4 | 5 | use num::NumCast; 6 | use rand::distributions::{Distribution, Uniform}; 7 | use rand::rngs::ThreadRng; 8 | 9 | use crate::genetic::Creator; 10 | 11 | /// Creator to initialize population by individuals with random genes in the preset 12 | /// intervals. 13 | /// `G` - type of genes. Chromosome is vector of the genes. 14 | pub struct RandomCreator { 15 | population_size: usize, 16 | intervals: Vec<(G, G)>, 17 | random: ThreadRng, 18 | } 19 | 20 | impl RandomCreator { 21 | /// Constructor. 22 | /// 23 | /// `G` - type of genes. Chromosome is vector of the genes. 24 | /// 25 | /// # Parameters 26 | /// * `population_size` - individuals count in the first generation. 27 | /// * `intervals` - vector of the tuples (minval, maxval). Length of the `intervals` must 28 | /// equal genes count in the chromosome. The values of `minval` and `maxval` will be included 29 | /// in random interval. 30 | pub fn new(population_size: usize, intervals: Vec<(G, G)>) -> Self { 31 | assert!(population_size > 0); 32 | assert!(!intervals.is_empty()); 33 | for interval in &intervals { 34 | assert!(interval.0 < interval.1); 35 | } 36 | 37 | let random = rand::thread_rng(); 38 | Self { 39 | population_size, 40 | intervals, 41 | random, 42 | } 43 | } 44 | } 45 | 46 | impl Creator> for RandomCreator { 47 | fn create(&mut self) -> Vec> { 48 | let mut population = Vec::with_capacity(self.population_size * 2); 49 | let chromo_count = self.intervals.len(); 50 | 51 | for _ in 0..self.population_size { 52 | let mut chromo = Vec::with_capacity(chromo_count); 53 | for interval in &self.intervals { 54 | let between = Uniform::new_inclusive( 55 | interval.0.to_f64().unwrap(), 56 | interval.1.to_f64().unwrap(), 57 | ); 58 | chromo.push(G::from(between.sample(&mut self.random)).unwrap()); 59 | } 60 | 61 | population.push(chromo); 62 | } 63 | 64 | population 65 | } 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | 72 | #[test] 73 | fn test_population_size() { 74 | let population_size = 10; 75 | let intervals = vec![(0.0, 1.0)]; 76 | let mut creator = RandomCreator::new(population_size, intervals); 77 | 78 | let chromosomes = creator.create(); 79 | assert_eq!(chromosomes.len(), population_size); 80 | } 81 | 82 | #[test] 83 | fn test_intervals() { 84 | let population_size = 1000; 85 | let intervals = vec![(0.0, 1.0), (-1.0, 1.0), (100.0, 110.0)]; 86 | let mut creator = RandomCreator::new(population_size, intervals); 87 | 88 | let chromosomes: Vec> = creator.create(); 89 | for chromosome in chromosomes { 90 | assert!(chromosome[0] >= 0.0); 91 | assert!(chromosome[0] <= 1.0); 92 | 93 | assert!(chromosome[1] >= -1.0); 94 | assert!(chromosome[1] <= 1.0); 95 | 96 | assert!(chromosome[2] >= 100.0); 97 | assert!(chromosome[2] <= 110.0); 98 | } 99 | } 100 | 101 | #[test] 102 | #[should_panic] 103 | fn empty_population() { 104 | let population_size = 0; 105 | let intervals = vec![(0.0, 1.0)]; 106 | RandomCreator::new(population_size, intervals); 107 | } 108 | 109 | #[test] 110 | #[should_panic] 111 | fn empty_intervals() { 112 | let population_size = 10; 113 | let intervals: Vec<(f64, f64)> = Vec::new(); 114 | RandomCreator::new(population_size, intervals); 115 | } 116 | 117 | #[test] 118 | #[should_panic] 119 | fn invalid_intervals_01() { 120 | let population_size = 10; 121 | let intervals = vec![(1.0, 0.0)]; 122 | RandomCreator::new(population_size, intervals); 123 | } 124 | 125 | #[test] 126 | #[should_panic] 127 | fn invalid_intervals_02() { 128 | let population_size = 10; 129 | let intervals = vec![(0.0, 0.0)]; 130 | RandomCreator::new(population_size, intervals); 131 | } 132 | 133 | #[test] 134 | #[should_panic] 135 | fn invalid_intervals_03() { 136 | let population_size = 10; 137 | let intervals = vec![(0.0, 1.0), (10.0, 0.0)]; 138 | RandomCreator::new(population_size, intervals); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /optlib/src/genetic/cross/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with most usable algorithms of crossing for various types. 2 | //! The module contains struct which implements the `Cross` trait and functions to cross 3 | //! chromosomes various types. 4 | 5 | use std::mem; 6 | 7 | use crate::genetic::Cross; 8 | use num::{Float, Num, NumCast}; 9 | use rand::distributions::{Distribution, Uniform}; 10 | use rand::rngs::ThreadRng; 11 | 12 | /// Struct to cross all genes (`G` - type of genes) in chromosome of type Vec. 13 | pub struct VecCrossAllGenes { 14 | single_cross: Box>, 15 | } 16 | 17 | /// Child chromosome is arithmetic mean of parent chromosomes. Result of cross is single child. 18 | /// The chromosomes must be numeric type. 19 | pub struct CrossMean; 20 | 21 | /// For float type chromosomes (f32, f64). Child chromosome is geometric mean of parent 22 | /// chromosomes. Result of cross is single child. 23 | pub struct FloatCrossGeometricMean; 24 | 25 | /// Bitwise cross. Use single point crossing. Result of cross is single child. 26 | pub struct CrossBitwise { 27 | random: ThreadRng, 28 | } 29 | 30 | /// Bitwise cross for float type chromosomes. Exponent and mantissa will be crossed independently. 31 | /// Use single point crossing. The sign is taken from one of parents at random. 32 | pub struct FloatCrossExp { 33 | random: ThreadRng, 34 | } 35 | 36 | impl CrossMean { 37 | pub fn new() -> Self { 38 | Self {} 39 | } 40 | } 41 | 42 | /// ``` 43 | /// use optlib::genetic::cross; 44 | /// use optlib::genetic::Cross; 45 | /// 46 | /// assert!(cross::CrossMean::new().cross(&vec![&1.0_f32, &2.0_f32]) == vec![1.5_f32]); 47 | /// assert!(cross::CrossMean::new().cross(&vec![&0.0_f64, &1.0_f64]) == vec![0.5_f64]); 48 | /// assert!(cross::CrossMean::new().cross(&vec![&0.0_f64, &1.0_f64, &2.0_f64]) == vec![1.0_f64]); 49 | /// ``` 50 | impl Cross for CrossMean { 51 | fn cross(&mut self, parents_genes: &[&G]) -> Vec { 52 | assert!(parents_genes.len() >= 2); 53 | let mut result: G = parents_genes 54 | .iter() 55 | .fold(G::zero(), |x, y| x + (**y).clone()); 56 | 57 | result = result / G::from(parents_genes.len()).unwrap(); 58 | vec![result] 59 | } 60 | } 61 | 62 | impl FloatCrossGeometricMean { 63 | /// Constructor. 64 | pub fn new() -> Self { 65 | Self {} 66 | } 67 | } 68 | 69 | /// ``` 70 | /// use optlib::genetic::cross; 71 | /// use optlib::genetic::Cross; 72 | /// 73 | /// let mut crosser = cross::FloatCrossGeometricMean::new(); 74 | /// 75 | /// let parents_genes_equal = vec![&1.0_f32, &1.0_f32]; 76 | /// assert!(crosser.cross(&parents_genes_equal).len() == 1); 77 | /// assert!(crosser.cross(&parents_genes_equal)[0] - 1.0_f32 < 1e-7); 78 | /// 79 | /// let parents_genes_1 = vec![&1.0_f32, &2.0_f32]; 80 | /// assert!(crosser.cross(&parents_genes_1).len() == 1); 81 | /// assert!(crosser.cross(&parents_genes_1)[0] - 1.41421 < 1e-4); 82 | /// 83 | /// let parents_genes_2 = vec![&1.0_f64, &2.0_f64, &3.0_f64]; 84 | /// assert!(crosser.cross(&parents_genes_2).len() == 1); 85 | /// assert!(crosser.cross(&parents_genes_2)[0] - 1.81712 < 1e-4); 86 | /// ``` 87 | impl Cross for FloatCrossGeometricMean { 88 | fn cross(&mut self, parents_genes: &[&G]) -> Vec { 89 | assert!(parents_genes.len() >= 2); 90 | let mut result: G = parents_genes.iter().fold(G::one(), |x, y| x * (**y)); 91 | 92 | result = result.powf(G::one() / G::from(parents_genes.len()).unwrap()); 93 | vec![result] 94 | } 95 | } 96 | 97 | impl CrossBitwise { 98 | /// Constructor. 99 | pub fn new() -> Self { 100 | let random = rand::thread_rng(); 101 | Self { random } 102 | } 103 | } 104 | 105 | impl Cross for CrossBitwise { 106 | fn cross(&mut self, parents_genes: &[&f64]) -> Vec { 107 | assert_eq!(parents_genes.len(), 2); 108 | let size = mem::size_of::() * 8; 109 | let between = Uniform::new(1, size); 110 | let pos = between.sample(&mut self.random); 111 | 112 | vec![cross_f64(*parents_genes[0], *parents_genes[1], pos)] 113 | } 114 | } 115 | 116 | impl Cross for CrossBitwise { 117 | fn cross(&mut self, parents_genes: &[&f32]) -> Vec { 118 | assert_eq!(parents_genes.len(), 2); 119 | let size = mem::size_of::() * 8; 120 | let between = Uniform::new(1, size); 121 | let pos = between.sample(&mut self.random); 122 | 123 | vec![cross_f32(*parents_genes[0], *parents_genes[1], pos)] 124 | } 125 | } 126 | 127 | impl VecCrossAllGenes { 128 | pub fn new(single_cross: Box>) -> Self { 129 | Self { single_cross } 130 | } 131 | } 132 | 133 | impl Cross> for VecCrossAllGenes { 134 | fn cross(&mut self, parents: &[&Vec]) -> Vec> { 135 | assert!(parents.len() == 2); 136 | 137 | let parent_1 = parents[0]; 138 | let parent_2 = parents[1]; 139 | 140 | let gene_count = parent_1.len(); 141 | let mut child = vec![]; 142 | 143 | for n in 0..gene_count { 144 | let mut new_gene = self 145 | .single_cross 146 | .cross(vec![&parent_1[n], &parent_2[n]].as_slice()); 147 | child.append(&mut new_gene); 148 | } 149 | vec![child] 150 | } 151 | } 152 | 153 | impl FloatCrossExp { 154 | pub fn new() -> Self { 155 | let random = rand::thread_rng(); 156 | Self { random } 157 | } 158 | } 159 | 160 | impl Cross for FloatCrossExp { 161 | fn cross(&mut self, parents_genes: &[&T]) -> Vec { 162 | assert_eq!(parents_genes.len(), 2); 163 | // mantissa: u64, exponent: i16, sign: i8 164 | let (mantissa_1, exponent_1, sign_1) = parents_genes[0].integer_decode(); 165 | let (mantissa_2, exponent_2, sign_2) = parents_genes[1].integer_decode(); 166 | 167 | let mantissa_size = mem::size_of_val(&mantissa_1) * 8; 168 | let exponent_size = mem::size_of_val(&exponent_1) * 8; 169 | 170 | let mantissa_between = Uniform::new(1, mantissa_size); 171 | let exponent_between = Uniform::new(1, exponent_size); 172 | 173 | let mantissa_pos = mantissa_between.sample(&mut self.random); 174 | let exponent_pos = exponent_between.sample(&mut self.random); 175 | 176 | let mantissa_child = cross_u64(mantissa_1, mantissa_2, mantissa_pos); 177 | let exponent_child = cross_i16(exponent_1, exponent_2, exponent_pos); 178 | 179 | let sign_child = match Uniform::new_inclusive(0i8, 1i8).sample(&mut self.random) { 180 | 0 => sign_1, 181 | 1 => sign_2, 182 | _ => panic!("Invalid random value in FloatCrossExp"), 183 | }; 184 | 185 | vec![ 186 | T::from(sign_child).unwrap() 187 | * T::from(mantissa_child).unwrap() 188 | * T::from(exponent_child).unwrap().exp2(), 189 | ] 190 | } 191 | } 192 | 193 | /// Single point crossing. 194 | /// 195 | /// # Parameters 196 | /// * `parent_1`, `parent_2` - parents for crossing. 197 | /// * `pos` - position for bytes exchange. The position is counted from right. 198 | /// 199 | /// Returns single child. 200 | /// 201 | /// # Examples 202 | /// 203 | /// ``` 204 | /// use optlib::genetic::cross; 205 | /// 206 | /// assert_eq!(cross::cross_u64(0u64, std::u64::MAX, 1), 1u64); 207 | /// assert_eq!(cross::cross_u64(0u64, std::u64::MAX, 4), 0b_1111_u64); 208 | /// assert_eq!(cross::cross_u64(0u64, std::u64::MAX, 63), 209 | /// 0b_0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_u64); 210 | /// assert_eq!(cross::cross_u64(std::u64::MAX, 0u64, 4), 211 | /// 0b_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_0000_u64); 212 | /// ``` 213 | pub fn cross_u64(parent_1: u64, parent_2: u64, pos: usize) -> u64 { 214 | let size = mem::size_of::() * 8; 215 | let mask_parent_1 = !0u64 << pos; 216 | let mask_parent_2 = !0u64 >> (size - pos); 217 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 218 | } 219 | 220 | /// Single point crossing. 221 | /// 222 | /// # Parameters 223 | /// * `parent_1`, `parent_2` - parents for crossing. 224 | /// * `pos` - position for bytes exchange. The position is counted from right. 225 | /// 226 | /// Returns single child. 227 | /// 228 | /// # Examples 229 | /// 230 | /// ``` 231 | /// use optlib::genetic::cross; 232 | /// 233 | /// assert_eq!(cross::cross_u32(0u32, std::u32::MAX, 1), 1u32); 234 | /// assert_eq!(cross::cross_u32(0u32, std::u32::MAX, 4), 0b_1111_u32); 235 | /// assert_eq!(cross::cross_u32(0u32, std::u32::MAX, 31), 0b_0111_1111_1111_1111_1111_1111_1111_1111_u32); 236 | /// assert_eq!(cross::cross_u32(std::u32::MAX, 0u32, 4), 0b_1111_1111_1111_1111_1111_1111_1111_0000_u32); 237 | /// ``` 238 | pub fn cross_u32(parent_1: u32, parent_2: u32, pos: usize) -> u32 { 239 | let size = mem::size_of::() * 8; 240 | let mask_parent_1 = !0u32 << pos; 241 | let mask_parent_2 = !0u32 >> (size - pos); 242 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 243 | } 244 | 245 | /// Single point crossing. 246 | /// 247 | /// # Parameters 248 | /// * `parent_1`, `parent_2` - parents for crossing. 249 | /// * `pos` - position for bytes exchange. The position is counted from right. 250 | /// 251 | /// Returns single child. 252 | /// 253 | ///# Examples 254 | /// 255 | /// ``` 256 | /// use optlib::genetic::cross; 257 | /// 258 | /// // -1i16 == 0b_1111_1111_1111_1111 259 | /// assert_eq!(cross::cross_i16(0i16, -1i16, 4), 0b_0000_0000_0000_1111_i16); 260 | /// assert_eq!(cross::cross_i16(0i16, -1i16, 1), 0b_0000_0000_0000_0001_i16); 261 | /// assert_eq!(cross::cross_i16(0i16, -1i16, 15), 0b_0111_1111_1111_1111_i16); 262 | /// assert_eq!(cross::cross_i16(0i16, -1i16, 8), 0b_0000_0000_1111_1111_i16); 263 | /// 264 | /// // -1i16 == 0b_1111_1111_1111_1111 265 | /// // -16i16 == 0b_1111_1111_1111_0000 266 | /// assert_eq!(cross::cross_i16(-1i16, 0i16, 4), -16i16); 267 | /// 268 | /// // -1i16 == 0b_1111_1111_1111_1111 269 | /// // -32768i16 == 0b_1000_0000_0000_0000 270 | /// assert_eq!(cross::cross_i16(-1i16, 0i16, 15), -32768i16); 271 | /// ``` 272 | pub fn cross_i16(parent_1: i16, parent_2: i16, pos: usize) -> i16 { 273 | let size = mem::size_of::() * 8; 274 | let mask_parent_1 = !0i16 << pos; 275 | let mask_parent_2 = std::i16::MAX >> (size - pos - 1); 276 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 277 | } 278 | 279 | /// Single point crossing. 280 | /// 281 | /// # Parameters 282 | /// * `parent_1`, `parent_2` - parents for crossing. 283 | /// * `pos` - position for bytes exchange. The position is counted from right. 284 | /// 285 | /// Returns single child. 286 | /// # Examples 287 | /// 288 | /// ``` 289 | /// use optlib::genetic::cross; 290 | /// 291 | /// assert_eq!(cross::cross_u16(0b_0000_0000_0000_0000, 0b_1111_1111_1111_1111, 4), 0b_1111); 292 | /// assert_eq!(cross::cross_u16(0b_0000_0000_0000_0000, 0b_1111_1111_1111_1111, 1), 0b_1); 293 | /// assert_eq!(cross::cross_u16(0b_0000_0000_0000_0000, 0b_1111_1111_1111_1111, 8), 0b_0000_0000_1111_1111); 294 | /// assert_eq!(cross::cross_u16(0b_0000_0000_0000_0000, 0b_1111_1111_1111_1111, 15), 0b_0111_1111_1111_1111); 295 | /// ``` 296 | pub fn cross_u16(parent_1: u16, parent_2: u16, pos: usize) -> u16 { 297 | let size = mem::size_of::() * 8; 298 | let mask_parent_1 = !0u16 << pos; 299 | let mask_parent_2 = !0u16 >> (size - pos); 300 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 301 | } 302 | 303 | /// Single point crossing. 304 | /// 305 | /// # Parameters 306 | /// * `parent_1`, `parent_2` - parents for crossing. 307 | /// * `pos` - position for bytes exchange. The position is counted from right. 308 | /// 309 | /// Returns single child. 310 | /// 311 | ///# Examples 312 | /// 313 | /// ``` 314 | /// use optlib::genetic::cross; 315 | /// 316 | /// assert_eq!(cross::cross_i8(0b_0000_0000_i8, 0b_0111_1111_i8, 4), 0b_0000_1111_i8); 317 | /// assert_eq!(cross::cross_i8(0b_0000_0000_i8, 0b_0111_1111_i8, 1), 0b_0000_0001_i8); 318 | /// assert_eq!(cross::cross_i8(0b_0000_0000_i8, 0b_0111_1111_i8, 7), 0b_0111_1111_i8); 319 | /// assert_eq!(cross::cross_i8(0b_0000_0000_i8, 0b_0111_1111_i8, 6), 0b_0011_1111_i8); 320 | /// 321 | /// // -1i8 == 0b_1111_1111 322 | /// // -16i8 == 0b_1111_0000 323 | /// assert_eq!(cross::cross_i8(-1i8, 0i8, 4), -16i8); 324 | /// 325 | /// // -1i8 == 0b_1111_1111 326 | /// // -128i8 == 0b_1000_0000 327 | /// assert_eq!(cross::cross_i8(-1i8, 0i8, 7), -128i8); 328 | /// ``` 329 | pub fn cross_i8(parent_1: i8, parent_2: i8, pos: usize) -> i8 { 330 | let size = mem::size_of::() * 8; 331 | let mask_parent_1 = !0i8 << pos; 332 | let mask_parent_2 = std::i8::MAX >> (size - pos - 1); 333 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 334 | } 335 | 336 | /// Single point crossing. 337 | /// 338 | /// # Parameters 339 | /// * `parent_1`, `parent_2` - parents for crossing. 340 | /// * `pos` - position for bytes exchange. The position is counted from right. 341 | /// 342 | /// Returns single child. 343 | /// 344 | /// # Examples 345 | /// 346 | /// ``` 347 | /// use optlib::genetic::cross; 348 | /// 349 | /// assert_eq!(cross::cross_u8(0b_0000_0000, 0b_1111_1111, 4), 0b_0000_1111); 350 | /// assert_eq!(cross::cross_u8(0b_0000_0000, 0b_1111_1111, 1), 0b_0000_0001); 351 | /// assert_eq!(cross::cross_u8(0b_0000_0000, 0b_1111_1111, 7), 0b_0111_1111); 352 | /// ``` 353 | pub fn cross_u8(parent_1: u8, parent_2: u8, pos: usize) -> u8 { 354 | let size = mem::size_of::() * 8; 355 | let mask_parent_1 = !0u8 << pos; 356 | let mask_parent_2 = !0u8 >> (size - pos); 357 | (parent_1 & mask_parent_1) | (parent_2 & mask_parent_2) 358 | } 359 | 360 | /// Single point crossing. 361 | /// 362 | /// # Parameters 363 | /// * `parent_1`, `parent_2` - parents for crossing. 364 | /// * `pos` - position for bytes exchange. The position is counted from right. 365 | /// 366 | /// Returns single child. 367 | /// 368 | /// # Examples 369 | /// 370 | /// ``` 371 | /// use optlib::genetic::cross; 372 | /// 373 | /// assert_eq!(cross::cross_f32(0f32, f32::from_bits(std::u32::MAX), 1), f32::from_bits(0b_0001)); 374 | /// assert_eq!(cross::cross_f32(0f32, f32::from_bits(std::u32::MAX), 4), f32::from_bits(0b_1111)); 375 | /// assert_eq!(cross::cross_f32(0f32, f32::from_bits(std::u32::MAX), 30), 376 | /// f32::from_bits(0b_0011_1111_1111_1111_1111_1111_1111_1111_u32)); 377 | /// ``` 378 | pub fn cross_f32(parent_1: f32, parent_2: f32, pos: usize) -> f32 { 379 | let parent_1_bits = parent_1.to_bits(); 380 | let parent_2_bits = parent_2.to_bits(); 381 | 382 | let child_bits = cross_u32(parent_1_bits, parent_2_bits, pos); 383 | f32::from_bits(child_bits) 384 | } 385 | 386 | /// Single point crossing. 387 | /// 388 | /// # Parameters 389 | /// * `parent_1`, `parent_2` - parents for crossing. 390 | /// * `pos` - position for bytes exchange. The position is counted from right. 391 | /// 392 | /// Returns single child. 393 | /// 394 | /// # Examples 395 | /// 396 | /// ``` 397 | /// use optlib::genetic::cross; 398 | /// 399 | /// assert_eq!(cross::cross_f64(0f64, f64::from_bits(std::u64::MAX), 1), f64::from_bits(0b_0001)); 400 | /// assert_eq!(cross::cross_f64(0f64, f64::from_bits(std::u64::MAX), 4), f64::from_bits(0b_1111)); 401 | /// assert_eq!(cross::cross_f64(0f64, f64::from_bits(std::u64::MAX), 62), 402 | /// f64::from_bits(0b_0011_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_u64)); 403 | /// ``` 404 | pub fn cross_f64(parent_1: f64, parent_2: f64, pos: usize) -> f64 { 405 | let parent_1_bits = parent_1.to_bits(); 406 | let parent_2_bits = parent_2.to_bits(); 407 | 408 | let child_bits = cross_u64(parent_1_bits, parent_2_bits, pos); 409 | f64::from_bits(child_bits) 410 | } 411 | -------------------------------------------------------------------------------- /optlib/src/genetic/mutation/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with most usable algorithms of mutations for various types. 2 | //! The module contains struct which implements the `Mutation` trait to mutate 3 | //! chromosomes various types. 4 | 5 | use crate::genetic::Mutation; 6 | use rand::distributions::{Distribution, Uniform}; 7 | use rand::rngs; 8 | use rand::rngs::ThreadRng; 9 | use std::mem; 10 | 11 | /// The struct to change random bits in the chromosomes. 12 | pub struct BitwiseMutation { 13 | random: ThreadRng, 14 | change_gene_count: usize, 15 | } 16 | 17 | /// Mutation for chromosomes of Vec, where G - type of single gene. 18 | pub struct VecMutation { 19 | probability: f64, 20 | random: rngs::ThreadRng, 21 | single_mutation: Box>, 22 | } 23 | 24 | impl BitwiseMutation { 25 | /// Constructor 26 | /// 27 | /// # Parameters 28 | /// * `change_gene_count` - how many bits will changed by algorithm during mutation. 29 | pub fn new(change_gene_count: usize) -> Self { 30 | let random = rand::thread_rng(); 31 | Self { 32 | random, 33 | change_gene_count, 34 | } 35 | } 36 | } 37 | 38 | impl Mutation for BitwiseMutation { 39 | fn mutation(&mut self, gene: &f32) -> f32 { 40 | let size = mem::size_of::() * 8; 41 | let between = Uniform::new(0, size); 42 | 43 | let mut bit_value = gene.to_bits(); 44 | for _ in 0..self.change_gene_count { 45 | let pos = between.sample(&mut self.random); 46 | bit_value ^= 1 << pos; 47 | } 48 | f32::from_bits(bit_value) 49 | } 50 | } 51 | 52 | impl Mutation for BitwiseMutation { 53 | fn mutation(&mut self, gene: &f64) -> f64 { 54 | let size = mem::size_of::() * 8; 55 | let between = Uniform::new(0, size); 56 | 57 | let mut bit_value = gene.to_bits(); 58 | for _ in 0..self.change_gene_count { 59 | let pos = between.sample(&mut self.random); 60 | bit_value ^= 1 << pos; 61 | } 62 | f64::from_bits(bit_value) 63 | } 64 | } 65 | 66 | impl VecMutation { 67 | /// Constructor 68 | /// 69 | /// # Parameters 70 | /// * `probability` - probability of mutation of single gene. 71 | /// * `single_mutation` - trait object with mutation algorithm for single gene. 72 | pub fn new(probability: f64, single_mutation: Box>) -> Self { 73 | let random = rand::thread_rng(); 74 | Self { 75 | probability, 76 | random, 77 | single_mutation, 78 | } 79 | } 80 | } 81 | 82 | impl Mutation> for VecMutation { 83 | fn mutation(&mut self, chromosomes: &Vec) -> Vec { 84 | let mutate = Uniform::new(0.0, 100.0); 85 | let mut result = Vec::with_capacity(chromosomes.len()); 86 | 87 | for chromo in chromosomes { 88 | if mutate.sample(&mut self.random) < self.probability { 89 | result.push(self.single_mutation.mutation(&chromo)); 90 | } else { 91 | result.push(chromo.clone()); 92 | } 93 | } 94 | 95 | result 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /optlib/src/genetic/pairing/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with pairing algorithm traits. The pairing algorithm selects individuals for crossing. 2 | 3 | use rand::distributions::{Distribution, Uniform}; 4 | use rand::rngs::ThreadRng; 5 | 6 | use crate::Agent; 7 | use crate::genetic:: {Pairing, Population}; 8 | 9 | /// Pairing algorithm which select random individuals for crossing. 10 | pub struct RandomPairing { 11 | random: ThreadRng, 12 | } 13 | 14 | impl Pairing for RandomPairing { 15 | fn get_pairs(&mut self, population: &Population) -> Vec> { 16 | let mut pairs: Vec> = vec![]; 17 | 18 | let between = Uniform::new(0, population.len()); 19 | let count = population.len() / 2; 20 | for _ in 0..count { 21 | let first = between.sample(&mut self.random); 22 | let second = between.sample(&mut self.random); 23 | let pair = vec![first, second]; 24 | pairs.push(pair); 25 | } 26 | 27 | pairs 28 | } 29 | } 30 | 31 | impl RandomPairing { 32 | /// Constructor. 33 | pub fn new() -> Self { 34 | let random = rand::thread_rng(); 35 | Self { random } 36 | } 37 | } 38 | 39 | /// Algorithm of tournament. 40 | /// 41 | /// Every individual to cross must be best of N others random individuals. 42 | pub struct Tournament { 43 | families_count: usize, 44 | partners_count: usize, 45 | rounds_count: usize, 46 | random: ThreadRng, 47 | } 48 | 49 | impl Tournament { 50 | /// Constructor. 51 | /// 52 | /// # Parameters 53 | /// * `families_count` - families count for crossing. 54 | pub fn new(families_count: usize) -> Self { 55 | let random = rand::thread_rng(); 56 | Self { 57 | families_count, 58 | partners_count: 2, 59 | rounds_count: 1, 60 | random, 61 | } 62 | } 63 | 64 | /// Set partners count for every family. Tthe default is 2. 65 | pub fn partners_count<'a>(mut self, count: usize) -> Self { 66 | self.partners_count = count; 67 | self 68 | } 69 | 70 | /// How many competitors should an individual win? The default is 1 71 | pub fn rounds_count<'a>(mut self, count: usize) -> Self { 72 | self.rounds_count = count; 73 | self 74 | } 75 | } 76 | 77 | impl Pairing for Tournament { 78 | fn get_pairs(&mut self, population: &Population) -> Vec> { 79 | let mut pairs: Vec> = Vec::with_capacity(self.families_count); 80 | let between = Uniform::new(0, population.len()); 81 | 82 | // Loop for families count 83 | for _ in 0..self.families_count { 84 | let mut family: Vec = Vec::with_capacity(self.partners_count); 85 | 86 | // Loop for partner 87 | for _ in 0..self.partners_count { 88 | let mut best_ind_index = between.sample(&mut self.random); 89 | 90 | // Loop for tournaments rounds 91 | for _ in 0..self.rounds_count { 92 | let challenger = between.sample(&mut self.random); 93 | if population[challenger].get_goal() < population[best_ind_index].get_goal() { 94 | best_ind_index = challenger; 95 | } 96 | } 97 | family.push(best_ind_index); 98 | } 99 | 100 | pairs.push(family); 101 | } 102 | 103 | pairs 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /optlib/src/genetic/pre_birth/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with PreBirth trait implementations. 2 | 3 | pub mod vec_float; 4 | -------------------------------------------------------------------------------- /optlib/src/genetic/pre_birth/vec_float.rs: -------------------------------------------------------------------------------- 1 | //! The module with PreBirth trait implementations for chromosomes of Vec type. 2 | 3 | use num::Float; 4 | 5 | use crate::genetic::{Population, PreBirth}; 6 | 7 | /// Kill individuals if theirs gene does not lie in the specified intevals. 8 | /// 9 | /// `G` - type of gene. 10 | /// Returns count of the killed individuals. 11 | pub struct CheckChromoInterval { 12 | intervals: Vec<(G, G)>, 13 | } 14 | 15 | impl CheckChromoInterval { 16 | /// Constructor. 17 | /// 18 | /// # Parameters 19 | /// * `intervals` - allowed interval for every gene. Count of the genes and count of the 20 | /// interval must be equal. 21 | pub fn new(intervals: Vec<(G, G)>) -> Self { 22 | Self { intervals } 23 | } 24 | } 25 | 26 | impl PreBirth> for CheckChromoInterval { 27 | fn pre_birth(&mut self, _population: &Population>, new_chromosomes: &mut Vec>) { 28 | new_chromosomes.retain(|chromosomes| self.check_chromo(chromosomes)); 29 | } 30 | } 31 | 32 | impl CheckChromoInterval { 33 | fn check_chromo(&mut self, chromosomes: &Vec) -> bool { 34 | assert_eq!(chromosomes.len(), self.intervals.len()); 35 | 36 | for (chromo, interval) in chromosomes.iter().zip(self.intervals.iter()) { 37 | if !chromo.is_finite() || *chromo < interval.0 || *chromo > interval.1 { 38 | return false; 39 | } 40 | } 41 | 42 | true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /optlib/src/genetic/selection/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with selection algorithms. 2 | //! 3 | //! The algoritms must kill individuals which does not go to 4 | //! next generation. The algorithm must call `kill()` method for such individuals. 5 | 6 | pub mod vec_float; 7 | 8 | use crate::genetic::{Population, Selection}; 9 | 10 | /// Kill individuals if value of theirs fitness (goal function) is NaN. 11 | /// Returns count of killed individuals. 12 | pub struct KillFitnessNaN; 13 | 14 | impl Selection for KillFitnessNaN { 15 | fn kill(&mut self, population: &mut Population) { 16 | for individual in population.iter_mut() { 17 | if !individual.get_fitness().is_finite() { 18 | individual.kill(); 19 | } 20 | } 21 | } 22 | } 23 | 24 | impl KillFitnessNaN { 25 | /// Constructor. 26 | pub fn new() -> Self { 27 | Self 28 | } 29 | } 30 | 31 | pub struct LimitPopulation { 32 | max_count: usize, 33 | } 34 | 35 | impl LimitPopulation { 36 | /// Constructor. 37 | pub fn new(max_count: usize) -> Self { 38 | Self { max_count } 39 | } 40 | 41 | pub fn set_limit(&mut self, max_count: usize) { 42 | self.max_count = max_count; 43 | } 44 | } 45 | 46 | impl Selection for LimitPopulation { 47 | fn kill(&mut self, population: &mut Population) { 48 | let alive_count = population.len_alive(); 49 | if alive_count > self.max_count { 50 | kill_worst(population, alive_count - self.max_count); 51 | } 52 | } 53 | } 54 | 55 | /// Function to kill worst individuals in population. 56 | /// `count` - how many individuals must be killed. 57 | pub fn kill_worst(population: &mut Population, count: usize) { 58 | // List of indexes of individuals in population to be kill 59 | let mut kill_list: Vec = Vec::with_capacity(count); 60 | kill_list.push(0); 61 | 62 | // Index of the items in kill_list with best fitness 63 | let mut best_index = 0; 64 | let mut best_fitness = population[kill_list[best_index]].get_fitness(); 65 | 66 | for n in 1..population.len() { 67 | if !population[n].is_alive() { 68 | continue; 69 | } 70 | 71 | if kill_list.len() < count { 72 | kill_list.push(n); 73 | if population[n].get_fitness() < best_fitness { 74 | best_index = kill_list.len() - 1; 75 | } 76 | } else { 77 | if population[n].get_fitness() > best_fitness { 78 | kill_list[best_index] = n; 79 | 80 | // Find new best item 81 | best_index = 0; 82 | best_fitness = population[kill_list[best_index]].get_fitness(); 83 | for m in 1..kill_list.len() { 84 | if population[kill_list[m]].get_fitness() < best_fitness { 85 | best_index = m; 86 | best_fitness = population[kill_list[best_index]].get_fitness(); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | for n in kill_list { 94 | population[n].kill(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /optlib/src/genetic/selection/vec_float.rs: -------------------------------------------------------------------------------- 1 | //! The module with selection algorithms for type chromosomes of Vec. 2 | 3 | use num::Float; 4 | 5 | use crate::genetic::{Population, Selection}; 6 | 7 | /// Kill individuals if theirs gene does not lie in the specified intevals. 8 | /// 9 | /// `G` - type of gene. 10 | /// Returns count of the killed individuals. 11 | pub struct CheckChromoInterval { 12 | intervals: Vec<(G, G)>, 13 | } 14 | 15 | impl CheckChromoInterval { 16 | /// Constructor. 17 | /// 18 | /// # Parameters 19 | /// * `intervals` - allowed interval for every gene. Count of the genes and count of the 20 | /// interval must be equal. 21 | pub fn new(intervals: Vec<(G, G)>) -> Self { 22 | Self { intervals } 23 | } 24 | } 25 | 26 | impl Selection> for CheckChromoInterval { 27 | fn kill(&mut self, population: &mut Population>) { 28 | for individual in population.iter_mut() { 29 | assert_eq!(individual.get_chromosomes().len(), self.intervals.len()); 30 | 31 | for (chromo, interval) in individual 32 | .get_chromosomes() 33 | .iter() 34 | .zip(self.intervals.iter()) 35 | { 36 | if !chromo.is_finite() || *chromo < interval.0 || *chromo > interval.1 { 37 | individual.kill(); 38 | break; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /optlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The crate for global optimization algorithms. 2 | //! The crate uses common traits for easy switch between algorithms. 3 | extern crate num; 4 | 5 | pub mod genetic; 6 | pub mod particleswarm; 7 | pub mod tools; 8 | 9 | type GoalValue = f64; 10 | 11 | /// First item is current solution in search space, 12 | /// second item is current goal value 13 | type Solution = (T, GoalValue); 14 | 15 | /// Common Optimizer trait. 16 | /// 17 | /// `T` - type of a point in search space for goal function. 18 | pub trait Optimizer { 19 | /// Run an algorithm. 20 | /// 21 | /// Returns `Some((x: &T, goal: GoalValue))`, where `x` - result of optimization, 22 | /// `goal` - value of goal function. Returns `None` if an algoritm can't find minimum of a goal function. 23 | /// 24 | /// # Remarks 25 | /// All algorithms with `Optimizer` must search minimum of a goal function. 26 | fn find_min(&mut self) -> Option>; 27 | } 28 | 29 | /// The trait for iterative algorithms. 30 | /// 31 | /// `T` - type of a point in search space for goal function. 32 | pub trait IterativeOptimizer { 33 | /// The method can be called after algorithm initialization. 34 | fn next_iterations(&mut self) -> Option>; 35 | } 36 | 37 | /// The trait for a struct with information about current algorithm state. 38 | /// For example: population for genetic algorithm, swarm for particle swarm algorithm etc 39 | /// 40 | /// `T` - type of a point in search space for goal function. 41 | pub trait AlgorithmState { 42 | fn get_best_solution(&self) -> Option>; 43 | fn get_iteration(&self) -> usize; 44 | } 45 | 46 | /// The trait for algotithms where use agents (genetic algorithm, partical swarm algorithm etc). 47 | /// 48 | /// `T` - type of a point in search space for goal function. 49 | pub trait AgentsState: AlgorithmState { 50 | type Agent: Agent; 51 | 52 | /// Returns vector with references to all agents 53 | fn get_agents(&self) -> Vec<&Self::Agent>; 54 | } 55 | 56 | /// The trait for single point in search space. The trait used with `AlgorithmWithAgents`. 57 | /// 58 | /// `T` - type of a point in search space for goal function. 59 | pub trait Agent { 60 | /// Returns parameter (point in search space) of the agent. 61 | fn get_parameter(&self) -> &T; 62 | 63 | /// Returns value of a goal function for current agent. 64 | fn get_goal(&self) -> GoalValue; 65 | } 66 | 67 | /// The trait for the goal function. 68 | pub trait Goal { 69 | /// Must return value of goal function for the point in the search space (x). 70 | fn get(&mut self, x: &T) -> GoalValue; 71 | } 72 | 73 | /// Struct to convert (wrap) function to `Goal` trait. 74 | pub struct GoalFromFunction { 75 | function: fn(&T) -> GoalValue, 76 | } 77 | 78 | impl GoalFromFunction { 79 | /// Constructor. 80 | pub fn new(function: fn(&T) -> GoalValue) -> Self { 81 | Self { function } 82 | } 83 | } 84 | 85 | impl Goal for GoalFromFunction { 86 | fn get(&mut self, x: &T) -> GoalValue { 87 | (self.function)(x) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /optlib/src/particleswarm/initializing/mod.rs: -------------------------------------------------------------------------------- 1 | use num::{NumCast, Zero}; 2 | 3 | use crate::tools::RandomVectorCreator; 4 | use crate::particleswarm::{CoordinatesInitializer, VelocityInitializer}; 5 | 6 | /// The struct to initialize particles coordinates with random value from given intervals. 7 | pub struct RandomCoordinatesInitializer { 8 | // Intervals for every dimension. Size of the vector must be equal to dimension. 9 | // The first value in tuple is minimum value, the second value is maximum value. 10 | intervals: Vec<(T, T)>, 11 | particles_count: usize, 12 | vector_creator: RandomVectorCreator, 13 | } 14 | 15 | impl RandomCoordinatesInitializer { 16 | /// Constructor. 17 | /// 18 | /// # Parameters 19 | /// `intervals` - vector of tuples. Size of the vector must be equal to dimension. The first value in tuple is minimum coordinate, the second value is maximum coordinate. 20 | /// `particles_count` - how many particles do you need to create. 21 | pub fn new(intervals: Vec<(T, T)>, particles_count: usize) -> Self { 22 | Self { 23 | intervals, 24 | particles_count, 25 | vector_creator: RandomVectorCreator::new(), 26 | } 27 | } 28 | } 29 | 30 | impl CoordinatesInitializer for RandomCoordinatesInitializer { 31 | fn get_coordinates(&mut self) -> Vec> { 32 | (0..self.particles_count) 33 | .map(|_| self.vector_creator.create_vec(&self.intervals)) 34 | .collect() 35 | } 36 | } 37 | 38 | /// The struct to initialze particles velocity with random velocity 39 | pub struct RandomVelocityInitializer { 40 | intervals: Vec<(T, T)>, 41 | particles_count: usize, 42 | vector_creator: RandomVectorCreator, 43 | } 44 | 45 | impl RandomVelocityInitializer { 46 | /// Constructor. 47 | /// 48 | /// # Parameters 49 | /// `intervals` - vector of tuples. Size of the vector must be equal to dimension. The first value in tuple is minimum velocity, the second value is maximum seed. 50 | /// `particles_count` - how many particles do you need to create. 51 | pub fn new(intervals: Vec<(T, T)>, particles_count: usize) -> Self { 52 | Self { 53 | intervals, 54 | particles_count, 55 | vector_creator: RandomVectorCreator::new(), 56 | } 57 | } 58 | } 59 | 60 | impl VelocityInitializer for RandomVelocityInitializer { 61 | fn get_velocity(&mut self) -> Vec> { 62 | (0..self.particles_count) 63 | .map(|_| self.vector_creator.create_vec(&self.intervals)) 64 | .collect() 65 | } 66 | } 67 | 68 | /// The struct to initialize particles velocity with zeros. 69 | pub struct ZeroVelocityInitializer { 70 | dimension: usize, 71 | particles_count: usize, 72 | } 73 | 74 | impl ZeroVelocityInitializer { 75 | pub fn new(dimension: usize, particles_count: usize) -> Self { 76 | Self { dimension, particles_count } 77 | } 78 | } 79 | 80 | impl VelocityInitializer for ZeroVelocityInitializer { 81 | fn get_velocity(&mut self) -> Vec> { 82 | (0..self.particles_count) 83 | .map(|_| vec![T::zero(); self.dimension]) 84 | .collect() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /optlib/src/particleswarm/postmove/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::particleswarm::PostMove; 2 | 3 | use num::Float; 4 | use rand::distributions::uniform::SampleUniform; 5 | use rand::distributions::{Distribution, Uniform}; 6 | use rand::rngs::ThreadRng; 7 | 8 | /// The struct to limit the coordinates of particle. 9 | pub struct MoveToBoundary { 10 | intervals: Vec<(T, T)>, 11 | } 12 | 13 | impl MoveToBoundary { 14 | /// Constructor. 15 | /// 16 | /// # Parameters 17 | /// `intervals` - `intervals` - vector of tuples. Size of the vector must be equal to dimension. The first value in tuple is minimum coordinate, the second value is maximum coordinate. 18 | pub fn new(intervals: Vec<(T, T)>) -> Self { 19 | Self { intervals } 20 | } 21 | } 22 | 23 | impl PostMove for MoveToBoundary { 24 | fn post_move(&mut self, coordinates: &mut Vec) { 25 | assert_eq!(coordinates.len(), self.intervals.len()); 26 | 27 | for i in 0..coordinates.len() { 28 | if !coordinates[i].is_finite() { 29 | coordinates[i] = self.intervals[i].0; 30 | } 31 | 32 | if coordinates[i] < self.intervals[i].0 { 33 | coordinates[i] = self.intervals[i].0; 34 | } 35 | 36 | if coordinates[i] > self.intervals[i].1 { 37 | coordinates[i] = self.intervals[i].1; 38 | } 39 | } 40 | } 41 | } 42 | 43 | /// The struct to move particle to random position with given probability 44 | pub struct RandomTeleport { 45 | intervals: Vec<(T, T)>, 46 | probability: f32, 47 | random: ThreadRng, 48 | random_intervals: Vec>, 49 | } 50 | 51 | impl RandomTeleport { 52 | /// Constructor. 53 | /// 54 | /// # Parameters 55 | /// `intervals` - `intervals` - vector of tuples. Size of the vector must be equal to dimension. The first value in tuple is minimum coordinate, the second value is maximum coordinate. 56 | /// `probability` - probability of particle teleportation. Must be in the range [0, 1]. 57 | pub fn new(intervals: Vec<(T, T)>, probability: f32) -> Self { 58 | assert!(probability >= 0_f32); 59 | assert!(probability <= 1_f32); 60 | let random_intervals = intervals 61 | .iter() 62 | .map(|(min, max)| Uniform::new_inclusive(min, max)) 63 | .collect(); 64 | Self { 65 | intervals, 66 | probability, 67 | random: rand::thread_rng(), 68 | random_intervals, 69 | } 70 | } 71 | } 72 | 73 | impl PostMove for RandomTeleport { 74 | fn post_move(&mut self, coordinates: &mut Vec) { 75 | assert_eq!(coordinates.len(), self.intervals.len()); 76 | 77 | let rnd = Uniform::new(0.0_f32, 1.0_f32).sample(&mut self.random); 78 | let teleport = self.probability > rnd; 79 | if teleport { 80 | for i in 0..coordinates.len() { 81 | coordinates[i] = self.random_intervals[i].sample(&mut self.random); 82 | } 83 | } 84 | } 85 | } 86 | 87 | #[cfg(test)] 88 | mod tests { 89 | use crate::particleswarm::{postmove::MoveToBoundary, PostMove}; 90 | use num::abs; 91 | 92 | #[test] 93 | fn test_empty() { 94 | let intervals: Vec<(f32, f32)> = vec![]; 95 | let mut coordinates = vec![]; 96 | 97 | let mut postmove = MoveToBoundary::new(intervals); 98 | postmove.post_move(&mut coordinates); 99 | } 100 | 101 | #[test] 102 | fn test_inside_interval() { 103 | let intervals = vec![(0.0_f32, 1.0_f32)]; 104 | let mut coordinates = vec![0.6_f32]; 105 | 106 | let mut postmove = MoveToBoundary::new(intervals); 107 | postmove.post_move(&mut coordinates); 108 | 109 | assert!(abs(coordinates[0] - 0.6_f32) < 1e-6); 110 | } 111 | 112 | #[test] 113 | fn test_min() { 114 | let intervals = vec![(0.0_f32, 1.0_f32)]; 115 | let mut coordinates = vec![-1.0_f32]; 116 | 117 | let mut postmove = MoveToBoundary::new(intervals); 118 | postmove.post_move(&mut coordinates); 119 | 120 | assert!(abs(coordinates[0]) < 1e-6); 121 | } 122 | 123 | #[test] 124 | fn test_max() { 125 | let intervals = vec![(0.0_f32, 1.0_f32)]; 126 | let mut coordinates = vec![3.0_f32]; 127 | 128 | let mut postmove = MoveToBoundary::new(intervals); 129 | postmove.post_move(&mut coordinates); 130 | 131 | assert!(abs(coordinates[0] - 1.0_f32) < 1e-6); 132 | } 133 | 134 | #[test] 135 | fn test_mix() { 136 | let intervals = vec![(0.0_f32, 1.0_f32), (1.0_f32, 3.0_f32), (5.0_f32, 10.0_f32)]; 137 | let mut coordinates = vec![-3.0_f32, 4.0_f32, 6.0_f32]; 138 | 139 | let mut postmove = MoveToBoundary::new(intervals); 140 | postmove.post_move(&mut coordinates); 141 | 142 | assert!(abs(coordinates[0] - 0.0_f32) < 1e-6); 143 | assert!(abs(coordinates[1] - 3.0_f32) < 1e-6); 144 | assert!(abs(coordinates[2] - 6.0_f32) < 1e-6); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /optlib/src/particleswarm/postvelocitycalc/mod.rs: -------------------------------------------------------------------------------- 1 | use num::Float; 2 | 3 | use crate::particleswarm::PostVelocityCalc; 4 | 5 | /// The trait to restrict value for every dimension of velocity 6 | pub struct MaxVelocityDimensions { 7 | max_velocity: Vec, 8 | } 9 | 10 | impl MaxVelocityDimensions { 11 | pub fn new(max_velocity: Vec) -> Self { 12 | Self { max_velocity } 13 | } 14 | } 15 | 16 | impl PostVelocityCalc for MaxVelocityDimensions { 17 | fn correct_velocity(&mut self, velocity: Vec) -> Vec { 18 | assert_eq!(velocity.len(), self.max_velocity.len()); 19 | 20 | let new_velocity = velocity 21 | .iter() 22 | .zip(self.max_velocity.iter()) 23 | .map(|(v, v_max)| { 24 | if v.abs() <= *v_max { 25 | *v 26 | } else { 27 | v_max.abs() * v.signum() 28 | } 29 | }) 30 | .collect(); 31 | 32 | new_velocity 33 | } 34 | } 35 | 36 | /// The trait to restrict absolute velocity 37 | pub struct MaxVelocityAbs { 38 | max_velocity: T, 39 | } 40 | 41 | impl MaxVelocityAbs { 42 | pub fn new(max_velocity: T) -> Self { 43 | Self { max_velocity } 44 | } 45 | } 46 | 47 | impl PostVelocityCalc for MaxVelocityAbs { 48 | fn correct_velocity(&mut self, velocity: Vec) -> Vec { 49 | let current_velocity_abs = velocity 50 | .iter() 51 | .fold(T::zero(), |acc, vi| acc + (*vi) * (*vi)) 52 | .sqrt(); 53 | if current_velocity_abs > self.max_velocity { 54 | let new_velocity = velocity 55 | .iter() 56 | .map(|vi| (*vi) * self.max_velocity / current_velocity_abs) 57 | .collect(); 58 | new_velocity 59 | } else { 60 | velocity 61 | } 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use num::abs; 68 | use super::{MaxVelocityAbs, MaxVelocityDimensions}; 69 | use crate::particleswarm::PostVelocityCalc; 70 | 71 | #[test] 72 | fn test_max_velocity_dimensions_empty() { 73 | let max_velocity: Vec = vec![]; 74 | let velocity: Vec = vec![]; 75 | 76 | let mut post_velocity = MaxVelocityDimensions::new(max_velocity); 77 | let new_velocity = post_velocity.correct_velocity(velocity); 78 | 79 | assert_eq!(new_velocity, vec![]); 80 | } 81 | 82 | #[test] 83 | #[should_panic] 84 | fn test_max_velocity_invalid_size() { 85 | let max_velocity: Vec = vec![2.0_f32]; 86 | let velocity: Vec = vec![]; 87 | 88 | let mut post_velocity = MaxVelocityDimensions::new(max_velocity); 89 | post_velocity.correct_velocity(velocity); 90 | } 91 | 92 | #[test] 93 | fn test_max_velocity_dimensions_not_change() { 94 | let max_velocity: Vec = vec![10.0_f32, 10.0_f32]; 95 | let velocity: Vec = vec![5.0_f32, -5.0_f32]; 96 | 97 | let mut post_velocity = MaxVelocityDimensions::new(max_velocity); 98 | let new_velocity = post_velocity.correct_velocity(velocity.clone()); 99 | 100 | assert_eq!(new_velocity, velocity); 101 | } 102 | 103 | #[test] 104 | fn test_max_velocity_dimensions_fix() { 105 | let max_velocity: Vec = vec![10.0_f32, 10.0_f32, 10.0_f32]; 106 | let velocity: Vec = vec![15.0_f32, -15.0_f32, 5.0_f32]; 107 | 108 | let mut post_velocity = MaxVelocityDimensions::new(max_velocity); 109 | let new_velocity = post_velocity.correct_velocity(velocity.clone()); 110 | 111 | assert_eq!(new_velocity, vec![10.0_f32, -10.0_f32, 5.0_f32]); 112 | } 113 | 114 | #[test] 115 | fn test_max_velocity_abs_empty() { 116 | let max_velocity = 10.0_f32; 117 | let velocity: Vec = vec![]; 118 | 119 | let mut post_velocity = MaxVelocityAbs::new(max_velocity); 120 | let new_velocity = post_velocity.correct_velocity(velocity); 121 | 122 | assert_eq!(new_velocity, vec![]); 123 | } 124 | 125 | #[test] 126 | fn test_max_velocity_abs_not_change() { 127 | let max_velocity = 10.0_f32; 128 | let velocity: Vec = vec![5.0_f32, -5.0_f32]; 129 | 130 | let mut post_velocity = MaxVelocityAbs::new(max_velocity); 131 | let new_velocity = post_velocity.correct_velocity(velocity.clone()); 132 | 133 | assert_eq!(new_velocity, velocity); 134 | } 135 | 136 | #[test] 137 | fn test_max_velocity_abs_change() { 138 | let max_velocity = 10.0_f32; 139 | let velocity: Vec = vec![15.0_f32, -15.0_f32]; 140 | 141 | let mut post_velocity = MaxVelocityAbs::new(max_velocity); 142 | let new_velocity = post_velocity.correct_velocity(velocity.clone()); 143 | let new_velocity_abs = (new_velocity[0] * new_velocity[0] + 144 | new_velocity[1] * new_velocity[1]).sqrt(); 145 | 146 | assert!(abs(new_velocity_abs - max_velocity) < 1e-3); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /optlib/src/particleswarm/velocitycalc/mod.rs: -------------------------------------------------------------------------------- 1 | use rand::distributions::{Distribution, Uniform}; 2 | use rand::rngs::ThreadRng; 3 | 4 | use num::{Float, Num, NumCast}; 5 | 6 | use crate::particleswarm::{Particle, VelocityCalculator, Swarm}; 7 | 8 | /// ClassicVelocityCalculator implements the equation from the article 9 | /// Kennedy, J.; Eberhart, R. (1995). "Particle Swarm Optimization". 10 | /// Proceedings of IEEE International Conference on Neural Networks IV, pp.1942-1948. 11 | /// v_i = v_i + phi_p * r_p * (p_i - x_i) + phi_g * r_g * (g_i - x_i) 12 | /// `v_i` - velocity projection for dimension i, 13 | /// `p_i` - personal best coordinate, 14 | /// `g_i` - global best coordinate, 15 | /// `x_i` - current coordinate, 16 | /// `phi_p`, `phi_g` - parameters, 17 | /// `r_p`, `r_g` - random values in (0, 1) 18 | pub struct ClassicVelocityCalculator { 19 | phi_personal: T, 20 | phi_global: T, 21 | 22 | random: ThreadRng, 23 | } 24 | 25 | impl ClassicVelocityCalculator { 26 | pub fn new(phi_personal: T, phi_global: T) -> Self { 27 | Self { 28 | phi_personal, 29 | phi_global, 30 | random: rand::thread_rng(), 31 | } 32 | } 33 | } 34 | 35 | impl VelocityCalculator for ClassicVelocityCalculator { 36 | fn calc_new_velocity(&mut self, swarm: &Swarm, particle: &Particle) -> Vec { 37 | let dimension = particle.coordinates.len(); 38 | let global_best_particle = swarm.best_particle.as_ref().unwrap(); 39 | let global_best_solution = &global_best_particle.coordinates; 40 | 41 | let between = Uniform::new_inclusive(0.0_f32, 1.0_f32); 42 | let mut new_velocity = Vec::with_capacity(dimension); 43 | for i in 0..dimension { 44 | let r_personal = T::from(between.sample(&mut self.random)).unwrap(); 45 | let r_global = T::from(between.sample(&mut self.random)).unwrap(); 46 | 47 | let velocity_item = particle.velocity[i] 48 | + self.phi_personal 49 | * r_personal 50 | * (particle.best_personal_coordinates[i] - particle.coordinates[i]) 51 | + self.phi_global * r_global * (global_best_solution[i] - particle.coordinates[i]); 52 | new_velocity.push(velocity_item); 53 | } 54 | 55 | new_velocity 56 | } 57 | } 58 | 59 | /// CanonicalVelocityCalculator implements the "canonical" equation: 60 | /// v_i = xi * (v_i + phi_p * r_p * (p_i - x_i) + phi_g * r_g * (g_i - x_i)) 61 | /// `v_i` - velocity projection for dimension i, 62 | /// `p_i` - personal best coordinate, 63 | /// `g_i` - global best coordinate, 64 | /// `x_i` - current coordinate, 65 | /// `phi_p`, `phi_g` - parameters, 66 | /// `r_p`, `r_g` - random values in (0, 1), 67 | /// `xi` = 2 * alpha / (phi - 2), 68 | /// `phi` = phi_p + phi_g 69 | /// `alpha` in (0, 1), 70 | /// `phi` must be greater than 4 71 | pub struct CanonicalVelocityCalculator { 72 | phi_personal: T, 73 | phi_global: T, 74 | xi: T, 75 | 76 | random: ThreadRng, 77 | } 78 | 79 | impl CanonicalVelocityCalculator { 80 | pub fn new(phi_personal: T, phi_global: T, alpha: T) -> Self { 81 | assert!(phi_personal + phi_global > T::from(4.0).unwrap()); 82 | assert!(alpha > T::zero()); 83 | assert!(alpha < T::one()); 84 | 85 | let phi = phi_global + phi_personal; 86 | let xi = T::from(2.0).unwrap() * alpha / (phi - T::from(2.0).unwrap()); 87 | Self { 88 | phi_personal, 89 | phi_global, 90 | xi, 91 | random: rand::thread_rng(), 92 | } 93 | } 94 | } 95 | 96 | impl VelocityCalculator for CanonicalVelocityCalculator { 97 | fn calc_new_velocity(&mut self, swarm: &Swarm, particle: &Particle) -> Vec { 98 | let dimension = particle.coordinates.len(); 99 | let global_best_particle = swarm.best_particle.as_ref().unwrap(); 100 | let global_best_solution = &global_best_particle.coordinates; 101 | 102 | let between = Uniform::new_inclusive(0.0_f32, 1.0_f32); 103 | let mut new_velocity = Vec::with_capacity(dimension); 104 | for i in 0..dimension { 105 | let r_personal = T::from(between.sample(&mut self.random)).unwrap(); 106 | let r_global = T::from(between.sample(&mut self.random)).unwrap(); 107 | 108 | let velocity_item = self.xi 109 | * (particle.velocity[i] 110 | + self.phi_personal 111 | * r_personal 112 | * (particle.best_personal_coordinates[i] - particle.coordinates[i]) 113 | + self.phi_global 114 | * r_global 115 | * (global_best_solution[i] - particle.coordinates[i])); 116 | new_velocity.push(velocity_item); 117 | } 118 | 119 | new_velocity 120 | } 121 | } 122 | 123 | /// Velocity update with negative reinforcement, global and current best and worst positions. 124 | /// v_i = xi * (v_i 125 | /// + phi_best_personal * rb_p * (p_best_i - x_i) 126 | /// + phi_best_current * rb_c * (c_best_i - x_i) 127 | /// + phi_best_global * rb_g * (g_best_i - x_i) 128 | /// - phi_worst_personal * rw_p * (p_worst_i - x_i) 129 | /// - phi_worst_current * rw_c * (c_worst_i - x_i) 130 | /// - phi_worst_global * rw_g * (g_worst_i - x_i)) 131 | /// 132 | /// `v_i` - velocity projection for dimension i, 133 | /// `p_best_i` - personal best coordinate, 134 | /// `c_best_i` - best coordinate for current swarm, 135 | /// `g_best_i` - global best coordinate, 136 | /// `p_worst_i` - personal worst coordinate, 137 | /// `c_worst_i` - worst coordinate for current swarm, 138 | /// `g_worst_i` - global worst coordinate, 139 | /// `xi` - parameter, 140 | /// `x_i` - current coordinate, 141 | /// `phi_best_personal`, `phi_best_current`, `phi_best_global`, `phi_worst_personal`, `phi_worst_current`, `phi_worst_global` - parameters, 142 | /// `rb_p`, `rb_c`, `rb_g`, `rw_p`, `rw_c`, `rw_g` - random values in (0, 1), 143 | pub struct NegativeReinforcement { 144 | phi_best_personal: T, 145 | phi_best_current: T, 146 | phi_best_global: T, 147 | 148 | phi_worst_personal: T, 149 | phi_worst_current: T, 150 | phi_worst_global: T, 151 | 152 | xi: T, 153 | 154 | random: ThreadRng, 155 | } 156 | impl NegativeReinforcement { 157 | pub fn new( 158 | phi_best_personal: T, 159 | phi_best_current: T, 160 | phi_best_global: T, 161 | phi_worst_personal: T, 162 | phi_worst_current: T, 163 | phi_worst_global: T, 164 | xi: T, 165 | ) -> Self { 166 | Self { 167 | phi_best_personal, 168 | phi_best_current, 169 | phi_best_global, 170 | phi_worst_personal, 171 | phi_worst_current, 172 | phi_worst_global, 173 | xi, 174 | random: rand::thread_rng(), 175 | } 176 | } 177 | } 178 | 179 | impl VelocityCalculator for NegativeReinforcement { 180 | fn calc_new_velocity(&mut self, swarm: &Swarm, particle: &Particle) -> Vec { 181 | let dimension = particle.coordinates.len(); 182 | 183 | let global_best_particle = swarm.best_particle.as_ref().unwrap(); 184 | let global_best_solution = &global_best_particle.coordinates; 185 | 186 | let current_best_particle = swarm.get_current_best_particle().unwrap(); 187 | let current_best_solution = current_best_particle.coordinates; 188 | 189 | let global_worst_particle = swarm.worst_particle.as_ref().unwrap(); 190 | let global_worst_solution = &global_worst_particle.coordinates; 191 | 192 | let current_worst_particle = swarm.get_current_worst_particle().unwrap(); 193 | let current_worst_solution = current_worst_particle.coordinates; 194 | 195 | let between = Uniform::new_inclusive(0.0, 1.0); 196 | let mut new_velocity = Vec::with_capacity(dimension); 197 | for i in 0..dimension { 198 | let r_best_global = T::from(between.sample(&mut self.random)).unwrap(); 199 | let r_best_current = T::from(between.sample(&mut self.random)).unwrap(); 200 | let r_best_personal = T::from(between.sample(&mut self.random)).unwrap(); 201 | 202 | let r_worst_global = T::from(between.sample(&mut self.random)).unwrap(); 203 | let r_worst_current = T::from(between.sample(&mut self.random)).unwrap(); 204 | let r_worst_personal = T::from(between.sample(&mut self.random)).unwrap(); 205 | 206 | let v_best_personal = self.phi_best_personal 207 | * r_best_personal 208 | * (particle.best_personal_coordinates[i] - particle.coordinates[i]); 209 | let v_best_current = self.phi_best_current 210 | * r_best_current 211 | * (current_best_solution[i] - particle.coordinates[i]); 212 | let v_best_global = self.phi_best_global 213 | * r_best_global 214 | * (global_best_solution[i] - particle.coordinates[i]); 215 | 216 | let v_worst_personal = self.phi_worst_personal 217 | * r_worst_personal 218 | * (particle.worst_personal_coordinates[i] - particle.coordinates[i]); 219 | let v_worst_current = self.phi_worst_current 220 | * r_worst_current 221 | * (current_worst_solution[i] - particle.coordinates[i]); 222 | let v_worst_global = self.phi_worst_global 223 | * r_worst_global 224 | * (global_worst_solution[i] - particle.coordinates[i]); 225 | 226 | let velocity_item = self.xi 227 | * (particle.velocity[i] + v_best_personal + v_best_current + v_best_global 228 | - v_worst_personal 229 | - v_worst_current 230 | - v_worst_global); 231 | new_velocity.push(velocity_item); 232 | } 233 | 234 | new_velocity 235 | } 236 | } 237 | 238 | /// The trait to calculate the inertia coefficient (w) for InertiaVelocityCalculator 239 | pub trait Inertia { 240 | fn get(&mut self, iteration: usize) -> T; 241 | } 242 | 243 | 244 | /// The inertia coefficient (w) does not depend on the iteration number 245 | pub struct ConstInertia { 246 | w: T, 247 | } 248 | 249 | impl ConstInertia { 250 | pub fn new(w: T) -> Self { 251 | Self { w } 252 | } 253 | } 254 | 255 | impl Inertia for ConstInertia { 256 | fn get(&mut self, _iteration: usize) -> T { 257 | self.w.clone() 258 | } 259 | } 260 | 261 | /// The inertia coefficient decreases linearly from w_max to w_min 262 | pub struct LinearInertia { 263 | w_min: T, 264 | w_max: T, 265 | t_max: usize, 266 | } 267 | 268 | impl LinearInertia { 269 | pub fn new(w_min: T, w_max: T, t_max: usize) -> Self { 270 | Self { 271 | w_min, 272 | w_max, 273 | t_max, 274 | } 275 | } 276 | } 277 | 278 | impl Inertia for LinearInertia { 279 | fn get(&mut self, iteration: usize) -> T { 280 | self.w_max 281 | - (self.w_max - self.w_min) * T::from(iteration).unwrap() / T::from(self.t_max).unwrap() 282 | } 283 | } 284 | 285 | /// InertiaVelocityCalculator implements the equation with itertia coefficient w(t) 286 | /// v_i = w(t) * v_i + phi_personal * r_p * (p_i - x_i) + phi_global * r_g * (g_i - x_i) 287 | /// `v_i` - velocity projection for dimension i, 288 | /// `p_i` - personal best coordinate, 289 | /// `g_i` - global best coordinate, 290 | /// `x_i` - current coordinate, 291 | /// `phi_personal`, `phi_global` - parameters, 292 | /// `r_p`, `r_g` - random values in (0, 1), 293 | /// `w(t)` calculate with the `Inertia` trait, 294 | /// `t` - iteration number, 295 | pub struct InertiaVelocityCalculator<'a, T> { 296 | phi_personal: T, 297 | phi_global: T, 298 | inertia: Box + 'a>, 299 | 300 | random: ThreadRng, 301 | } 302 | 303 | impl<'a, T> InertiaVelocityCalculator<'a, T> { 304 | pub fn new(phi_personal: T, phi_global: T, inertia: Box + 'a>) -> Self { 305 | Self { 306 | phi_personal, 307 | phi_global, 308 | inertia, 309 | random: rand::thread_rng(), 310 | } 311 | } 312 | } 313 | 314 | impl<'a, T: NumCast + Num + Copy> VelocityCalculator for InertiaVelocityCalculator<'a, T> { 315 | fn calc_new_velocity(&mut self, swarm: &Swarm, particle: &Particle) -> Vec { 316 | let dimension = particle.coordinates.len(); 317 | let global_best_particle = swarm.best_particle.as_ref().unwrap(); 318 | let global_best_solution = &global_best_particle.coordinates; 319 | let inertia_ratio = self.inertia.get(swarm.iteration); 320 | 321 | let between = Uniform::new_inclusive(0.0_f32, 1.0_f32); 322 | let mut new_velocity = Vec::with_capacity(dimension); 323 | for i in 0..dimension { 324 | let r_personal = T::from(between.sample(&mut self.random)).unwrap(); 325 | let r_global = T::from(between.sample(&mut self.random)).unwrap(); 326 | 327 | let velocity_item = inertia_ratio * particle.velocity[i] 328 | + self.phi_personal 329 | * r_personal 330 | * (particle.best_personal_coordinates[i] - particle.coordinates[i]) 331 | + self.phi_global * r_global * (global_best_solution[i] - particle.coordinates[i]); 332 | new_velocity.push(velocity_item); 333 | } 334 | 335 | new_velocity 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /optlib/src/tools/logging/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module with the loggers ready for using. The loggers implements the `Logger` trait. 2 | 3 | use std::fmt::Display; 4 | use std::io; 5 | use std::time; 6 | 7 | use crate::AlgorithmState; 8 | 9 | /// The logging trait for algorithm with the agents. 10 | /// 11 | /// `T` - type of a point in the search space for goal function. 12 | pub trait Logger { 13 | /// Will be called after algorithm initializing. 14 | fn start(&mut self, _state: &dyn AlgorithmState) {} 15 | 16 | /// Will be called before run algorithm (possibly after result algorithm after pause). 17 | fn resume(&mut self, _state: &dyn AlgorithmState) {} 18 | 19 | /// Will be called in the end of iteration. 20 | fn next_iteration(&mut self, _state: &dyn AlgorithmState) {} 21 | 22 | /// Will be called when algorithm will be stopped. 23 | fn finish(&mut self, _state: &dyn AlgorithmState) {} 24 | 25 | // fn as_any(&self) -> &dyn Any; 26 | } 27 | 28 | /// The logger prints out current solution and goal function for every iteration. 29 | pub struct VerboseLogger<'a> { 30 | writer: &'a mut dyn io::Write, 31 | precision: usize, 32 | } 33 | 34 | impl<'a> VerboseLogger<'a> { 35 | /// Constructor 36 | /// 37 | /// # Parameters 38 | /// * `precision` - count of the digits after comma for float numbers. 39 | pub fn new(writer: &'a mut dyn io::Write, precision: usize) -> Self { 40 | Self { writer, precision } 41 | } 42 | } 43 | 44 | impl<'a, T: Display> Logger> for VerboseLogger<'a> { 45 | fn next_iteration(&mut self, state: &dyn AlgorithmState>) { 46 | if let Some((solution, goal)) = state.get_best_solution() { 47 | let mut result = String::new(); 48 | result = result + &format!("{:<8}", state.get_iteration()); 49 | 50 | for x in solution { 51 | result = result + &format!(" {:<20.*}", self.precision, x); 52 | } 53 | result = result + &format!(" {:20.*}", self.precision, goal); 54 | 55 | writeln!(&mut self.writer, "{}", result).unwrap(); 56 | } 57 | } 58 | } 59 | 60 | /// The logger print out to stdout best result and value of goal function after end of genetic algorithm running. 61 | pub struct ResultOnlyLogger<'a> { 62 | writer: &'a mut dyn io::Write, 63 | precision: usize, 64 | } 65 | 66 | impl<'a> ResultOnlyLogger<'a> { 67 | /// Constructor 68 | /// 69 | /// # Parameters 70 | /// * `precision` - count of the digits after comma for float numbers. 71 | pub fn new(writer: &'a mut dyn io::Write, precision: usize) -> Self { 72 | Self { writer, precision } 73 | } 74 | } 75 | 76 | impl<'a, T: Display> Logger> for ResultOnlyLogger<'a> { 77 | fn start(&mut self, _state: &dyn AlgorithmState>) {} 78 | 79 | fn finish(&mut self, state: &dyn AlgorithmState>) { 80 | match state.get_best_solution() { 81 | None => writeln!(&mut self.writer, "Solution not found").unwrap(), 82 | Some((solution, goal)) => { 83 | let mut result = String::new(); 84 | result = result + "Solution:\n"; 85 | 86 | for x in solution { 87 | result = result + &format!(" {:.*}\n", self.precision, x); 88 | } 89 | 90 | result = result + "\n"; 91 | writeln!(&mut self.writer, "{}", result).unwrap(); 92 | writeln!(&mut self.writer, "Goal: {:.*}", self.precision, goal).unwrap(); 93 | } 94 | } 95 | writeln!( 96 | &mut self.writer, 97 | "Iterations count: {}", 98 | state.get_iteration() 99 | ) 100 | .unwrap(); 101 | } 102 | } 103 | 104 | /// The logger prints out time duration after finish of algorithm. 105 | pub struct TimeLogger<'a> { 106 | writer: &'a mut dyn io::Write, 107 | start_time: Option, 108 | } 109 | 110 | impl<'a> TimeLogger<'a> { 111 | /// Constructor 112 | pub fn new(writer: &'a mut dyn io::Write) -> Self { 113 | Self { 114 | writer, 115 | start_time: None, 116 | } 117 | } 118 | } 119 | 120 | impl<'a, T: Display> Logger> for TimeLogger<'a> { 121 | fn resume(&mut self, _state: &dyn AlgorithmState>) { 122 | self.start_time = Some(time::Instant::now()); 123 | } 124 | 125 | fn finish(&mut self, _state: &dyn AlgorithmState>) { 126 | let duration = self.start_time.unwrap().elapsed(); 127 | let time_ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; 128 | 129 | writeln!(&mut self.writer, "Time elapsed: {} ms", time_ms).unwrap(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /optlib/src/tools/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod logging; 2 | pub mod stopchecker; 3 | pub mod statistics; 4 | 5 | use num::NumCast; 6 | use rand::distributions::{Distribution, Uniform}; 7 | use rand::rngs::ThreadRng; 8 | 9 | /// Creator to initialize vector with random values in given interval. 10 | /// `T` - vector items type 11 | pub struct RandomVectorCreator { 12 | random: ThreadRng, 13 | } 14 | 15 | impl RandomVectorCreator { 16 | /// Constructor. 17 | pub fn new() -> Self { 18 | Self { 19 | random: rand::thread_rng(), 20 | } 21 | } 22 | 23 | pub fn create_vec(&mut self, intervals: &Vec<(T, T)>) -> Vec { 24 | for interval in intervals { 25 | assert!(interval.0 < interval.1); 26 | } 27 | 28 | let result = intervals 29 | .iter() 30 | .map(|(min, max)| { 31 | let between = Uniform::new_inclusive(min.to_f64().unwrap(), max.to_f64().unwrap()); 32 | T::from(between.sample(&mut self.random)).unwrap() 33 | }) 34 | .collect(); 35 | 36 | result 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use crate::tools::RandomVectorCreator; 43 | 44 | #[test] 45 | fn test_empty() { 46 | let intervals: Vec<(f64, f64)> = vec![]; 47 | let mut creator = RandomVectorCreator::new(); 48 | 49 | let result = creator.create_vec(&intervals); 50 | assert_eq!(result.len(), 0); 51 | } 52 | 53 | #[test] 54 | fn test_intervals() { 55 | let run_count = 1000; 56 | let intervals = vec![(0.0, 1.0), (-1.0, 1.0), (100.0, 110.0)]; 57 | let mut creator = RandomVectorCreator::new(); 58 | 59 | for _ in 0..run_count { 60 | let result: Vec = creator.create_vec(&intervals); 61 | assert!(result[0] >= 0.0); 62 | assert!(result[0] <= 1.0); 63 | 64 | assert!(result[1] >= -1.0); 65 | assert!(result[1] <= 1.0); 66 | 67 | assert!(result[2] >= 100.0); 68 | assert!(result[2] <= 110.0); 69 | } 70 | } 71 | 72 | #[test] 73 | #[should_panic] 74 | fn invalid_intervals_01() { 75 | let intervals = vec![(1.0, 0.0)]; 76 | let mut creator = RandomVectorCreator::new(); 77 | creator.create_vec(&intervals); 78 | } 79 | 80 | #[test] 81 | #[should_panic] 82 | fn invalid_intervals_02() { 83 | let intervals = vec![(0.0, 0.0)]; 84 | let mut creator = RandomVectorCreator::new(); 85 | creator.create_vec(&intervals); 86 | } 87 | 88 | #[test] 89 | #[should_panic] 90 | fn invalid_intervals_03() { 91 | let intervals = vec![(0.0, 1.0), (10.0, 0.0)]; 92 | let mut creator = RandomVectorCreator::new(); 93 | creator.create_vec(&intervals); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /optlib/src/tools/stopchecker/mod.rs: -------------------------------------------------------------------------------- 1 | use std::f64; 2 | 3 | use super::super::AlgorithmState; 4 | 5 | /// The trait with break criterion optimization algorithm. 6 | /// 7 | /// `T` - type of a point in the search space for goal function. 8 | pub trait StopChecker { 9 | /// The method must return true if algorithm must be stopped. 10 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool; 11 | } 12 | 13 | /// Stop the algorithm if ANY of stop checker returns true 14 | pub struct CompositeAny { 15 | stop_checkers: Vec>>, 16 | } 17 | 18 | impl CompositeAny { 19 | /// Constructor 20 | pub fn new(stop_checkers: Vec>>) -> Self { 21 | assert!(stop_checkers.len() != 0); 22 | Self { stop_checkers } 23 | } 24 | } 25 | 26 | impl StopChecker for CompositeAny { 27 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool { 28 | for checker in &mut self.stop_checkers { 29 | if checker.can_stop(state) { 30 | return true; 31 | } 32 | } 33 | 34 | false 35 | } 36 | } 37 | 38 | /// Stop the algorithm if ALL stop checkers returns true 39 | pub struct CompositeAll { 40 | stop_checkers: Vec>>, 41 | } 42 | 43 | impl CompositeAll { 44 | /// Constructor 45 | pub fn new(stop_checkers: Vec>>) -> Self { 46 | assert!(stop_checkers.len() != 0); 47 | Self { stop_checkers } 48 | } 49 | } 50 | 51 | impl StopChecker for CompositeAll { 52 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool { 53 | for checker in &mut self.stop_checkers { 54 | if !checker.can_stop(state) { 55 | return false; 56 | } 57 | } 58 | 59 | true 60 | } 61 | } 62 | 63 | /// The algorithm will be stopped after specified iteration. 64 | pub struct MaxIterations { 65 | max_iter: usize, 66 | } 67 | 68 | impl MaxIterations { 69 | /// Constructor 70 | /// 71 | /// # Parameters 72 | /// * `max_iter` - how many iterations will run the algorithm. 73 | pub fn new(max_iter: usize) -> Self { 74 | MaxIterations { max_iter } 75 | } 76 | } 77 | 78 | impl StopChecker for MaxIterations { 79 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool { 80 | state.get_iteration() >= self.max_iter 81 | } 82 | } 83 | 84 | /// The algorithm will be stopped if the best goal function does not change. 85 | pub struct GoalNotChange { 86 | max_iter: usize, 87 | delta: f64, 88 | 89 | old_goal: f64, 90 | change_iter: usize, 91 | } 92 | 93 | impl GoalNotChange { 94 | /// Constructor. 95 | /// 96 | /// # Parameters 97 | /// * `max_iter` - how many iterations the value of goal function of the best 98 | /// solution may not change. 99 | /// * `delta` - small value. The change of goal function is not considered if the change less 100 | /// of `delta`. 101 | pub fn new(max_iter: usize, delta: f64) -> Self { 102 | GoalNotChange { 103 | max_iter, 104 | delta, 105 | old_goal: f64::MAX, 106 | change_iter: 0, 107 | } 108 | } 109 | } 110 | 111 | impl StopChecker for GoalNotChange { 112 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool { 113 | match state.get_best_solution() { 114 | None => false, 115 | Some((_, best_goal)) => { 116 | let delta = (best_goal - self.old_goal).abs(); 117 | if delta > self.delta { 118 | self.old_goal = best_goal; 119 | self.change_iter = state.get_iteration(); 120 | } 121 | 122 | (state.get_iteration() - self.change_iter) > self.max_iter 123 | } 124 | } 125 | } 126 | } 127 | 128 | /// Stop the algorithm if value of the goal function less of than threshold. 129 | pub struct Threshold { 130 | threshold: f64, 131 | } 132 | 133 | impl Threshold { 134 | /// Constructor. 135 | /// 136 | /// # Parameters 137 | /// * `threshold` - min value of the goal function 138 | pub fn new(threshold: f64) -> Self { 139 | Self { threshold } 140 | } 141 | } 142 | 143 | impl StopChecker for Threshold { 144 | fn can_stop(&mut self, state: &dyn AlgorithmState) -> bool { 145 | match state.get_best_solution() { 146 | None => false, 147 | Some((_, goal)) => goal <= self.threshold, 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /optlib/tests/genetic-paraboloid.rs: -------------------------------------------------------------------------------- 1 | use num::abs; 2 | 3 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 4 | use optlib::tools::logging; 5 | use optlib::tools::stopchecker; 6 | use optlib::{GoalFromFunction, Optimizer}; 7 | use optlib_testfunc; 8 | 9 | type Gene = f32; 10 | type Chromosomes = Vec; 11 | 12 | #[test] 13 | fn genetic_paraboloid() { 14 | // General parameters 15 | let minval: Gene = -100.0; 16 | let maxval: Gene = 100.0; 17 | let population_size = 800; 18 | let chromo_count = 5; 19 | let intervals = vec![(minval, maxval); chromo_count]; 20 | 21 | // Goal function 22 | let goal = GoalFromFunction::new(optlib_testfunc::paraboloid); 23 | 24 | // Creator 25 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 26 | 27 | // Pairing 28 | // let pairing = pairing::RandomPairing::new(); 29 | let families_count = population_size / 2; 30 | let partners_count = 2; 31 | let rounds_count = 2; 32 | let pairing = pairing::Tournament::new(families_count) 33 | .partners_count(partners_count) 34 | .rounds_count(rounds_count); 35 | 36 | // Cross 37 | let single_cross = cross::FloatCrossExp::new(); 38 | // let single_cross = cross::CrossBitwise::new(); 39 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 40 | 41 | // Mutation 42 | let mutation_probability = 15.0; 43 | let mutation_gene_count = 3; 44 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 45 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 46 | 47 | // Pre birth 48 | let pre_births: Vec>> = vec![Box::new( 49 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 50 | )]; 51 | 52 | // Selection 53 | let selections: Vec>> = vec![ 54 | Box::new(selection::KillFitnessNaN::new()), 55 | Box::new(selection::LimitPopulation::new(population_size)), 56 | ]; 57 | 58 | // Stop checker 59 | let change_max_iterations = 150; 60 | let change_delta = 1e-7; 61 | let stop_checker = stopchecker::CompositeAny::new(vec![ 62 | Box::new(stopchecker::Threshold::new(1e-6)), 63 | Box::new(stopchecker::GoalNotChange::new( 64 | change_max_iterations, 65 | change_delta, 66 | )), 67 | Box::new(stopchecker::MaxIterations::new(5000)), 68 | ]); 69 | 70 | // Logger 71 | let loggers: Vec>> = vec![]; 72 | 73 | let mut optimizer = genetic::GeneticOptimizer::new( 74 | Box::new(goal), 75 | Box::new(stop_checker), 76 | Box::new(creator), 77 | Box::new(pairing), 78 | Box::new(cross), 79 | Box::new(mutation), 80 | selections, 81 | pre_births, 82 | ); 83 | optimizer.set_loggers(loggers); 84 | 85 | // Run genetic algorithm 86 | match optimizer.find_min() { 87 | None => assert!(false), 88 | Some((solution, goal_value)) => { 89 | for i in 0..chromo_count { 90 | assert!(abs(solution[i] - (i as f32 + 1.0)) < 0.1); 91 | } 92 | 93 | assert!(abs(goal_value) < 1e-3); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /optlib/tests/genetic-rastrigin.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rastrigin function. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (0.0, 0.0, ...) for any xi lying in [-5.12; 5.12]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use num::abs; 15 | 16 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 17 | use optlib::tools::logging; 18 | use optlib::tools::stopchecker; 19 | use optlib::{GoalFromFunction, Optimizer}; 20 | use optlib_testfunc; 21 | 22 | /// Gene type 23 | type Gene = f32; 24 | 25 | /// Chromosomes type 26 | type Chromosomes = Vec; 27 | 28 | #[test] 29 | fn genetic_rastrigin() { 30 | // General parameters 31 | 32 | // Search space. Any xi lies in [-5.12; 5.12] 33 | let minval: Gene = -5.12; 34 | let maxval: Gene = 5.12; 35 | 36 | // Count individuals in initial population 37 | let population_size = 20; 38 | 39 | // Count of xi in the chromosomes 40 | let chromo_count = 30; 41 | 42 | let intervals = vec![(minval, maxval); chromo_count]; 43 | 44 | // Make a trait object for goal function (Schwefel function) 45 | let goal = GoalFromFunction::new(optlib_testfunc::rastrigin); 46 | 47 | // Make the creator to create initial population. 48 | // RandomCreator will fill initial population with individuals with random chromosomes in a 49 | // given interval, 50 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 51 | 52 | // Make a trait object for the pairing. 53 | // Pairing is algorithm of selection individuals for crossbreeding. 54 | 55 | // Select random individuals from the population. 56 | // let pairing = pairing::RandomPairing::new(); 57 | 58 | // Tournament method. 59 | let families_count = population_size / 2; 60 | let rounds_count = 5; 61 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 62 | 63 | // Crossbreeding algorithm. 64 | // Make a Cross trait object. The bitwise crossing for float genes. 65 | let single_cross = cross::FloatCrossExp::new(); 66 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 67 | 68 | // Make a Mutation trait object. 69 | // Use bitwise mutation (change random bits with given probability). 70 | let mutation_probability = 15.0; 71 | let mutation_gene_count = 3; 72 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 73 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 74 | 75 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 76 | let pre_births: Vec>> = vec![Box::new( 77 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 78 | )]; 79 | 80 | // Stop checker. Stop criterion for genetic algorithm. 81 | let change_max_iterations = 200; 82 | let change_delta = 1e-7; 83 | 84 | // Stop algorithm if the value of goal function will become less of 1e-4 or 85 | // after 3000 generations (iterations). 86 | let stop_checker = stopchecker::CompositeAny::new(vec![ 87 | Box::new(stopchecker::Threshold::new(1e-4)), 88 | Box::new(stopchecker::GoalNotChange::new( 89 | change_max_iterations, 90 | change_delta, 91 | )), 92 | Box::new(stopchecker::MaxIterations::new(3000)), 93 | ]); 94 | 95 | // Make a trait object for selection. Selection is killing the worst individuals. 96 | // Kill all individuals if the value of goal function is NaN or Inf. 97 | // Kill the worst individuals to population size remained unchanged. 98 | let selections: Vec>> = vec![ 99 | Box::new(selection::KillFitnessNaN::new()), 100 | Box::new(selection::LimitPopulation::new(population_size)), 101 | ]; 102 | 103 | // Make a loggers trait objects 104 | let loggers: Vec>> = vec![]; 105 | 106 | // Construct main optimizer struct 107 | let mut optimizer = genetic::GeneticOptimizer::new( 108 | Box::new(goal), 109 | Box::new(stop_checker), 110 | Box::new(creator), 111 | Box::new(pairing), 112 | Box::new(cross), 113 | Box::new(mutation), 114 | selections, 115 | pre_births, 116 | ); 117 | optimizer.set_loggers(loggers); 118 | 119 | // Run genetic algorithm 120 | match optimizer.find_min() { 121 | None => assert!(false), 122 | Some((solution, goal_value)) => { 123 | for i in 0..chromo_count { 124 | assert!(abs(solution[i] - 0.0) < 0.01); 125 | } 126 | 127 | assert!(abs(goal_value) < 1e-3); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /optlib/tests/genetic-rosenbrock.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Rosenbrock function. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (1.0, 1.0, ...) for any xi. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | use std::sync::mpsc; 15 | use std::thread; 16 | 17 | use optlib::genetic::{ 18 | self, creation, cross, mutation, pairing, pre_birth, selection, GeneticOptimizer, 19 | }; 20 | use optlib::tools::statistics::{get_predicate_success_vec_solution, StatFunctionsSolution}; 21 | use optlib::tools::{logging, statistics, stopchecker}; 22 | use optlib::{Goal, GoalFromFunction, Optimizer}; 23 | use optlib_testfunc; 24 | 25 | /// Gene type 26 | type Gene = f32; 27 | 28 | /// Chromosomes type 29 | type Chromosomes = Vec; 30 | 31 | fn create_optimizer<'a>( 32 | chromo_count: usize, 33 | goal: Box + 'a>, 34 | ) -> GeneticOptimizer<'a, Chromosomes> { 35 | // General parameters 36 | 37 | // Search space. Any xi lies in [-500.0; 500.0] 38 | let minval: Gene = -2.0; 39 | let maxval: Gene = 2.0; 40 | 41 | // Count individuals in initial population 42 | let population_size = 700; 43 | 44 | let intervals = vec![(minval, maxval); chromo_count]; 45 | 46 | // Make the creator to create initial population. 47 | // RandomCreator will fill initial population with individuals with random chromosomes in a 48 | // given interval, 49 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 50 | 51 | // Make a trait object for the pairing. 52 | // Pairing is algorithm of selection individuals for crossbreeding. 53 | 54 | // Tournament method. 55 | let pairing = pairing::RandomPairing::new(); 56 | 57 | // Crossbreeding algorithm. 58 | // Make a Cross trait object. The bitwise crossing for float genes. 59 | let single_cross = cross::FloatCrossExp::new(); 60 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 61 | 62 | // Make a Mutation trait object. 63 | // Use bitwise mutation (change random bits with given probability). 64 | let mutation_probability = 80.0; 65 | let mutation_gene_count = 3; 66 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 67 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 68 | 69 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 70 | let pre_births: Vec>> = vec![Box::new( 71 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 72 | )]; 73 | 74 | // Stop checker. Stop criterion for genetic algorithm. 75 | // Stop algorithm after 3000 generation (iteration). 76 | let change_max_iterations = 2000; 77 | let change_delta = 1e-7; 78 | let stop_checker = stopchecker::CompositeAny::new(vec![ 79 | Box::new(stopchecker::Threshold::new(1e-6)), 80 | Box::new(stopchecker::GoalNotChange::new( 81 | change_max_iterations, 82 | change_delta, 83 | )), 84 | ]); 85 | 86 | // Make a trait object for selection. Selection is killing the worst individuals. 87 | // Kill all individuals if the value of goal function is NaN or Inf. 88 | // Kill the worst individuals to population size remained unchanged. 89 | let selections: Vec>> = vec![ 90 | Box::new(selection::KillFitnessNaN::new()), 91 | Box::new(selection::LimitPopulation::new(population_size)), 92 | ]; 93 | 94 | // Construct main optimizer struct 95 | let optimizer = genetic::GeneticOptimizer::new( 96 | goal, 97 | Box::new(stop_checker), 98 | Box::new(creator), 99 | Box::new(pairing), 100 | Box::new(cross), 101 | Box::new(mutation), 102 | selections, 103 | pre_births, 104 | ); 105 | 106 | optimizer 107 | } 108 | 109 | #[test] 110 | fn genetic_rosenbrock() { 111 | let cpu = num_cpus::get(); 112 | let dimension = 3; 113 | 114 | // Running count per CPU 115 | let run_count = 100 / cpu; 116 | 117 | // Statistics from all runnings 118 | let mut full_stat = statistics::Statistics::new(); 119 | 120 | let (tx, rx) = mpsc::channel(); 121 | 122 | for _ in 0..cpu { 123 | let current_tx = mpsc::Sender::clone(&tx); 124 | 125 | thread::spawn(move || { 126 | let mut local_full_stat = statistics::Statistics::new(); 127 | 128 | for _ in 0..run_count { 129 | // Statistics from single run 130 | let mut statistics_data = statistics::Statistics::new(); 131 | { 132 | // Make a trait object for goal function 133 | let goal = GoalFromFunction::new(optlib_testfunc::rosenbrock); 134 | 135 | let mut optimizer = create_optimizer(dimension, Box::new(goal)); 136 | 137 | // Add logger to collect statistics 138 | let stat_logger = 139 | Box::new(statistics::StatisticsLogger::new(&mut statistics_data)); 140 | let loggers: Vec>> = vec![stat_logger]; 141 | optimizer.set_loggers(loggers); 142 | 143 | // Run optimization 144 | optimizer.find_min(); 145 | } 146 | 147 | // Add current running statistics to full statistics 148 | local_full_stat.unite(statistics_data); 149 | } 150 | current_tx.send(local_full_stat).unwrap(); 151 | }); 152 | } 153 | 154 | // Collect data from threads 155 | for _ in 0..cpu { 156 | let statistics_data = rx.recv().unwrap(); 157 | full_stat.unite(statistics_data); 158 | } 159 | 160 | let valid_answer = vec![1.0; dimension]; 161 | let delta = vec![1e-2; dimension]; 162 | 163 | let success_rate = full_stat 164 | .get_results() 165 | .get_success_rate(get_predicate_success_vec_solution(valid_answer, delta)) 166 | .unwrap(); 167 | 168 | assert!(success_rate >= 0.75); 169 | } 170 | -------------------------------------------------------------------------------- /optlib/tests/genetic-schwefel.rs: -------------------------------------------------------------------------------- 1 | //! Example of optimizing the Schwefel function with genetic algorithm. 2 | //! 3 | //! y = f(x), where x = (x0, x1, ..., xi,... xn). 4 | //! Global minimum is x' = (420.9687, 420.9687, ...) for any xi lying in [-500.0; 500.0]. 5 | //! f(x') = 0 6 | //! 7 | //! # Terms 8 | //! * `Goal function` - the function for optimization. y = f(x). 9 | //! * `Gene` - a single value of xi. 10 | //! * `Chromosome` - a point in the search space. x = (x0, x1, x2, ..., xn). 11 | //! * `Individual` - union of x and value of goal function. 12 | //! * `Population` - set of the individuals. 13 | //! * `Generation` - a number of iteration of genetic algorithm. 14 | 15 | use num::abs; 16 | 17 | use optlib::genetic::{self, creation, cross, mutation, pairing, pre_birth, selection}; 18 | use optlib::tools::logging; 19 | use optlib::tools::stopchecker; 20 | use optlib::{GoalFromFunction, Optimizer}; 21 | use optlib_testfunc; 22 | 23 | /// Gene type 24 | type Gene = f32; 25 | 26 | /// Chromosomes type 27 | type Chromosomes = Vec; 28 | 29 | #[test] 30 | fn genetic_schwefel() { 31 | // General parameters 32 | 33 | // Search space. Any xi lies in [-500.0; 500.0] 34 | let minval: Gene = -500.0; 35 | let maxval: Gene = 500.0; 36 | 37 | // Count individuals in initial population 38 | let population_size = 800; 39 | 40 | // Count of xi in the chromosomes 41 | let chromo_count = 5; 42 | 43 | let intervals = vec![(minval, maxval); chromo_count]; 44 | 45 | // Make a trait object for goal function (Schwefel function) 46 | let goal = GoalFromFunction::new(optlib_testfunc::schwefel); 47 | 48 | // Make the creator to create initial population. 49 | // RandomCreator will fill initial population with individuals with random chromosomes in a 50 | // given interval, 51 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 52 | 53 | // Make a trait object for the pairing. 54 | // Pairing is algorithm of selection individuals for crossbreeding. 55 | 56 | // Select random individuals from the population. 57 | // let pairing = pairing::RandomPairing::new(); 58 | 59 | // Tournament method. 60 | let families_count = population_size / 2; 61 | let rounds_count = 5; 62 | let pairing = pairing::Tournament::new(families_count).rounds_count(rounds_count); 63 | 64 | // Crossbreeding algorithm. 65 | // Make a Cross trait object. The bitwise crossing for float genes. 66 | let single_cross = cross::FloatCrossExp::new(); 67 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 68 | 69 | // Make a Mutation trait object. 70 | // Use bitwise mutation (change random bits with given probability). 71 | let mutation_probability = 15.0; 72 | let mutation_gene_count = 3; 73 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 74 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 75 | 76 | // Pre birth. Throw away new chlld chromosomes if their genes do not lies if given intervals. 77 | let pre_births: Vec>> = vec![Box::new( 78 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 79 | )]; 80 | 81 | // Stop checker. Stop criterion for genetic algorithm. 82 | let change_max_iterations = 200; 83 | let change_delta = 1e-7; 84 | 85 | // Stop algorithm if the value of goal function will become less of 1e-4 or 86 | // after 3000 generations (iterations). 87 | let stop_checker = stopchecker::CompositeAny::new(vec![ 88 | Box::new(stopchecker::Threshold::new(1e-4)), 89 | Box::new(stopchecker::GoalNotChange::new( 90 | change_max_iterations, 91 | change_delta, 92 | )), 93 | Box::new(stopchecker::MaxIterations::new(3000)), 94 | ]); 95 | 96 | // Make a trait object for selection. Selection is killing the worst individuals. 97 | // Kill all individuals if the value of goal function is NaN or Inf. 98 | // Kill the worst individuals to population size remained unchanged. 99 | let selections: Vec>> = vec![ 100 | Box::new(selection::KillFitnessNaN::new()), 101 | Box::new(selection::LimitPopulation::new(population_size)), 102 | ]; 103 | 104 | // Make a loggers trait objects 105 | let loggers: Vec>> = vec![]; 106 | 107 | // Construct main optimizer struct 108 | let mut optimizer = genetic::GeneticOptimizer::new( 109 | Box::new(goal), 110 | Box::new(stop_checker), 111 | Box::new(creator), 112 | Box::new(pairing), 113 | Box::new(cross), 114 | Box::new(mutation), 115 | selections, 116 | pre_births, 117 | ); 118 | optimizer.set_loggers(loggers); 119 | 120 | // Run genetic algorithm 121 | match optimizer.find_min() { 122 | None => assert!(false), 123 | Some((solution, goal_value)) => { 124 | for i in 0..chromo_count { 125 | assert!(abs(solution[i] - 421.0) < 0.1); 126 | } 127 | 128 | assert!(abs(goal_value) < 1e-3); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /optlib/tests/genetic-stat-paraboloid.rs: -------------------------------------------------------------------------------- 1 | use optlib::genetic::{ 2 | self, creation, cross, mutation, pairing, pre_birth, selection, GeneticOptimizer, 3 | }; 4 | use optlib::tools::{statistics, logging, stopchecker}; 5 | use optlib::{GoalFromFunction, Optimizer}; 6 | use optlib_testfunc; 7 | 8 | type Gene = f32; 9 | type Chromosomes = Vec; 10 | 11 | fn _create_optimizer<'a>(chromo_count: usize) -> GeneticOptimizer<'a, Chromosomes> { 12 | // General parameters 13 | let minval: Gene = -100.0; 14 | let maxval: Gene = 100.0; 15 | let population_size = 800; 16 | let intervals = vec![(minval, maxval); chromo_count]; 17 | 18 | // Goal function 19 | let goal = GoalFromFunction::new(optlib_testfunc::paraboloid); 20 | 21 | // Creator 22 | let creator = creation::vec_float::RandomCreator::new(population_size, intervals.clone()); 23 | 24 | // Pairing 25 | let families_count = population_size / 2; 26 | let partners_count = 2; 27 | let rounds_count = 2; 28 | let pairing = pairing::Tournament::new(families_count) 29 | .partners_count(partners_count) 30 | .rounds_count(rounds_count); 31 | 32 | // Cross 33 | let single_cross = cross::FloatCrossExp::new(); 34 | let cross = cross::VecCrossAllGenes::new(Box::new(single_cross)); 35 | 36 | // Mutation 37 | let mutation_probability = 15.0; 38 | let mutation_gene_count = 3; 39 | let single_mutation = mutation::BitwiseMutation::new(mutation_gene_count); 40 | let mutation = mutation::VecMutation::new(mutation_probability, Box::new(single_mutation)); 41 | 42 | // Pre birth 43 | let pre_births: Vec>> = vec![Box::new( 44 | pre_birth::vec_float::CheckChromoInterval::new(intervals.clone()), 45 | )]; 46 | 47 | // Selection 48 | let selections: Vec>> = vec![ 49 | Box::new(selection::KillFitnessNaN::new()), 50 | Box::new(selection::LimitPopulation::new(population_size)), 51 | ]; 52 | 53 | // Stop checker 54 | let change_max_iterations = 150; 55 | let change_delta = 1e-7; 56 | let stop_checker = stopchecker::CompositeAny::new(vec![ 57 | Box::new(stopchecker::Threshold::new(1e-6)), 58 | Box::new(stopchecker::GoalNotChange::new( 59 | change_max_iterations, 60 | change_delta, 61 | )), 62 | Box::new(stopchecker::MaxIterations::new(5000)), 63 | ]); 64 | 65 | let optimizer = genetic::GeneticOptimizer::new( 66 | Box::new(goal), 67 | Box::new(stop_checker), 68 | Box::new(creator), 69 | Box::new(pairing), 70 | Box::new(cross), 71 | Box::new(mutation), 72 | selections, 73 | pre_births, 74 | ); 75 | 76 | optimizer 77 | } 78 | 79 | #[test] 80 | fn genetic_paraboloid_single() { 81 | let run_count = 1; 82 | let chromo_count = 5; 83 | let result; 84 | 85 | // Logging 86 | let mut statistics = statistics::Statistics::new(); 87 | { 88 | let mut optimizer = _create_optimizer(chromo_count); 89 | 90 | let stat_logger = Box::new(statistics::StatisticsLogger::new(&mut statistics)); 91 | let loggers: Vec>> = vec![stat_logger]; 92 | 93 | optimizer.set_loggers(loggers); 94 | 95 | result = optimizer.find_min().unwrap(); 96 | } 97 | 98 | assert_eq!(statistics.get_run_count(), run_count); 99 | assert_eq!(statistics.get_results().len(), run_count); 100 | assert_eq!(statistics.get_convergence().len(), run_count); 101 | 102 | let stat_results = statistics.get_results(); 103 | let (stat_solution, stat_goal) = stat_results[0].as_ref().unwrap(); 104 | 105 | assert_eq!(result.0, *stat_solution); 106 | assert_eq!(result.1, *stat_goal); 107 | } 108 | 109 | #[test] 110 | fn genetic_paraboloid_two() { 111 | let run_count = 2; 112 | let chromo_count = 5; 113 | let result_1; 114 | let result_2; 115 | 116 | // Logging 117 | let mut statistics = statistics::Statistics::new(); 118 | { 119 | let mut optimizer = _create_optimizer(chromo_count); 120 | 121 | let stat_logger = Box::new(statistics::StatisticsLogger::new(&mut statistics)); 122 | let loggers: Vec>> = vec![stat_logger]; 123 | 124 | optimizer.set_loggers(loggers); 125 | 126 | result_1 = optimizer.find_min().unwrap(); 127 | result_2 = optimizer.find_min().unwrap(); 128 | } 129 | 130 | assert_eq!(statistics.get_run_count(), run_count); 131 | assert_eq!(statistics.get_results().len(), run_count); 132 | assert_eq!(statistics.get_convergence().len(), run_count); 133 | 134 | let stat_results = statistics.get_results(); 135 | let (stat_solution_1, stat_goal_1) = stat_results[0].as_ref().unwrap(); 136 | let (stat_solution_2, stat_goal_2) = stat_results[1].as_ref().unwrap(); 137 | 138 | assert_eq!(result_1.0, *stat_solution_1); 139 | assert_eq!(result_1.1, *stat_goal_1); 140 | 141 | assert_eq!(result_2.0, *stat_solution_2); 142 | assert_eq!(result_2.1, *stat_goal_2); 143 | } 144 | -------------------------------------------------------------------------------- /optlib/tests/particleswarm-paraboloid.rs: -------------------------------------------------------------------------------- 1 | use num::abs; 2 | 3 | use optlib::{ 4 | tools::{logging, stopchecker}, 5 | GoalFromFunction, Optimizer, 6 | particleswarm::{ 7 | self, 8 | initializing, 9 | postmove, 10 | velocitycalc, 11 | PostMove, 12 | }, 13 | }; 14 | 15 | use optlib_testfunc; 16 | 17 | type Coordinate = f32; 18 | 19 | #[test] 20 | fn test_particleswarm_paraboloid() { 21 | // General parameters 22 | let minval: Coordinate = -100.0; 23 | let maxval: Coordinate = 100.0; 24 | let particles_count = 150; 25 | let dimension = 5; 26 | let intervals = vec![(minval, maxval); dimension]; 27 | let phi_personal = 2.0; 28 | let phi_global = 6.0; 29 | let k = 0.2; 30 | 31 | // Goal function 32 | let goal = GoalFromFunction::new(optlib_testfunc::paraboloid); 33 | 34 | // Particles initializers 35 | let coord_initializer = initializing::RandomCoordinatesInitializer::new(intervals.clone(), particles_count); 36 | let velocity_initializer = initializing::ZeroVelocityInitializer::new(dimension, particles_count); 37 | 38 | // PostMove 39 | let post_moves: Vec>> = vec![Box::new(postmove::MoveToBoundary::new(intervals.clone()))]; 40 | 41 | // Velocity calculator 42 | let velocity_calculator = velocitycalc::CanonicalVelocityCalculator::new(phi_personal, phi_global, k); 43 | 44 | // Stop checker 45 | let change_max_iterations = 150; 46 | let change_delta = 1e-7; 47 | let stop_checker = stopchecker::CompositeAny::new(vec![ 48 | Box::new(stopchecker::Threshold::new(1e-6)), 49 | Box::new(stopchecker::GoalNotChange::new( 50 | change_max_iterations, 51 | change_delta, 52 | )), 53 | Box::new(stopchecker::MaxIterations::new(3000)), 54 | ]); 55 | 56 | // Logger 57 | let loggers: Vec>>> = vec![]; 58 | 59 | let mut optimizer = particleswarm::ParticleSwarmOptimizer::new( 60 | Box::new(goal), 61 | Box::new(stop_checker), 62 | Box::new(coord_initializer), 63 | Box::new(velocity_initializer), 64 | Box::new(velocity_calculator), 65 | ); 66 | optimizer.set_loggers(loggers); 67 | optimizer.set_post_moves(post_moves); 68 | 69 | match optimizer.find_min() { 70 | None => assert!(false), 71 | Some((solution, goal_value)) => { 72 | for i in 0..dimension { 73 | assert!(abs(solution[i] - (i as f32 + 1.0)) < 0.3); 74 | } 75 | 76 | assert!(abs(goal_value) < 0.1); 77 | } 78 | } 79 | } 80 | --------------------------------------------------------------------------------