Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
ga_benchmarks.cpp
Go to the documentation of this file.
1
28//
29// GA (Genetic Algorithm) benchmarks
30//
31
32#include "apps/benchmarks.hpp"
33#include <cmath>
34#include <cstdint>
35#include <sstream>
36#include <filesystem>
37
38namespace opt = mc::optim;
39
40// --- GA Helper Functions ---
41
48static std::string makeFrameName(const std::string& baseName, size_t iter) {
49 std::ostringstream oss;
50 oss << baseName << "_iter_" << iter << ".dat";
51 return oss.str();
52}
53
63static void savePopulationFrame2D(const std::string& baseName, size_t iter,
64 const std::vector<opt::GA::Individual>& pop) {
65 // Create ga_frames subdirectory
66 std::string dir = "./ga_frames";
67 try {
68 std::filesystem::create_directories(dir);
69 } catch (const std::exception&) {}
70
71 std::string filename = dir + "/" + makeFrameName(baseName, iter);
72 std::ofstream out(filename);
73 if (!out.is_open()) {
74 std::cerr << "Warning: Could not create file " << filename << std::endl;
75 return;
76 }
77 for (const auto& ind : pop) {
78 if (ind.genome.size() >= 2) {
79 out << ind.genome[0] << " " << ind.genome[1] << "\n";
80 }
81 }
82 out.close();
83}
84
85static void savePopulationFrame3D(const std::string& baseName, size_t iter,
86 const std::vector<opt::GA::Individual>& pop) {
87 // Create ga_frames subdirectory
88 std::string dir = "./ga_frames";
89 try {
90 std::filesystem::create_directories(dir);
91 } catch (const std::exception&) {}
92
93 std::string filename = dir + "/" + makeFrameName(baseName, iter);
94 std::ofstream out(filename);
95 if (!out.is_open()) {
96 std::cerr << "Warning: Could not create file " << filename << std::endl;
97 return;
98 }
99 for (const auto& ind : pop) {
100 if (ind.genome.size() >= 3) {
101 out << ind.genome[0] << " " << ind.genome[1] << " " << ind.genome[2] << "\n";
102 }
103 }
104 out.close();
105}
106
107// --- GA Test Functions ---
108
109void runSphereTest(opt::GA& ga, const opt::Coordinates& lower, const opt::Coordinates& upper) {
110 std::cout << "Optimization Problem: Minimize Sphere Function in 2D" << std::endl;
111 std::cout << "Search Space: [-10, 10] per dimension" << std::endl;
112 std::cout << "Running optimizer..." << std::endl;
113
114 opt::ObjectiveFunction sphere_function = [](const opt::Coordinates& coords) {
115 opt::Real sum = 0.0;
116 for (auto val : coords) sum += val * val;
117 return sum;
118 };
119
120 ga.setBounds(lower, upper);
121 ga.setObjectiveFunction(sphere_function);
122 ga.setMode(opt::OptimizationMode::MINIMIZE);
123
124 ga.setCallback([](const opt::Solution& current_best, size_t iteration) {
125 if (iteration == 0 || iteration % 10 == 0) {
126 std::cout << "[Sphere Test | Step " << std::setw(3) << iteration << "] "
127 << "Best Value: " << std::scientific << std::setprecision(5)
128 << current_best.value << std::defaultfloat << std::endl;
129 }
130 });
131
132 auto start = std::chrono::high_resolution_clock::now();
133 opt::Solution best_sol = ga.optimize();
134 auto end = std::chrono::high_resolution_clock::now();
135 std::chrono::duration<double, std::milli> duration = end - start;
136
137 std::cout << "\nOptimization Completed in " << duration.count() << " ms." << std::endl;
138 std::cout << "Best Value Found: " << std::fixed << std::setprecision(10) << best_sol.value << std::endl;
139 std::cout << "Best Position: [ ";
140 for (auto val : best_sol.params) {
141 std::cout << std::fixed << std::setprecision(5) << val << " ";
142 }
143 std::cout << "]" << std::endl;
144}
145
146void runBoundaryTest(opt::GA& ga, const opt::Coordinates& lower, const opt::Coordinates& upper) {
147 std::cout << "\n-------------------------------------------" << std::endl;
148 std::cout << "TEST 2: Boundary Constraint Test (Linear Plane)" << std::endl;
149 std::cout << "Objective: f(x,y) = x + y (Minimization)" << std::endl;
150 std::cout << "Search Space: [-10, 10] per dimension" << std::endl;
151 std::cout << "Expected Result: -20.0 at [-10.0, -10.0]" << std::endl;
152 std::cout << "Running optimizer..." << std::endl;
153
154 opt::ObjectiveFunction plane_function = [](const opt::Coordinates& coords) {
155 opt::Real sum = 0.0;
156 for (auto val : coords) sum += val;
157 return sum;
158 };
159
160 ga.setBounds(lower, upper);
161 ga.setObjectiveFunction(plane_function);
162 ga.setMode(opt::OptimizationMode::MINIMIZE);
163
164 ga.setCallback([](const opt::Solution& current_best, size_t iteration) {
165 if (iteration % 20 == 0) {
166 std::cout << "[Boundary Test | Step " << std::setw(3) << iteration << "] "
167 << "Val: " << std::fixed << std::setprecision(4) << current_best.value << std::endl;
168 }
169 });
170
171 auto start = std::chrono::high_resolution_clock::now();
172 opt::Solution best_sol = ga.optimize();
173 auto end = std::chrono::high_resolution_clock::now();
174 std::chrono::duration<double, std::milli> duration = end - start;
175
176 std::cout << "Optimization Completed in " << duration.count() << " ms." << std::endl;
177 std::cout << "Best Value Found: " << std::fixed << std::setprecision(5) << best_sol.value << std::endl;
178 std::cout << "Best Position: [ ";
179 for (auto val : best_sol.params) {
180 std::cout << std::fixed << std::setprecision(5) << val << " ";
181 }
182 std::cout << "]" << std::endl;
183
184 if (std::abs(best_sol.value - (-20.0)) < 1e-3) {
185 std::cout << ">> SUCCESS: Boundary minimum found correctly!" << std::endl;
186 } else {
187 std::cout << ">> WARNING: Did not reach the exact boundary." << std::endl;
188 }
189}
190
191void runRastriginTest(opt::GA& /*ga*/, int dim) {
192 std::cout << "\n-------------------------------------------" << std::endl;
193 std::cout << "TEST 3: High-Dimensional Stress Test (Rastrigin Function)" << std::endl;
194 std::cout << "Dimension: " << dim << "D" << std::endl;
195 std::cout << "Search Space: [-5.12, 5.12] per dimension" << std::endl;
196 std::cout << "Goal: Find global minimum 0.0 (avoiding local traps)" << std::endl;
197
198 opt::ObjectiveFunction rastrigin_func = [dim](const opt::Coordinates& coords) {
199 double sum = 0.0;
200 double A = 10.0;
201 double pi = 3.14159265358979323846;
202 for (auto x : coords) sum += (x * x) - (A * std::cos(2.0 * pi * x));
203 return A * dim + sum;
204 };
205
206 opt::Coordinates lower(dim, -5.12);
207 opt::Coordinates upper(dim, 5.12);
208
209 // Hard configuration for GA (similar to PSO's hard config)
210 opt::GAConfig hard_config;
211 hard_config.population_size = 800; // More individuals = more exploration
212 hard_config.max_generations = 1200; // More time
213 hard_config.tournament_k = 4;
214 hard_config.crossover_rate = 0.90;
215 hard_config.mutation_rate = 0.15;
216 hard_config.mutation_sigma = 0.08;
217 hard_config.elitism_count = 3;
218
219 opt::GA local_ga(hard_config);
220 local_ga.setBounds(lower, upper);
221 local_ga.setObjectiveFunction(rastrigin_func);
222 local_ga.setMode(opt::OptimizationMode::MINIMIZE);
223
224 local_ga.setCallback([](const opt::Solution& sol, size_t i) {
225 if (i % 100 == 0) {
226 std::cout << "[Rastrigin " << i << "] Best: "
227 << std::scientific << std::setprecision(4) << sol.value
228 << std::defaultfloat << std::endl;
229 }
230 });
231
232 std::cout << "Running optimizer (this might take longer)..." << std::endl;
233
234 auto start = std::chrono::high_resolution_clock::now();
235 opt::Solution best_sol = local_ga.optimize();
236 auto end = std::chrono::high_resolution_clock::now();
237 std::chrono::duration<double, std::milli> duration = end - start;
238
239 std::cout << "Optimization Completed in " << duration.count() << " ms." << std::endl;
240 std::cout << "Best Value Found: " << std::fixed << std::setprecision(5) << best_sol.value << std::endl;
241
242 if (best_sol.value < 1e-2) {
243 std::cout << ">> SUCCESS: Global minimum found!" << std::endl;
244 } else if (best_sol.value < 5.0) {
245 std::cout << ">> ACCEPTABLE: Found a good local minimum, but not global." << std::endl;
246 } else {
247 std::cout << ">> FAIL: Stuck in a high local minimum." << std::endl;
248 }
249}
250
252 std::cout << "===========================================" << std::endl;
253 std::cout << " Visual GA Benchmark (2D Animation)" << std::endl;
254 std::cout << "===========================================" << std::endl;
255
256 opt::GAConfig config;
257 config.population_size = 80;
258 config.max_generations = 120; // frames
259 config.tournament_k = 3;
260 config.crossover_rate = 0.9;
261 config.mutation_rate = 0.10;
262 config.mutation_sigma = 0.05;
263 config.elitism_count = 2;
264
265 opt::GA ga(config);
266
267 auto rastrigin = [](const opt::Coordinates& x) {
268 double A = 10.0;
269 double sum = 0.0;
270 double pi = 3.14159265358979323846;
271 for (double val : x) sum += val*val - A*std::cos(2*pi*val);
272 return 2*A + sum;
273 };
274
275 ga.setObjectiveFunction(rastrigin);
276 ga.setBounds({-5.12, -5.12}, {5.12, 5.12});
277 ga.setMode(opt::OptimizationMode::MINIMIZE);
278
279 std::string baseName = "ga_vis";
280 std::string gridFile = "ga_grid.dat";
281
282 std::cout << "Generating background grid (heatmap)..." << std::endl;
283 mc::utils::saveFunctionGrid(gridFile, rastrigin, -5.12, 5.12, -5.12, 5.12, 100);
284
285 ga.setCallback([&](const opt::Solution&, size_t gen) {
286 savePopulationFrame2D(baseName, gen, ga.getPopulation());
287 std::cout << "Saved frame " << gen << "/" << config.max_generations << "\r" << std::flush;
288 });
289
290 std::cout << "Running optimization..." << std::endl;
291 ga.optimize();
292 std::cout << "\nOptimization finished." << std::endl;
293
294 std::cout << "Launching Gnuplot animation..." << std::endl;
295 // riuso lo script PSO: basta cambiare nome file gp e titolo
296 mc::utils::createPSOAnimationScript("run_ga.gp", gridFile, baseName, config.max_generations, "GA Rastrigin 2D");
297}
298
300 std::cout << "===========================================" << std::endl;
301 std::cout << " Visual GA Benchmark (3D Animation)" << std::endl;
302 std::cout << "===========================================" << std::endl;
303
304 opt::GAConfig config;
305 config.population_size = 120;
306 config.max_generations = 160;
307 config.tournament_k = 3;
308 config.crossover_rate = 0.9;
309 config.mutation_rate = 0.10;
310 config.mutation_sigma = 0.05;
311 config.elitism_count = 2;
312
313 opt::GA ga(config);
314
315 auto rastrigin3D = [](const opt::Coordinates& x) {
316 double sum = 0.0;
317 double A = 10.0;
318 double pi = 3.14159265358979323846;
319 for (double val : x) sum += val * val - A * std::cos(2 * pi * val);
320 return 3.0 * A + sum;
321 };
322
323 double min_b = -5.12;
324 double max_b = 5.12;
325
326 ga.setObjectiveFunction(rastrigin3D);
327 ga.setBounds({min_b, min_b, min_b}, {max_b, max_b, max_b});
328 ga.setMode(opt::OptimizationMode::MINIMIZE);
329
330 std::string baseName = "ga_vis_3d";
331 std::string slicesFile = "ga_slices_3d.dat";
332
333 std::cout << "Generating 3D function slices (walls)..." << std::endl;
334 mc::utils::saveFunctionSlices3D(slicesFile, rastrigin3D, min_b, max_b, 50);
335
336 ga.setCallback([&](const opt::Solution&, size_t gen) {
337 savePopulationFrame3D(baseName, gen, ga.getPopulation());
338 if (gen % 10 == 0) {
339 std::cout << "Generating Frame " << gen << "/" << config.max_generations << "\r" << std::flush;
340 }
341 });
342
343 std::cout << "Running 3D optimization..." << std::endl;
344 ga.optimize();
345 std::cout << "\nOptimization finished." << std::endl;
346
347 std::cout << "Launching Gnuplot 3D animation..." << std::endl;
348 mc::utils::createPSOAnimationScript3D("run_ga_3d.gp", slicesFile, baseName, config.max_generations,
349 "GA 3D Rastrigin", min_b, max_b);
350}
351
353 std::cout << "=========================================" << std::endl;
354 std::cout << " Genetic Algorithm (GA) Benchmark" << std::endl;
355 std::cout << "=========================================" << std::endl;
356
357 opt::GAConfig config;
358 config.population_size = 80;
359 config.max_generations = 200;
360 config.tournament_k = 3;
361 config.crossover_rate = 0.9;
362 config.mutation_rate = 0.10;
363 config.mutation_sigma = 0.05;
364 config.elitism_count = 2;
365
366 opt::GA ga(config);
367
368 opt::Coordinates lower_bounds = {-10.0, -10.0};
369 opt::Coordinates upper_bounds = { 10.0, 10.0};
370
371 try {
372 runSphereTest(ga, lower_bounds, upper_bounds);
373 runBoundaryTest(ga, lower_bounds, upper_bounds);
374 runRastriginTest(ga, 10);
375
376 // Visual specchio
379
380 } catch (const std::exception& e) {
381 std::cerr << "Optimization failed: " << e.what() << std::endl;
382 }
383}
Benchmarking framework for Monte Carlo integration algorithms.
Genetic Algorithm optimizer.
Definition GA.hpp:46
void setMode(OptimizationMode mode) override
Set optimization mode (minimize or maximize).
Definition GA.cpp:43
void setBounds(const Coordinates &lower, const Coordinates &upper) override
Set lower/upper bounds of the search hyper-rectangle.
Definition GA.cpp:34
Solution optimize() override
Run GA for max_generations.
Definition GA.cpp:211
void setCallback(StepCallback cb) override
Register a callback invoked after each generation.
Definition GA.cpp:30
void setObjectiveFunction(ObjectiveFunction func) override
Set the objective function to optimize.
Definition GA.cpp:25
const std::vector< Individual > & getPopulation() const
Access the current population.
Definition GA.hpp:101
void runBoundaryTest(opt::GA &ga, const opt::Coordinates &lower, const opt::Coordinates &upper)
void runOptimizationBenchmarksGA()
static std::string makeFrameName(const std::string &baseName, size_t iter)
Generate frame filename for GA population evolution.
void runVisualGA3DBenchmark()
void runVisualGABenchmark()
void runRastriginTest(opt::GA &, int dim)
void runSphereTest(opt::GA &ga, const opt::Coordinates &lower, const opt::Coordinates &upper)
static void savePopulationFrame3D(const std::string &baseName, size_t iter, const std::vector< opt::GA::Individual > &pop)
static void savePopulationFrame2D(const std::string &baseName, size_t iter, const std::vector< opt::GA::Individual > &pop)
Save population snapshot for 2D visualization.
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 saveFunctionSlices3D(const std::string &filename, const Func &func, double min, double max, int resolution=50)
Definition plotter.hpp:338
Configuration parameters for GA.
Definition GA.hpp:21
size_t elitism_count
Number of top individuals copied unchanged to next generation.
Definition GA.hpp:38
size_t tournament_k
Tournament size for selection (k >= 2).
Definition GA.hpp:28
Real mutation_rate
Per-gene mutation probability.
Definition GA.hpp:33
size_t population_size
Size of the population.
Definition GA.hpp:23
Real crossover_rate
Probability of performing crossover in reproduction.
Definition GA.hpp:31
Real mutation_sigma
Mutation magnitude (scaled by coordinate span).
Definition GA.hpp:35
size_t max_generations
Number of generations to evolve.
Definition GA.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