mahf/heuristics/
pso.rs

1//! Particle Swarm Optimization (PSO).
2//!
3//! # References
4//!
5//! \[1\] James Kennedy and Russell Eberhart. 1995.
6//! Particle swarm optimization.
7//! In Proceedings of ICNN’95 - International Conference on Neural Networks, 1942–1948 vol.4.
8//! DOI:<https://doi.org/10/bdc3t3>
9//!
10//! \[2\] Riccardo Poli, James Kennedy, and Tim Blackwell. 2007.
11//! Particle swarm optimization.
12//! Swarm Intell 1, 1 (June 2007), 33–57.
13//! DOI:<https://doi.org/10/dhnq29>
14
15use eyre::WrapErr;
16
17use crate::{
18    component::ExecResult,
19    components::{boundary, initialization, mapping, swarm},
20    conditions::Condition,
21    configuration::Configuration,
22    identifier::{Global, Identifier},
23    lens::ValueOf,
24    logging::Logger,
25    problems::{LimitedVectorProblem, SingleObjectiveProblem},
26    state::common,
27    Component,
28};
29
30/// Parameters for [`real_pso`].
31pub struct RealProblemParameters {
32    pub num_particles: u32,
33    pub start_weight: f64,
34    pub end_weight: f64,
35    pub c_one: f64,
36    pub c_two: f64,
37    pub v_max: f64,
38}
39
40/// An example single-objective PSO operating on a real search space.
41///
42/// Uses the [`pso`] component internally.
43pub fn real_pso<P>(
44    params: RealProblemParameters,
45    condition: Box<dyn Condition<P>>,
46) -> ExecResult<Configuration<P>>
47where
48    P: SingleObjectiveProblem + LimitedVectorProblem<Element = f64>,
49{
50    let RealProblemParameters {
51        num_particles,
52        start_weight,
53        end_weight,
54        c_one,
55        c_two,
56        v_max,
57    } = params;
58
59    Ok(Configuration::builder()
60        .do_(initialization::RandomSpread::new(num_particles))
61        .evaluate()
62        .update_best_individual()
63        .do_(pso::<P, Global>(
64            Parameters {
65                particle_init: swarm::ParticleSwarmInit::new(v_max)?,
66                particle_update: swarm::ParticleVelocitiesUpdate::new(
67                    start_weight,
68                    c_one,
69                    c_two,
70                    v_max,
71                )
72                .wrap_err("failed to construct particle velocities update")?,
73                constraints: boundary::Saturation::new(),
74                inertia_weight_update: Some(mapping::Linear::new(
75                    start_weight,
76                    end_weight,
77                    ValueOf::<common::Progress<ValueOf<common::Iterations>>>::new(),
78                    ValueOf::<swarm::InertiaWeight<swarm::ParticleVelocitiesUpdate>>::new(),
79                )),
80                state_update: swarm::ParticleSwarmUpdate::new(),
81            },
82            condition,
83        ))
84        .build())
85}
86
87/// Basic building blocks of [`pso`].
88pub struct Parameters<P> {
89    pub particle_init: Box<dyn Component<P>>,
90    pub particle_update: Box<dyn Component<P>>,
91    pub constraints: Box<dyn Component<P>>,
92    pub inertia_weight_update: Option<Box<dyn Component<P>>>,
93    pub state_update: Box<dyn Component<P>>,
94}
95
96/// A generic single-objective Particle Swarm Optimization (PSO) template.
97pub fn pso<P, I>(params: Parameters<P>, condition: Box<dyn Condition<P>>) -> Box<dyn Component<P>>
98where
99    P: SingleObjectiveProblem,
100    I: Identifier,
101{
102    let Parameters {
103        particle_init,
104        particle_update,
105        constraints,
106        inertia_weight_update,
107        state_update,
108    } = params;
109
110    Configuration::builder()
111        .do_(particle_init)
112        .while_(condition, |builder| {
113            builder
114                .do_(particle_update)
115                .do_(constraints)
116                .evaluate_with::<I>()
117                .update_best_individual()
118                .do_if_some_(inertia_weight_update)
119                .do_(state_update)
120                .do_(Logger::new())
121        })
122        .build_component()
123}