# POS_PSO Highly Configurable Particle Swarm Optimizer implemented in pure Rust #### Easily mimimize a cost function across multiple threads * Multiple optimizations can be run in parallel with __Independant Swarms__ * or Swarms can periodically share best-known locations in the search space with __Collaborative Swarms__ #### Motion dynamics are loosly based on [Matlab's Particle Swarm Algorithm](https://www.mathworks.com/help/gads/particle-swarm-optimization-algorithm.html) #### Optimizer takes a closure with the following header as the cost function: ```Rust move |x: &[f64]| -> f64 {} ``` * The input slice-array represents a point in the N dimensional optimization space * The returned cost is used to navigate the search space to locate a minimum * currently only supports f64, but future updates may allow more generic cost-functions ## Some Examples to get started: ### Single-Threaded Example (Single Swarm) * Use the default independant swarm configuration (good place to start, but you may want to define your own based on your use case) * Search Space Bounds and Max Velocities still must be defined in a JobConfig object * Single-Swarm may be the fastest performing option for high-end intel processors ``` Rust extern crate pos_pso; use pos_pso::{PSO, PSOConfig, JobConfig}; fn main() { let num_variables = 5; // Define a cost function to minimize: //data captured by closure let mins = vec![1.0, 2.0, 3.0, 4.0, 5.0]; let coeffs = vec![0.25; num_variables]; //cost function closure must take a &[f64] and return f64 let cost_function = move |point: &[f64]| -> f64 { let mut sum = 0.0; for i in 0..num_variables { sum += (point[i] - mins[i]).powi(2) * coeffs[i]; } sum }; // Create a PSO Configuration: let pso_config = PSOConfig::new( 1, // 1 swarm used in optimization 256, // 256 particles are spawned 10, // console is updated every 10 itterations true // optimizer is verbose (will provide more detailed information to console) ); // Create a PSO: let pso = PSO::new(pso_config); // Create a Job Configuration: let job_config = JobConfig::new( num_variables, vec![[-15.0, 15.0]; num_variables], // [upper, lower] bounds for each variable vec![1.125; num_variables], // max velocity for each variable 100, // run for 100 itterations 0.0000001, // exit cost (optimization will stop when a cost of 0.0000001 is reached) ); // Minimize cost function: //use minimize_independant to optimize with the default independant-swarm configuration //the next example will show how to use collaborative-swarms let min = pso.minimize_independant(job_config, cost_function); println!("Minimum of: {}, With value: {:?}", min.0, min.1); } ``` ### Multi-Threaded Example* Use 4 collaborative swarms with fewer particles in each) * A custom swarm configuration is defined with motion coefficients and a record-sharing period * This may be the fastest performing option on high-end AMD processors ``` Rust let num_variables = 5; // Define a cost function to minimize: //data captured by closure let mins = vec![1.0, 2.0, 3.0, 4.0, 5.0]; let coeffs = vec![0.25; num_variables]; //cost function closure must take a &[f64] and return f64 let cost_function = move |point: &[f64]| -> f64 { let mut sum = 0.0; for i in 0..num_variables { sum += (point[i] - mins[i]).powi(2) * coeffs[i]; } sum }; // Create a PSO Configuration: let pso_config = PSOConfig::new( 4, // 4 swarms (each spawned on their own thread) 64, // 64 particles per swarm 10, // console is updated every 10 itterations true // optimizer is verbose (will provide more detailed information to console) ); // Create a PSO: let pso = PSO::new(pso_config); // Create a Job Configuration: let job_config = JobConfig::new( num_variables, vec![[-15.0, 15.0]; num_variables], // [upper, lower] bounds for each variable vec![1.125; num_variables], // max velocity for each variable 100, // run for 100 itterations 0.0000001, // exit cost (swarms will stop when a cost of 0.0000001 is reached) ); // Create a custom Swarm Configuration: //collaborative swarms will share information with eachother about best known locations in the search space let swarm_config = SwarmConfig::new_collaborative( 1.45, // local weigth: how much particles care about their best known location 1.6, // tribal weight: how much particles care about their swarms best known location 1.25, // global weight: how much particles care about the overall best known location 0.4, // inertial coefficient: component of a particles velocity that contributes to its next velocity 1.25, // inertial growth factor: how much inertia grows and shrinks throughout optimization 0.125, // wall bounce factor: component of velocity that is saved when particle goes out of bounds 10, // tribal-global collab period: swarms share best known location every 10 itterations ); // Minimize cost function: //use minimize to optimize with a custom SwarmConfig let min = pso.minimize(job_config, swarm_config, cost_function); println!("Minimum of: {}, With value: {:?}", min.0, min.1); ``` ### Advanced Example * Use a group of 8 independant swarms to minimize the same cost function with different swarm parameters * This example shows how to use a SwarmConfigDistribution where swarm-behavior coefficients are sampled from a user defined distribution * All 8 swarms run in parallel on seperate threads (the lowest cost and best location are returned after all threads have finished) ``` Rust extern crate pos_pso; use pos_pso::{PSO, PSOConfig, JobConfig, SwarmConfigDistribution, ParamDist}; fn main() { let num_variables = 5; // Define a cost function to minimize: //data captured by closure let mins = vec![1.0, 2.0, 3.0, 4.0, 5.0]; let coeffs = vec![0.25; num_variables]; //cost function closure must take a &[f64] and return f64 let cost_function = move |point: &[f64]| -> f64 { let mut sum = 0.0; for i in 0..num_variables { sum += (point[i] - mins[i]).powi(2) * coeffs[i]; } sum }; // Create a PSO Configuration: let pso_config = PSOConfig::new( 8, // 8 swarms (each spawned on their own thread) 128, // 128 particles per swarm 10, // console is updated by each thread every 10 itterations true // optimizer is verbose (will provide more detailed information to console) ); // Create a PSO: let pso = PSO::new(pso_config); // Create a Job Configuration: let job_config = JobConfig::new( num_variables, vec![[-15.0, 15.0]; num_variables], // [upper, lower] bounds for each variable vec![1.125; num_variables], // max velocity for each variable 100, // run for 100 itterations 0.0000001, // exit cost (swarms will stop when a cost of 0.0000001 is reached) ); // Create a Swarm Configuration Distribution: //the optimizer will sample a new swarm configuration for each swarm //this is usefull for automatically creating a range of swarm behaviors //in this case we are using new_independant, so all 8 optimizations will run seperately in parallel let swarm_config = SwarmConfigDistribution::new_independant( ParamDist::Fixed(1.45), // local: fixed value of 1.45 ParamDist::Range([1.65, 0.25]), // tribal: random value: 1.65 +/- 25% ParamDist::Fixed(0.4), // momentum: fixed value of 0.4 ParamDist::Range([1.25, 0.05]), // momentum growth factor: random value: 1.25 +/- 5% ParamDist::Fixed(0.0125), // wall bounce factor: fixed value of 0.0125 ); // Minimize cost function: //use minimize_distributed to accept SwarmConfigDistribution let min = pso.minimize_distributed(job_config, swarm_config, cost_function); println!("Minimum of: {}, With value: {:?}", min.0, min.1); } ```