argmin/solver/particleswarm/
mod.rs1use crate::prelude::*;
13use serde::{de::DeserializeOwned, Deserialize, Serialize};
14use std;
15use std::default::Default;
16
17#[derive(Serialize, Deserialize)]
25pub struct ParticleSwarm<P, F> {
26 particles: Vec<Particle<P, F>>,
27 best_position: P,
28 best_cost: F,
29
30 weight_momentum: F,
32 weight_particle: F,
33 weight_swarm: F,
34
35 search_region: (P, P),
36 num_particles: usize,
37}
38
39impl<P, F> ParticleSwarm<P, F>
40where
41 P: Position<F> + DeserializeOwned + Serialize,
42 F: ArgminFloat,
43{
44 pub fn new(
51 search_region: (P, P),
52 num_particles: usize,
53 weight_momentum: F,
54 weight_particle: F,
55 weight_swarm: F,
56 ) -> Result<Self, Error> {
57 let particle_swarm = ParticleSwarm {
58 particles: vec![],
59 best_position: P::rand_from_range(
60 &search_region.0,
62 &search_region.1,
63 ),
64 best_cost: F::infinity(),
65 weight_momentum,
66 weight_particle,
67 weight_swarm,
68 search_region,
69 num_particles,
70 };
71
72 Ok(particle_swarm)
73 }
74
75 fn initialize_particles<O: ArgminOp<Param = P, Output = F, Float = F>>(
76 &mut self,
77 op: &mut OpWrapper<O>,
78 ) {
79 self.particles = (0..self.num_particles)
80 .map(|_| self.initialize_particle(op))
81 .collect();
82
83 self.best_position = self.get_best_position();
84 self.best_cost = op.apply(&self.best_position).unwrap();
85 }
87
88 fn initialize_particle<O: ArgminOp<Param = P, Output = F, Float = F>>(
89 &mut self,
90 op: &mut OpWrapper<O>,
91 ) -> Particle<P, F> {
92 let (min, max) = &self.search_region;
93 let delta = max.sub(min);
94 let delta_neg = delta.mul(&F::from_f64(-1.0).unwrap());
95
96 let initial_position = O::Param::rand_from_range(min, max);
97 let initial_cost = op.apply(&initial_position).unwrap(); Particle {
100 position: initial_position.clone(),
101 velocity: O::Param::rand_from_range(&delta_neg, &delta),
102 cost: initial_cost,
103 best_position: initial_position,
104 best_cost: initial_cost,
105 }
106 }
107
108 fn get_best_position(&self) -> P {
109 let mut best: Option<(&P, F)> = None;
110
111 for p in &self.particles {
112 match best {
113 Some(best_sofar) => {
114 if p.cost < best_sofar.1 {
115 best = Some((&p.position, p.cost))
116 }
117 }
118 None => best = Some((&p.position, p.cost)),
119 }
120 }
121
122 match best {
123 Some(best_sofar) => best_sofar.0.clone(),
124 None => panic!("Particles not initialized"),
125 }
126 }
127}
128
129impl<O, P, F> Solver<O> for ParticleSwarm<P, F>
130where
131 O: ArgminOp<Output = F, Param = P, Float = F>,
132 O::Param: Position<F> + DeserializeOwned + Serialize,
133 O::Hessian: Clone + Default,
134 F: ArgminFloat,
135{
136 const NAME: &'static str = "Particle Swarm Optimization";
137
138 fn init(
139 &mut self,
140 _op: &mut OpWrapper<O>,
141 _state: &IterState<O>,
142 ) -> Result<Option<ArgminIterData<O>>, Error> {
143 self.initialize_particles(_op);
144
145 Ok(None)
146 }
147
148 fn next_iter(
150 &mut self,
151 _op: &mut OpWrapper<O>,
152 _state: &IterState<O>,
153 ) -> Result<ArgminIterData<O>, Error> {
154 let zero = O::Param::zero_like(&self.best_position);
155
156 for p in self.particles.iter_mut() {
157 let momentum = p.velocity.mul(&self.weight_momentum);
164
165 let to_optimum = p.best_position.sub(&p.position);
167 let pull_to_optimum = O::Param::rand_from_range(&zero, &to_optimum);
168 let pull_to_optimum = pull_to_optimum.mul(&self.weight_particle);
169
170 let to_global_optimum = self.best_position.sub(&p.position);
172 let pull_to_global_optimum =
173 O::Param::rand_from_range(&zero, &to_global_optimum).mul(&self.weight_swarm);
174
175 p.velocity = momentum.add(&pull_to_optimum).add(&pull_to_global_optimum);
176 let new_position = p.position.add(&p.velocity);
177
178 p.position = O::Param::min(
180 &O::Param::max(&new_position, &self.search_region.0),
181 &self.search_region.1,
182 );
183
184 p.cost = _op.apply(&p.position)?;
185 if p.cost < p.best_cost {
186 p.best_position = p.position.clone();
187 p.best_cost = p.cost;
188
189 if p.cost < self.best_cost {
190 self.best_position = p.position.clone();
191 self.best_cost = p.cost;
192 }
193 }
194 }
195
196 let population = self
198 .particles
199 .iter()
200 .map(|particle| (particle.position.clone(), particle.cost))
201 .collect();
202
203 let out = ArgminIterData::new()
204 .param(self.best_position.clone())
205 .cost(self.best_cost)
206 .population(population)
207 .kv(make_kv!(
208 "particles" => &self.particles;
209 ));
210
211 Ok(out)
212 }
213}
214
215pub trait Position<F: ArgminFloat>:
217 Clone
218 + Default
219 + ArgminAdd<Self, Self>
220 + ArgminSub<Self, Self>
221 + ArgminMul<F, Self>
222 + ArgminZeroLike
223 + ArgminRandom
224 + ArgminMinMax
225 + std::fmt::Debug
226{
227}
228impl<T, F: ArgminFloat> Position<F> for T
229where
230 T: Clone
231 + Default
232 + ArgminAdd<Self, Self>
233 + ArgminSub<Self, Self>
234 + ArgminMul<F, Self>
235 + ArgminZeroLike
236 + ArgminRandom
237 + ArgminMinMax
238 + std::fmt::Debug,
239 F: ArgminFloat,
240{
241}
242
243#[derive(Clone, Serialize, Deserialize, Debug)]
257pub struct Particle<T, F> {
258 pub position: T,
260 velocity: T,
262 pub cost: F,
264 best_position: T,
266 best_cost: F,
268}