Optimist  0.0.0
A C++ library for optimization
Loading...
Searching...
No Matches
ParticleSwarm.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Optimist project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * davide.stocco@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef OPTIMIST_OPTIMIZER_PARTICLE_SWARM_HH
14#define OPTIMIST_OPTIMIZER_PARTICLE_SWARM_HH
15
16#include "Optimist/Optimizer.hh"
17
18namespace Optimist {
19 namespace Optimizer {
20
25 template <typename Scalar>
27 Scalar m_weight{1.0};
28
29 public:
34
39 ConstantWeight(const Scalar weight) : m_weight(weight) {}
40
47 Scalar operator()(const Integer /*iteration*/,
48 const Integer /*max_iterations*/) const {
49 return this->m_weight;
50 }
51
52 }; // class ConstantWeight
53
54 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55 // - -
56 // - - - - - - - -
57
68 template <typename Scalar>
70 Scalar m_min_weight{0.4};
71 Scalar m_max_weight{0.9};
72
73 public:
79
86 LinearDecrease(const Scalar min_weight, const Scalar max_weight)
87 : m_min_weight(min_weight), m_max_weight(max_weight) {}
88
95 Scalar operator()(const Integer iteration,
96 const Integer max_iterations) const {
97 Scalar factor{static_cast<Scalar>(iteration) /
98 static_cast<Scalar>(max_iterations)};
99 return this->m_min_weight +
100 (this->m_max_weight - this->m_min_weight) * factor;
101 }
102
103 }; // class LinearDecrease
104
105 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106 // - -
107 // - - - - - - - -
108
119 template <typename Scalar>
121 Scalar m_min_weight{0.4};
122 Scalar m_max_weight{0.9};
123
124 public:
130
136 ExponentialDecrease1(const Scalar min_weight, const Scalar max_weight)
137 : m_min_weight(min_weight), m_max_weight(max_weight) {}
138
145 Scalar operator()(const Integer iteration,
146 const Integer max_iterations) const {
147 Scalar exponent{static_cast<Scalar>(iteration) /
148 (static_cast<Scalar>(max_iterations) / 10.0)};
149 return this->m_min_weight +
150 (this->m_max_weight - this->m_min_weight) * std::exp(-exponent);
151 }
152
153 }; // class ExponentialDecrease1
154
155 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156 // - -
157 // - - - - - - - -
158
169 template <typename Scalar>
171 Scalar m_min_weight{0.4};
172 Scalar m_max_weight{0.9};
173
174 public:
180
187 ExponentialDecrease2(const Scalar min_weight, const Scalar max_weight)
188 : m_min_weight(min_weight), m_max_weight(max_weight) {}
189
196 Scalar operator()(const Integer iteration,
197 const Integer max_iterations) const {
198 Scalar exponent{static_cast<Scalar>(iteration) /
199 (static_cast<Scalar>(max_iterations) / 4.0)};
200 exponent *= exponent;
201 return this->m_min_weight +
202 (this->m_max_weight - this->m_min_weight) * std::exp(-exponent);
203 }
204
205 }; // class ExponentialDecrease2
206
207 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
208 // - -
209 // - - - - - - - -
210
221 template <typename Scalar>
223 Scalar m_min_weight{0.4};
224 Scalar m_max_weight{0.95};
225 Scalar m_d_1{0.2};
226 Scalar m_d_2{7.0};
227
228 public:
234
243 ExponentialDecrease3(const Scalar min_weight,
244 const Scalar max_weight,
245 const Scalar d_1,
246 const Scalar d_2)
247 : m_min_weight(min_weight),
248 m_max_weight(max_weight),
249 m_d_1(d_1),
250 m_d_2(d_2) {}
251
258 Scalar operator()(const Integer iteration,
259 const Integer max_iterations) const {
260 const Scalar factor{static_cast<Scalar>(iteration) /
261 static_cast<Scalar>(max_iterations)};
262 const Scalar exponent{1.0 / (1.0 + this->m_d_2 * factor)};
263 return (this->m_max_weight - this->m_min_weight - this->m_d_1) *
264 std::exp(exponent);
265 }
266
267 }; // class ExponentialDecrease3
268
269 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
270 // - -
271 // - - - - - - - -
272
273 /*\
274 | ____ _ _ _ ____
275 | | _ \ __ _ _ __| |_(_) ___| | ___/ ___|_ ____ _ _ __ _ __ ___
276 | | |_) / _` | '__| __| |/ __| |/ _ \___ \ \ /\ / / _` | '__| '_ ` _ \
277 | | __/ (_| | | | |_| | (__| | __/___) \ V V / (_| | | | | | | | |
278 | |_| \__,_|_| \__|_|\___|_|\___|____/ \_/\_/ \__,_|_| |_| |_| |_|
279 |
280 \*/
281
293 template <typename Vector,
294 typename InertiaWeightStrategy =
295 ConstantWeight<typename Vector::Scalar>>
299 : public Optimizer<Vector, ParticleSwarm<Vector>, true> {
300 public:
301 static constexpr bool RequiresFunction{true};
302 static constexpr bool RequiresFirstDerivative{false};
303 static constexpr bool RequiresSecondDerivative{false};
304
306 using Scalar = typename Vector::Scalar;
307
308 using Particles = std::vector<Vector>;
309
310 private:
311 InertiaWeightStrategy
313
314 Scalar m_phi_p{SQRT_EPSILON};
315 Scalar m_phi_g{SQRT_EPSILON};
317
320 std::function<Scalar()> this->m_dice;
321
322 public:
327
332 constexpr std::string name_impl() const {
333 return "ParticleSwarm";
334 }
335
340 void randomize_particles(Particles &particles) const {
341 for (Integer i{0}; i < particles.size(); ++i) {
342 for (Integer j{0}; j < particles.rows(); ++j) {
343 Scalar lb{this->m_lower_bound(j)};
344 Scalar ub{this->m_upper_bound(j)};
345 particles(j, i) = lb + (this->m_dice() * (ub - lb));
346 }
347 }
348 }
349
355 void randomize_velocities(Particles &velocities) const {
356 for (Integer i{0}; i < velocities.cols(); ++i) {
357 for (Integer j{0}; j < velocities.rows(); ++j) {
358 Scalar lb{this->m_lower_bound(j)};
359 Scalar ub{this->m_upper_bound(j)};
360 Scalar diff{ub - lb};
361 Scalar vel{-diff + (this->m_dice() * 2.0 * diff)};
362 velocities(j, i) =
363 std::min(m_max_velocity, std::max(-m_max_velocity, vel));
364 }
365 }
366 }
367
368 void evaluate_objective(const Particles &particles, Vector &fvals) {
369 for (Integer i{0}; i < particles.size(); ++i) {
370 fvals(i) = objective_(particles.col(i));
371 }
372 }
373
374 void maintainBounds(Matrix &particles) const {
375 for (Integer i{0}; i < particles.size(); ++i) {
376 for (Integer j{0}; j < particles.rows(); ++j) {
377 Scalar minval{bounds(0, j)};
378 Scalar maxval{bounds(1, j)};
379 particles[j](i) =
380 std::min(maxval, std::max(minval, particles[j](i)));
381 }
382 }
383 }
384
385 void calculateVelocities(const Matrix &particles,
386 const Matrix &best_particles,
387 const Integer gbest,
388 const Integer iteration,
389 Matrix &velocities) {
390 assert(velocities.rows() == particles.rows());
391 assert(velocities.cols() == particles.size());
392 assert(velocities.rows() == best_particles.rows());
393 assert(velocities.cols() == best_particles.size());
394 assert(gbest < best_particles.size());
395
396 Scalar weight = m_weight_strategy(iteration, this->m_max_iterations);
397
398 for (Integer i{0}; i < velocities.cols(); ++i) {
399 for (Integer j{0}; j < velocities.rows(); ++j) {
400 Scalar velp =
401 this->m_dice() * (best_particles(j, i) - particles(j, i));
402 Scalar velg =
403 this->m_dice() * (best_particles(j, gbest) - particles(j, i));
404 Scalar vel =
405 weight * velocities(j, i) + m_phi_p * velp + m_phi_g * velg;
406
407 if (m_max_velocity > 0)
408 vel = std::min(m_max_velocity, std::max(-m_max_velocity, vel));
409
410 velocities(j, i) = vel;
411 }
412 }
413 }
414
415 Result _minimize(const Matrix &bounds, Matrix &particles) {
416 Matrix velocities(particles.rows(), particles.size());
417
418 Vector fvals(particles.size());
419
420 Matrix best_particles = particles;
421 Vector bestFvals(particles.size());
422
423 Matrix prevParticles(particles.rows(), particles.size());
424 Vector prevFvals(particles.size());
425
426 Vector diff(particles.rows());
427
428 Index gbest = 0;
429
430 // initialize velocities randomly
431 randomize_velocities(bounds, velocities);
432
433 // evaluate objective function for the initial particles
434 evaluate_objective(particles, fvals);
435 bestFvals = fvals;
436 bestFvals.minCoeff(&gbest);
437
438 // init stop conditions
439 Index iterations = 0;
440 Scalar fchange = feps_ + 1;
441 Scalar xchange = xeps_ + 1;
442
443 while ((this->m_iterations < this->m_max_iterations) &&
444 fchange > feps_ && xchange > xeps_) {
445 // calculate new velocities
446 calculateVelocities(particles,
447 best_particles,
448 gbest,
450 velocities);
451
452 // move particles by velocity and stay within bounds
453 particles += velocities;
454 maintainBounds(bounds, particles);
455
456 // evaluate objective for moved particles
457 evaluate_objective(particles, fvals);
458
459 prevParticles = best_particles;
460 prevFvals = bestFvals;
461
462 for (Integer i{0}; i < fvals.size(); ++i) {
463 // check if there was an improvement and update best vals
464 if (fvals(i) < bestFvals(i)) {
465 bestFvals(i) = fvals(i);
466 best_particles.col(i) = particles.col(i);
467 }
468 }
469 bestFvals.minCoeff(&gbest);
470
471 // calculate new diffs
472 xchange = (best_particles - prevParticles).colwise().norm().sum();
473 fchange = (bestFvals - prevFvals).array().abs().sum();
474
475 xchange /= best_particles.size();
476 fchange /= bestFvals.size();
477
478 if (this->m_verbose) {
479 this->info(gbest);
480 }
481
482 ++iterations;
483 }
484
485 result.iterations = iterations;
486 result.converged = fchange <= feps_ || xchange <= xeps_;
487 result.fval = bestFvals(gbest);
488 result.xval = best_particles.col(gbest);
489
490 return result;
491 }
492
493 public:
496 xeps_(static_cast<Scalar>(1e-6)),
497 feps_(static_cast<Scalar>(1e-6)),
498 m_phi_p(static_cast<Scalar>(2.0)),
499 m_phi_g(static_cast<Scalar>(2.0)),
500 m_max_velocity(static_cast<Scalar>(0.0)),
501 this->m_verbose(0),
502 this->m_dice() {
503 std::default_random_engine gen(std::time(0));
504 std::uniform_real_distribution<Scalar> distrib(0.0, 1.0);
505 this->m_dice = std::bind(distrib, gen);
506 }
507
515 const Scalar change = ParticleSwarm::SQRT_EPSILON) {
516 xeps_ = change;
517 }
518
524 const Scalar change = ParticleSwarm::SQRT_EPSILON) {
525 feps_ = change;
526 }
527
535 void setPhiParticles(const Scalar phip) {
536 m_phi_p = phip;
537 }
538
546 void setPhiGlobal(const Scalar phig) {
547 m_phi_g = phig;
548 }
549
555 void setMaxVelocity(const Scalar max_velocity) {
556 this->m_max_velocity = max_velocity;
557 }
558
560 const InertiaWeightStrategy &t_weightStrategy) {
561 this->m_weight_strategy = t_weightStrategy;
562 }
563
575 Result minimize(const Integer cnt) {
576 if (cnt == 0)
577 throw std::runtime_error("particle count cannot be 0");
578 if (bounds.rows() != 2)
579 throw std::runtime_error("bounds has not exactly 2 rows (min, max)");
580 for (Integer i{0}; i < bounds.cols(); ++i) {
581 if (bounds(0, i) >= bounds(1, i))
582 throw std::runtime_error("bounds min is greater than max");
583 }
584
585 Matrix particles(bounds.cols(), cnt);
586 randomize_particles(bounds, particles);
587
588 return _minimize(bounds, particles);
589 }
590
608 Result minimize(const Integer cnt, const Vector &initGuess) {
609 if (cnt == 0)
610 throw std::runtime_error("particle count cannot be 0");
611 if (bounds.rows() != 2)
612 throw std::runtime_error("bounds has not exactly 2 rows (min, max)");
613 for (Integer i{0}; i < bounds.cols(); ++i) {
614 if (bounds(0, i) >= bounds(1, i))
615 throw std::runtime_error("bounds min is greater than max");
616 }
617 if (bounds.cols() != initGuess.size())
618 throw std::runtime_error(
619 "init guess and bounds have different dimensions");
620
621 Matrix particles(bounds.cols(), cnt);
622 randomize_particles(bounds, particles);
623 particles.col(0) = initGuess;
624 maintainBounds(bounds, particles);
625
626 return _minimize(bounds, particles);
627 }
628
638 Result minimize(Matrix &particles) {
639 if (bounds.rows() != 2)
640 throw std::runtime_error("bounds has not exactly 2 rows (min, max)");
641 if (bounds.cols() != particles.rows())
642 throw std::runtime_error(
643 "columns of bounds and rows of "
644 "particles do not match");
645 for (Integer i{0}; i < bounds.cols(); ++i) {
646 if (bounds(0, i) >= bounds(1, i))
647 throw std::runtime_error("bounds min is greater than max");
648 }
649
650 maintainBounds(bounds, particles);
651
652 return _minimize(bounds, particles);
653 }
654
655 void getRandomParticles(const Integer cnt, Matrix &particles) {
656 particles.resize(bounds.cols(), cnt);
657 randomize_particles(bounds, particles);
658 }
659
660 }; // class ParticleSwarmOptimization
661
662 } // namespace Optimizer
663
664} // namespace Optimist
665
666#endif // OPTIMIST_OPTIMIZER_PARTICLE_SWARM_HH
Scalar m_weight
Definition ParticleSwarm.hh:27
ConstantWeight(const Scalar weight)
Definition ParticleSwarm.hh:39
Scalar operator()(const Integer, const Integer) const
Definition ParticleSwarm.hh:47
ConstantWeight()
Definition ParticleSwarm.hh:33
Scalar operator()(const Integer iteration, const Integer max_iterations) const
Definition ParticleSwarm.hh:145
ExponentialDecrease1(const Scalar min_weight, const Scalar max_weight)
Definition ParticleSwarm.hh:136
ExponentialDecrease1()
Definition ParticleSwarm.hh:129
Scalar m_min_weight
Definition ParticleSwarm.hh:121
Scalar m_max_weight
Definition ParticleSwarm.hh:122
Scalar operator()(const Integer iteration, const Integer max_iterations) const
Definition ParticleSwarm.hh:196
ExponentialDecrease2()
Definition ParticleSwarm.hh:179
Scalar m_max_weight
Definition ParticleSwarm.hh:172
Scalar m_min_weight
Definition ParticleSwarm.hh:171
ExponentialDecrease2(const Scalar min_weight, const Scalar max_weight)
Definition ParticleSwarm.hh:187
Scalar m_min_weight
Definition ParticleSwarm.hh:223
ExponentialDecrease3()
Definition ParticleSwarm.hh:233
ExponentialDecrease3(const Scalar min_weight, const Scalar max_weight, const Scalar d_1, const Scalar d_2)
Definition ParticleSwarm.hh:243
Scalar m_d_1
Definition ParticleSwarm.hh:225
Scalar operator()(const Integer iteration, const Integer max_iterations) const
Definition ParticleSwarm.hh:258
Scalar m_d_2
Definition ParticleSwarm.hh:226
Scalar m_max_weight
Definition ParticleSwarm.hh:224
LinearDecrease(const Scalar min_weight, const Scalar max_weight)
Definition ParticleSwarm.hh:86
Scalar m_max_weight
Definition ParticleSwarm.hh:71
Scalar operator()(const Integer iteration, const Integer max_iterations) const
Definition ParticleSwarm.hh:95
Scalar m_min_weight
Definition ParticleSwarm.hh:70
LinearDecrease()
Definition ParticleSwarm.hh:78
Result _minimize(const Matrix &bounds, Matrix &particles)
Definition ParticleSwarm.hh:415
constexpr std::string name_impl() const
Definition ParticleSwarm.hh:332
std::function< Scalar()> this m_dice
Definition ParticleSwarm.hh:320
static constexpr bool RequiresSecondDerivative
Definition ParticleSwarm.hh:303
Result minimize(const Integer cnt)
Definition ParticleSwarm.hh:575
void setPhiGlobal(const Scalar phig)
Definition ParticleSwarm.hh:546
static constexpr bool RequiresFunction
Definition ParticleSwarm.hh:301
void setMaxVelocity(const Scalar max_velocity)
Definition ParticleSwarm.hh:555
Scalar m_phi_g
Definition ParticleSwarm.hh:315
Scalar feps_
Definition ParticleSwarm.hh:319
ParticleSwarm()
Definition ParticleSwarm.hh:326
InertiaWeightStrategy m_weight_strategy
Definition ParticleSwarm.hh:312
void randomize_particles(Particles &particles) const
Definition ParticleSwarm.hh:340
Result minimize(const Integer cnt, const Vector &initGuess)
Definition ParticleSwarm.hh:608
ParticleSwarmOptimization()
Definition ParticleSwarm.hh:494
Scalar xeps_
Definition ParticleSwarm.hh:318
Scalar m_phi_p
Definition ParticleSwarm.hh:314
void maintainBounds(Matrix &particles) const
Definition ParticleSwarm.hh:374
void setMinParticleChange(const Scalar change=ParticleSwarm::SQRT_EPSILON)
Definition ParticleSwarm.hh:514
void getRandomParticles(const Integer cnt, Matrix &particles)
Definition ParticleSwarm.hh:655
static constexpr bool RequiresFirstDerivative
Definition ParticleSwarm.hh:302
void evaluate_objective(const Particles &particles, Vector &fvals)
Definition ParticleSwarm.hh:368
void inertia_weight_strategy(const InertiaWeightStrategy &t_weightStrategy)
Definition ParticleSwarm.hh:559
typename Vector::Scalar Scalar
Definition ParticleSwarm.hh:306
void randomize_velocities(Particles &velocities) const
Definition ParticleSwarm.hh:355
TypeTrait< Vector > VectorTrait
Definition ParticleSwarm.hh:305
void setPhiParticles(const Scalar phip)
Definition ParticleSwarm.hh:535
void setMinFunctionChange(const Scalar change=ParticleSwarm::SQRT_EPSILON)
Definition ParticleSwarm.hh:523
std::vector< Vector > Particles
Definition ParticleSwarm.hh:308
void calculateVelocities(const Matrix &particles, const Matrix &best_particles, const Integer gbest, const Integer iteration, Matrix &velocities)
Definition ParticleSwarm.hh:385
Result minimize(Matrix &particles)
Definition ParticleSwarm.hh:638
void info(Scalar residuals, const std::string &notes="-")
Definition SolverBase.hh:1230
void bounds(const Vector &t_lower_bound, const Vector &t_upper_bound)
Definition SolverBase.hh:313
Namespace for the Optimist library.
Definition Optimist.hh:89
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:97
Definition Optimist.hh:113