Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
pso_benchmarks.cpp
Go to the documentation of this file.
1
28//
29// PSO (Particle Swarm Optimization) benchmarks
30//
31
32#include "apps/benchmarks.hpp"
33#include <cmath>
34#include <cstdint>
35
36namespace opt = mc::optim;
37
44void runSphereTest(opt::PSO& pso, const opt::Coordinates& lower, const opt::Coordinates& upper) {
45 std::cout << "Optimization Problem: Minimize Sphere Function in 2D" << std::endl;
46 std::cout << "Search Space: [-10, 10] per dimension" << std::endl;
47 std::cout << "Running optimizer..." << std::endl;
48
49 // Define the objective function: Sphere Function f(x) = sum(x_i^2)
50 opt::ObjectiveFunction sphere_function = [](const opt::Coordinates& coords) {
51 opt::Real sum = 0.0;
52 for (auto val : coords) {
53 sum += val * val;
54 }
55 return sum;
56 };
57
58 // Set up the optimizer
59 pso.setBounds(lower, upper);
60 pso.setObjectiveFunction(sphere_function);
61 pso.setMode(opt::OptimizationMode::MINIMIZE);
62
63 // Set a callback to print progress every 10 iterations
64 pso.setCallback([](const opt::Solution& current_best, size_t iteration) {
65 if (iteration == 0 || iteration % 10 == 0) {
66 std::cout << "[Sphere Test | Step " << std::setw(3) << iteration << "] "
67 << "Best Value: " << std::scientific << std::setprecision(5)
68 << current_best.value << std::defaultfloat << std::endl;
69 }
70 });
71
72 // Execute
73 auto start = std::chrono::high_resolution_clock::now();
74 opt::Solution best_sol = pso.optimize();
75 auto end = std::chrono::high_resolution_clock::now();
76 std::chrono::duration<double, std::milli> duration = end - start;
77
78 // Report
79 std::cout << "\nOptimization Completed in " << duration.count() << " ms." << std::endl;
80 std::cout << "Best Value Found: " << std::fixed << std::setprecision(10) << best_sol.value << std::endl;
81 std::cout << "Best Position: [ ";
82 for (auto val : best_sol.params) {
83 std::cout << std::fixed << std::setprecision(5) << val << " ";
84 }
85 std::cout << "]" << std::endl;
86}
87
95void runBoundaryTest(opt::PSO& pso, const opt::Coordinates& lower, const opt::Coordinates& upper) {
96 std::cout << "\n-------------------------------------------" << std::endl;
97 std::cout << "TEST 2: Boundary Constraint Test (Linear Plane)" << std::endl;
98 std::cout << "Objective: f(x,y) = x + y (Minimization)" << std::endl;
99 std::cout << "Search Space: [-10, 10] per dimension" << std::endl;
100 std::cout << "Expected Result: -20.0 at [-10.0, -10.0]" << std::endl;
101 std::cout << "Running optimizer..." << std::endl;
102
103 // Define the linear function
104 opt::ObjectiveFunction plane_function = [](const opt::Coordinates& coords) {
105 opt::Real sum = 0.0;
106 for (auto val : coords) {
107 sum += val;
108 }
109 return sum;
110 };
111
112 // Set up the optimizer (reusing the instance)
113 pso.setBounds(lower, upper);
114 pso.setObjectiveFunction(plane_function);
115 // Mode is already MINIMIZE, but good practice to ensure
116 pso.setMode(opt::OptimizationMode::MINIMIZE);
117
118 // Callback to print progress
119 pso.setCallback([](const opt::Solution& current_best, size_t iteration) {
120 if (iteration % 20 == 0) {
121 std::cout << "[Boundary Test | Step " << std::setw(3) << iteration << "] "
122 << "Val: " << std::fixed << std::setprecision(4) << current_best.value << std::endl;
123 }
124 });
125
126 // Execute
127 auto start = std::chrono::high_resolution_clock::now();
128 opt::Solution best_sol = pso.optimize();
129 auto end = std::chrono::high_resolution_clock::now();
130 std::chrono::duration<double, std::milli> duration = end - start;
131
132 // Report
133 std::cout << "Optimization Completed in " << duration.count() << " ms." << std::endl;
134 std::cout << "Best Value Found: " << std::fixed << std::setprecision(5) << best_sol.value << std::endl;
135 std::cout << "Best Position: [ ";
136 for (auto val : best_sol.params) {
137 std::cout << std::fixed << std::setprecision(5) << val << " ";
138 }
139 std::cout << "]" << std::endl;
140
141 // Verification
142 if (std::abs(best_sol.value - (-20.0)) < 1e-3) {
143 std::cout << ">> SUCCESS: Boundary minimum found correctly!" << std::endl;
144 } else {
145 std::cout << ">> WARNING: Did not reach the exact boundary." << std::endl;
146 }
147}
148
161 std::cout << "\n-------------------------------------------" << std::endl;
162 std::cout << "TEST 3: High-Dimensional Stress Test (Rastrigin Function)" << std::endl;
163 std::cout << "Dimension: " << dim << "D" << std::endl;
164 std::cout << "Search Space: [-5.12, 5.12] per dimension" << std::endl;
165 std::cout << "Goal: Find global minimum 0.0 (avoiding local traps)" << std::endl;
166
167 // 1. Define the Rastrigin function
168 opt::ObjectiveFunction rastrigin_func = [dim](const opt::Coordinates& coords) {
169 double sum = 0.0;
170 double A = 10.0;
171 // M_PI is standard in <cmath>, if missing use 3.14159265358979323846
172 double pi = 3.14159265358979323846;
173
174 for (auto x : coords) {
175 sum += (x * x) - (A * std::cos(2.0 * pi * x));
176 }
177 return A * dim + sum;
178 };
179
180 // 2. Define Bounds for N dimensions
181 opt::Coordinates lower(dim, -5.12);
182 opt::Coordinates upper(dim, 5.12);
183
184 // 3. Configure PSO specifically for a harder problem
185 // We need more particles and more time to explore 10 dimensions
186 opt::PSOConfig hard_config;
187 hard_config.population_size = 500; // Increased from 50 - with population 100 only finds local minima
188 hard_config.max_iterations = 1000; // Increased from 100
189 hard_config.inertia_weight = 0.729; // Classic "constriction factor" value
190 hard_config.cognitive_coeff = 1.49;
191 hard_config.social_coeff = 1.49;
192
193 // We create a new local instance to not mess up the previous config
194 opt::PSO local_pso(hard_config);
195
196 local_pso.setBounds(lower, upper);
197 local_pso.setObjectiveFunction(rastrigin_func);
198 local_pso.setMode(opt::OptimizationMode::MINIMIZE);
199
200 // Minimal callback to show we are alive
201 local_pso.setCallback([](const opt::Solution& sol, size_t i) {
202 if (i % 100 == 0) {
203 std::cout << "[Rastrigin " << i << "] Best: "
204 << std::scientific << std::setprecision(4) << sol.value
205 << std::defaultfloat << std::endl;
206 }
207 });
208
209 std::cout << "Running optimizer (this might take longer)..." << std::endl;
210
211 auto start = std::chrono::high_resolution_clock::now();
212 opt::Solution best_sol = local_pso.optimize();
213 auto end = std::chrono::high_resolution_clock::now();
214 std::chrono::duration<double, std::milli> duration = end - start;
215
216 std::cout << "Optimization Completed in " << duration.count() << " ms." << std::endl;
217 std::cout << "Best Value Found: " << std::fixed << std::setprecision(5) << best_sol.value << std::endl;
218
219 // Validation: It's very hard to get exactly 0.0 in 10D without huge resources.
220 // Anything below 1.0 is considered a "good" result for a basic PSO.
221 // Anything close to 0.0 is excellent.
222 if (best_sol.value < 1e-2) {
223 std::cout << ">> SUCCESS: Global minimum found!" << std::endl;
224 } else if (best_sol.value < 5.0) {
225 std::cout << ">> ACCEPTABLE: Found a good local minimum, but not global." << std::endl;
226 } else {
227 std::cout << ">> FAIL: Stuck in a high local minimum." << std::endl;
228 }
229}
230
232 std::cout << "===========================================" << std::endl;
233 std::cout << " Visual PSO Benchmark (2D Animation)" << std::endl;
234 std::cout << "===========================================" << std::endl;
235
236 // 1. Configuration
237 opt::PSOConfig config;
238 config.population_size = 60; // 30 particles
239 config.max_iterations = 100; // 50 frames for animation
240 opt::PSO pso(config);
241
242 // 2. Objective Function (Rastrigin 2D)
243 // Global minimum is at (0,0)
244 auto rastrigin = [](const opt::Coordinates& x) {
245 double A = 10.0;
246 double sum = 0.0;
247 // M_PI is typically defined in <cmath>, ensure it is available or use 3.14...
248 double pi = 3.14159265358979323846;
249 for (double val : x) sum += val*val - A*std::cos(2*pi*val);
250 return 2*A + sum;
251 };
252
253 pso.setObjectiveFunction(rastrigin);
254 pso.setBounds({-5.12, -5.12}, {5.12, 5.12});
255 pso.setMode(opt::OptimizationMode::MINIMIZE);
256
257 // 3. Prepare Plotting
258 std::string baseName = "pso_vis";
259 std::string gridFile = "pso_grid.dat";
260
261 std::cout << "Generating background grid (heatmap)..." << std::endl;
262 // Save the static background (function landscape)
263 mc::utils::saveFunctionGrid(gridFile, rastrigin, -5.12, 5.12, -5.12, 5.12, 100);
264
265 // 4. Set Callback to save each frame
266 pso.setCallback([&](const opt::Solution&, size_t iter) {
267 // Use the public getter to access particle positions
268 mc::utils::saveSwarmFrame(baseName, iter, pso.getParticles());
269 std::cout << "Saved frame " << iter << "/" << config.max_iterations << "\r" << std::flush;
270 });
271
272 // 5. Run Optimization
273 std::cout << "Running optimization..." << std::endl;
274 pso.optimize();
275 std::cout << "\nOptimization finished." << std::endl;
276
277 // 6. Launch Animation
278 std::cout << "Launching Gnuplot animation..." << std::endl;
279 mc::utils::createPSOAnimationScript("run_pso.gp", gridFile, baseName, config.max_iterations, "PSO Rastrigin 2D");
280}
281
283 std::cout << "===========================================" << std::endl;
284 std::cout << " Visual PSO Benchmark (3D Animation)" << std::endl;
285 std::cout << "===========================================" << std::endl;
286
287 // 1. Configuration
288 opt::PSOConfig config;
289 config.population_size = 100;
290 config.max_iterations = 150;
291 opt::PSO pso(config);
292
293 // 2. Objective: 3D Rastrigin Function
294 auto rastrigin3D = [](const opt::Coordinates& x) {
295 double sum = 0.0;
296 double A = 10.0;
297 double pi = 3.14159265358979323846;
298 for (double val : x) {
299 sum += val * val - A * std::cos(2 * pi * val);
300 }
301 return 3.0 * A + sum;
302 };
303
304 pso.setObjectiveFunction(rastrigin3D);
305
306 // Bounds [-5.12, 5.12]
307 double min_b = -5.12;
308 double max_b = 5.12;
309 pso.setBounds({min_b, min_b, min_b}, {max_b, max_b, max_b});
310 pso.setMode(opt::OptimizationMode::MINIMIZE);
311
312 // 3. Setup Visualization
313 std::string baseName = "pso_vis_3d";
314 std::string slicesFile = "pso_slices_3d.dat"; // [NEW] File for wall heatmaps
315
316 // [NEW] Generate the 3D Slices (Walls)
317 std::cout << "Generating 3D function slices (walls)..." << std::endl;
318 mc::utils::saveFunctionSlices3D(slicesFile, rastrigin3D, min_b, max_b, 50); // 50 = resolution
319
320 // 4. Callback
321 pso.setCallback([&](const opt::Solution&, size_t iter) {
322 mc::utils::saveSwarmFrame(baseName, iter, pso.getParticles());
323 if (iter % 10 == 0) std::cout << "Generating Frame " << iter << "/" << config.max_iterations << "\r" << std::flush;
324 });
325
326 // 5. Run
327 std::cout << "Running 3D optimization..." << std::endl;
328 pso.optimize();
329 std::cout << "\nOptimization finished." << std::endl;
330
331 // 6. Launch 3D Animation (Pass slicesFile)
332 std::cout << "Launching Gnuplot 3D animation..." << std::endl;
333 mc::utils::createPSOAnimationScript3D("run_pso_3d.gp", slicesFile, baseName, config.max_iterations, "PSO 3D Rastrigin", min_b, max_b);
334}
335
336// --- Main Optimization Benchmark Entry Point ---
337
339 std::cout << "=========================================" << std::endl;
340 std::cout << " Particle Swarm Optimization (PSO) Benchmark" << std::endl;
341 std::cout << "=========================================" << std::endl;
342
343 // 1. Configuration for PSO
344 opt::PSOConfig config;
345 config.population_size = 50; // Number of particles
346 config.max_iterations = 100; // Iterations
347 config.inertia_weight = 0.7;
348 config.cognitive_coeff = 1.5;
349 config.social_coeff = 1.5;
350
351 // 2. Instantiate PSO
352 opt::PSO pso(config);
353
354 // 3. Define Shared Bounds [-10, 10]
355 opt::Coordinates lower_bounds = {-10.0, -10.0};
356 opt::Coordinates upper_bounds = {10.0, 10.0};
357
358 try {
359 // Run Test 1
360 runSphereTest(pso, lower_bounds, upper_bounds);
361
362 // Run Test 2
363 runBoundaryTest(pso, lower_bounds, upper_bounds);
364
365 // Run Test 3: 10-Dimensional Rastrigin
366 runRastriginTest(pso, 10);
367
368 // Run visual test:
370
371 // Run 3D visual test
373
374 } catch (const std::exception& e) {
375 std::cerr << "Optimization failed: " << e.what() << std::endl;
376 }
377}
Benchmarking framework for Monte Carlo integration algorithms.
Particle Swarm Optimization algorithm.
Definition PSO.hpp:40
Solution optimize() override
Execute the optimization loop for max_iterations.
Definition PSO.cpp:167
const std::vector< Particle > & getParticles() const
Access the current swarm state (particles).
Definition PSO.hpp:102
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
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
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
void createPSOAnimationScript3D(const std::string &scriptName, const std::string &slicesFile, const std::string &swarmBasename, size_t max_iter, const std::string &title, double min_bound, double max_bound)
Definition plotter.hpp:403
void saveFunctionGrid(const std::string &filename, const Func &func, double x_min, double x_max, double y_min, double y_max, int resolution=100)
Definition plotter.hpp:223
void createPSOAnimationScript(const std::string &scriptName, const std::string &gridFile, const std::string &swarmBasename, size_t max_iter, const std::string &title)
Definition plotter.hpp:295
void saveSwarmFrame(const std::string &basename, size_t iteration, const std::vector< ParticleT > &swarm)
Definition plotter.hpp:263
void saveFunctionSlices3D(const std::string &filename, const Func &func, double min, double max, int resolution=50)
Definition plotter.hpp:338
void runSphereTest(opt::PSO &pso, const opt::Coordinates &lower, const opt::Coordinates &upper)
Test 1: Sphere Function.
void runVisualPSOBenchmark()
void runRastriginTest(opt::PSO &pso, int dim)
Test 3: High-Dimensional Rastrigin Function Objective: Minimize f(x) = 10n + sum(x_i^2 - 10cos(2*pi*x...
void runVisualPSO3DBenchmark()
void runBoundaryTest(opt::PSO &pso, const opt::Coordinates &lower, const opt::Coordinates &upper)
Test 2: Boundary Constraint Test Objective: Minimize f(x, y) = x + y This function is a constant incl...
void runOptimizationBenchmarksPSO()
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
Coordinates params
Parameter vector (coordinates in the search space).
Definition types.hpp:53
Real value
Evaluated objective value for params.
Definition types.hpp:55