Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
PSO.cpp
Go to the documentation of this file.
1
6#include "PSO.hpp"
7#include "../rng/rng_factory.hpp"
8#include <stdexcept>
9#include <cstdint>
10
11#ifdef _OPENMP
12 #include <omp.h>
13#endif
14
15namespace mc{
16namespace optim{
17
18 PSO::PSO(const PSOConfig& config)
19 : m_config(config),
20 m_global_best(Solution::make_worst(OptimizationMode::MINIMIZE))
21 {}
22
24 m_func = std::move(func);
25 m_initialized = false;
26 }
27
29 m_callback = std::move(cb);
30 }
31
32 void PSO::setBounds(const Coordinates& lower, const Coordinates& upper) {
33 if (lower.size() != upper.size()) {
34 throw std::invalid_argument("Lower and Upper bounds must have the same dimension.");
35 }
36 m_lower_bounds = lower;
37 m_upper_bounds = upper;
38 m_initialized = false;
39 }
40
42 m_mode = mode;
43 m_global_best = Solution::make_worst(m_mode);
44 m_initialized = false;
45 }
46
47 void PSO::initialize() {
48 if (!m_func) throw std::runtime_error("Objective function not set.");
49 if (m_lower_bounds.empty()) throw std::runtime_error("Bounds not set.");
50
51 m_swarm.resize(m_config.population_size);
52 const size_t dim = m_lower_bounds.size();
53 m_current_iter = 0; // Reset iteration counter for deterministic seeding
54
55 // Init positions/velocities and evaluate
56 #ifdef _OPENMP
57 #pragma omp parallel for schedule(static)
58 #endif
59 for (int p_idx = 0; p_idx < static_cast<int>(m_swarm.size()); ++p_idx) {
60 auto& p = m_swarm[static_cast<size_t>(p_idx)];
61
62 // Deterministic RNG decoupled from thread scheduling.
63 // Unique stream per particle.
64 auto local_gen = mc::rng::make_engine(1000ULL + static_cast<std::uint64_t>(p_idx));
65 std::uniform_real_distribution<Real> dist(0.0, 1.0);
66
67 p.position.resize(dim);
68 p.velocity.resize(dim);
69
70 for (size_t i = 0; i < dim; ++i) {
71 const Real span = m_upper_bounds[i] - m_lower_bounds[i];
72 p.position[i] = m_lower_bounds[i] + dist(local_gen) * span;
73 p.velocity[i] = (dist(local_gen) - 0.5) * span * 0.1;
74 }
75
76 p.current_value = m_func(p.position);
77 p.best_position = p.position;
78 p.best_value = p.current_value;
79 }
80
81 // Find global best (small, keep it simple)
82 m_global_best = Solution::make_worst(m_mode);
83 for (const auto& p : m_swarm) {
84 Solution current_sol{p.position, p.current_value};
85 if (current_sol.isBetterThan(m_global_best, m_mode)) {
86 m_global_best = current_sol;
87 }
88 }
89
90 m_initialized = true;
91 }
92
93 void PSO::step() {
94 if (!m_initialized) initialize();
95
96 const size_t dim = m_lower_bounds.size();
97 const Solution old_global_best = m_global_best;
98
99 #ifdef _OPENMP
100 #pragma omp parallel for schedule(static)
101 #endif
102 for (int p_idx = 0; p_idx < static_cast<int>(m_swarm.size()); ++p_idx) {
103 auto& p = m_swarm[static_cast<size_t>(p_idx)];
104
105 // Deterministic RNG: mix iteration count into stream id so values change each step.
106 std::uint64_t stream_id = 2000ULL +
107 (static_cast<std::uint64_t>(m_current_iter) * m_config.population_size) +
108 static_cast<std::uint64_t>(p_idx);
109 auto local_gen = mc::rng::make_engine(stream_id);
110 std::uniform_real_distribution<Real> r_dist(0.0, 1.0);
111
112 for (size_t i = 0; i < dim; ++i) {
113 const Real r1 = r_dist(local_gen);
114 const Real r2 = r_dist(local_gen);
115
116 const Real cognitive_comp =
117 m_config.cognitive_coeff * r1 * (p.best_position[i] - p.position[i]);
118 const Real social_comp =
119 m_config.social_coeff * r2 * (old_global_best.params[i] - p.position[i]);
120
121 p.velocity[i] =
122 (m_config.inertia_weight * p.velocity[i]) + cognitive_comp + social_comp;
123
124 p.position[i] += p.velocity[i];
125 }
126
127 enforceBounds(p);
128
129 const Real new_val = m_func(p.position);
130 p.current_value = new_val;
131
132 Solution new_sol{p.position, new_val};
133 Solution pbest_sol{p.best_position, p.best_value};
134
135 if (new_sol.isBetterThan(pbest_sol, m_mode)) {
136 p.best_value = new_val;
137 p.best_position = p.position;
138 }
139
140 // Global best update moved to a serial post-processing section for determinism
141 }
142
143 // Serial update of global best to ensure deterministic tie-breaking
144 for (const auto& p : m_swarm) {
145 Solution sol{p.best_position, p.best_value};
146 if (sol.isBetterThan(m_global_best, m_mode)) {
147 m_global_best = sol;
148 }
149 }
150
151 // Advance iteration counter for next step's RNG seeding
152 ++m_current_iter;
153 }
154
155 void PSO::enforceBounds(Particle& p) {
156 for (size_t i = 0; i < p.position.size(); ++i) {
157 if (p.position[i] < m_lower_bounds[i]) {
158 p.position[i] = m_lower_bounds[i];
159 p.velocity[i] *= -0.5;
160 } else if (p.position[i] > m_upper_bounds[i]) {
161 p.position[i] = m_upper_bounds[i];
162 p.velocity[i] *= -0.5;
163 }
164 }
165 }
166
168 initialize();
169 for (size_t i = 0; i < m_config.max_iterations; ++i) {
170 step();
171 if (m_callback) {
172 m_callback(m_global_best, i);
173 }
174 }
175 return m_global_best;
176 }
177
179 return m_global_best;
180 }
181
182} //namespace mc
183} //namespace optim
Particle Swarm Optimization (PSO) interface and data structures.
std::function< void(const Solution &current_best, size_t iteration)> StepCallback
Callback invoked after each step/generation.
Definition optimizer.hpp:25
Solution optimize() override
Execute the optimization loop for max_iterations.
Definition PSO.cpp:167
PSO(const PSOConfig &config=PSOConfig{})
Construct a PSO optimizer with the given configuration.
Definition PSO.cpp:18
void setBounds(const Coordinates &lower, const Coordinates &upper) override
Set lower/upper bounds of the search hyper-rectangle.
Definition PSO.cpp:32
void setCallback(StepCallback cb) override
Register a callback invoked after each step().
Definition PSO.cpp:28
void setObjectiveFunction(ObjectiveFunction func) override
Set the objective function to optimize.
Definition PSO.cpp:23
Solution getBestSolution() const override
Get the best solution found so far.
Definition PSO.cpp:178
void step() override
Perform one PSO iteration: update velocity/position, evaluate, update personal and global bests.
Definition PSO.cpp:93
void setMode(OptimizationMode mode) override
Set optimization mode (minimize or maximize).
Definition PSO.cpp:41
constexpr int dim
Default dimensionality for integration.
Definition main.cpp:36
OptimizationMode
Optimization goal.
Definition types.hpp:42
std::function< Real(const Coordinates &)> ObjectiveFunction
Objective function signature.
Definition types.hpp:37
std::vector< Real > Coordinates
A point in the N-dimensional search space.
Definition types.hpp:31
double Real
Scalar precision used across optimizers.
Definition types.hpp:26
std::mt19937 make_engine(std::uint64_t stream_id)
Create a deterministic RNG engine for a specific stream.
Configuration parameters for PSO.
Definition PSO.hpp:21
size_t population_size
Number of particles in the swarm.
Definition PSO.hpp:23
Real social_coeff
Social coefficient (c2): scales attraction to global best.
Definition PSO.hpp:32
Real cognitive_coeff
Cognitive coefficient (c1): scales attraction to particle best.
Definition PSO.hpp:30
Real inertia_weight
Inertia weight (w): scales previous velocity.
Definition PSO.hpp:28
size_t max_iterations
Number of iterations to run the optimizer.
Definition PSO.hpp:25
Represents a candidate solution in the search space.
Definition types.hpp:51
static Solution make_worst(OptimizationMode mode)
Helper to create a worst-case solution for initialization.
Definition types.hpp:62
Coordinates params
Parameter vector (coordinates in the search space).
Definition types.hpp:53