Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
plotter.hpp
Go to the documentation of this file.
1//
2// Created by domenico on 11/27/25.
3//
4
5#ifndef MONTECARLO_1_PLOTTER_HPP
6#define MONTECARLO_1_PLOTTER_HPP
7
8#include <fstream>
9#include <iostream>
10#include <string>
11#include <sstream>
12#include <vector>
13#include <cstdlib> // std::system
14#include <filesystem>
15#include "../domains/integration_domain.hpp"
16
17namespace mc::utils {
18
22inline void closeGnuplotWindows() {
23 std::system("pkill -f gnuplot > /dev/null 2>&1");
24}
25
26inline std::string formatTitle(std::string name) {
27 std::string target = "_samples";
28 size_t pos = name.find(target);
29 if (pos != std::string::npos) name.erase(pos, target.length());
30 for (auto &c : name) if (c == '_') c = ' ';
31 return name;
32}
33
38template <size_t dim>
39inline void createGnuplotScript(const std::string& tempRawDataFile,
41 size_t currentSamples) {
42
43 if (dim > 3) return; // Cannot plot 4D+ geometry easily
44
45 // Prepare unique filenames
46 std::string baseName = tempRawDataFile;
47 size_t lastDot = baseName.find_last_of(".");
48 if (lastDot != std::string::npos) baseName = baseName.substr(0, lastDot);
49 size_t lastSlash = baseName.find_last_of("/\\");
50 if (lastSlash != std::string::npos) baseName = baseName.substr(lastSlash + 1);
51
52 std::string uniqueID = baseName + "_geom_" + std::to_string(currentSamples);
53 std::string uniqueDataFile = uniqueID + ".dat";
54 std::string scriptName = "plot_" + uniqueID + ".gp";
55
56 // Read temp raw data and write unique file with Inside/Outside status
57 std::ifstream inFile(tempRawDataFile);
58 if (!inFile.is_open()) return;
59
60 std::vector<mc::geom::Point<dim>> points;
61 std::string line;
62 while (std::getline(inFile, line)) {
63 if (line.empty()) continue;
64 std::stringstream ss(line);
66 for (size_t i = 0; i < dim; ++i) ss >> p[i];
67 points.push_back(p);
68 }
69 inFile.close();
70
71 std::ofstream outFile(uniqueDataFile);
72 for (const auto& p : points) {
73 for (size_t i = 0; i < dim; ++i) outFile << p[i] << " ";
74 bool isInside = domain.isInside(p);
75 outFile << (isInside ? 1 : 0) << "\n";
76 }
77 outFile.close();
78
79 // Create Gnuplot script
80 std::ofstream gp(scriptName);
81 if (!gp.is_open()) return;
82
83 gp << "set grid\n";
84 gp << "set title 'Domain Geometry (" << dim << "D) - N=" << currentSamples << "'\n";
85 gp << "set key outside\n";
86
87 // Set Axis Ranges
88 auto bounds = domain.getBounds();
89 double margin = 0.1;
90 auto setRange = [&](int axisIdx, std::string axisName) {
91 if (axisIdx < dim) {
92 double min = bounds[axisIdx].first;
93 double max = bounds[axisIdx].second;
94 double span = max - min;
95 gp << "set " << axisName << "range [" << (min - span * margin) << ":" << (max + span * margin) << "]\n";
96 }
97 };
98 setRange(0, "x");
99 if (dim >= 2) setRange(1, "y");
100 if (dim >= 3) setRange(2, "z");
101
102 int statusCol = dim + 1; // The added status column index (1-based)
103
104 if (dim == 1) {
105 gp << "set xlabel 'X'\n unset ytics\n set yrange [-1:1]\n";
106 gp << "plot '" << uniqueDataFile << "' u 1:($" << statusCol << "==0?0:1/0) w p pt 7 ps 0.4 lc rgb 'blue' t 'Out', \\\n";
107 gp << " '" << uniqueDataFile << "' u 1:($" << statusCol << "==1?0:1/0) w p pt 7 ps 0.4 lc rgb 'red' t 'In'\n";
108 }
109 else if (dim == 2) {
110 gp << "set size square\n set xlabel 'X'\n set ylabel 'Y'\n";
111 gp << "plot '" << uniqueDataFile << "' u 1:($" << statusCol << "==0?$2:1/0) w p pt 7 ps 0.4 lc rgb 'blue' t 'Out', \\\n";
112 gp << " '" << uniqueDataFile << "' u 1:($" << statusCol << "==1?$2:1/0) w p pt 7 ps 0.4 lc rgb 'red' t 'In'\n";
113 }
114 else if (dim == 3) {
115 gp << "set view equal xyz\n set xlabel 'X'\n set ylabel 'Y'\n set zlabel 'Z'\n set view 60, 30\n";
116 gp << "splot '" << uniqueDataFile << "' u 1:2:($" << statusCol << "==0?$3:1/0) w p pt 7 ps 0.4 lc rgb 'blue' t 'Out', \\\n";
117 gp << " '" << uniqueDataFile << "' u 1:2:($" << statusCol << "==1?$3:1/0) w p pt 7 ps 0.4 lc rgb 'red' t 'In'\n";
118 }
119
120 gp << "pause mouse close\n"; // Keep window open
121 gp.close();
122
123 // Execute Gnuplot in background
124 std::string command = "gnuplot " + scriptName + " > /dev/null 2>&1 &";
125 std::system(command.c_str());
126}
127
133template <size_t dim, typename Func>
134inline void createFunctionGnuplotScript(const std::string& tempRawDataFile,
136 const Func& func,
137 size_t currentSamples) {
138 if (dim > 3) return;
139
140 std::string baseName = tempRawDataFile;
141 size_t lastDot = baseName.find_last_of(".");
142 if (lastDot != std::string::npos) baseName = baseName.substr(0, lastDot);
143 size_t lastSlash = baseName.find_last_of("/\\");
144 if (lastSlash != std::string::npos) baseName = baseName.substr(lastSlash + 1);
145
146 // Unique filename for function values
147 std::string uniqueID = baseName + "_func_" + std::to_string(currentSamples);
148 std::string uniqueDataFile = uniqueID + ".dat";
149 std::string scriptName = "plot_" + uniqueID + ".gp";
150
151 // Read raw data, evaluate f(x) ONLY for points inside the domain, and write to file
152 std::ifstream inFile(tempRawDataFile);
153 if (!inFile.is_open()) return;
154
155 std::ofstream outFile(uniqueDataFile);
156
157 std::string line;
158 while (std::getline(inFile, line)) {
159 if (line.empty()) continue;
160 std::stringstream ss(line);
162 for (size_t i = 0; i < dim; ++i) ss >> p[i];
163
164 if (domain.isInside(p)) {
165 double val = func(p);
166 for(size_t i=0; i<dim; ++i) outFile << p[i] << " ";
167 outFile << val << "\n";
168 }
169 }
170 inFile.close();
171 outFile.close();
172
173 // Generate Gnuplot script
174 std::ofstream gp(scriptName);
175 if (!gp.is_open()) return;
176
177 gp << "set grid\n";
178 gp << "set title 'Function Value (" << dim << "D) - N=" << currentSamples << "'\n";
179
180 // Configure Color Palette logic
181 if (dim == 3) {
182 // Only 3D Domain (4D total visualization) uses heatmap
183 gp << "set palette rgbformulae 33,13,10\n";
184 gp << "set colorbox\n";
185 } else {
186 // 1D and 2D Domains use solid green
187 gp << "unset colorbox\n";
188 }
189
190 int valCol = dim + 1; // Column containing function value
191
192 if (dim == 1) {
193 gp << "set xlabel 'X'\n set ylabel 'f(X)'\n";
194 // 1D Domain -> 2D Plot (x, f(x)) -> Solid Green
195 gp << "plot '" << uniqueDataFile << "' u 1:" << valCol << " w p pt 7 ps 0.4 lc rgb 'green' t 'f(x)'\n";
196 }
197 else if (dim == 2) {
198 gp << "set size square\n set view 60, 30\n";
199 gp << "set xlabel 'X'\n set ylabel 'Y'\n set zlabel 'f(X,Y)'\n";
200 // 2D Domain -> 3D Plot (x, y, z=f(x,y)) -> Solid Green
201 gp << "splot '" << uniqueDataFile << "' u 1:2:" << valCol << " w p pt 7 ps 0.4 lc rgb 'green' t 'f(x,y)'\n";
202 }
203 else if (dim == 3) {
204 gp << "set view equal xyz\n set view 60, 30\n";
205 gp << "set xlabel 'X'\n set ylabel 'Y'\n set zlabel 'Z'\n";
206 // 3D Domain -> 4D Visualization (x, y, z, color=f(x,y,z)) -> Palette
207 gp << "splot '" << uniqueDataFile << "' u 1:2:3:" << valCol << " w p pt 7 ps 0.4 lc palette t 'f(x,y,z)'\n";
208 }
209
210 gp << "pause mouse close\n";
211 gp.close();
212
213 std::string command = "gnuplot " + scriptName + " > /dev/null 2>&1 &";
214 std::system(command.c_str());
215}
216
222template <typename Func>
223inline void saveFunctionGrid(const std::string& filename, const Func& func,
224 double x_min, double x_max, double y_min, double y_max,
225 int resolution = 100) {
226 // Extract directory from filename
227 std::string dir = "./pso_frames";
228 if (filename.find("ga_") != std::string::npos) {
229 dir = "./ga_frames";
230 }
231
232 // Create directory if it doesn't exist
233 try {
234 std::filesystem::create_directories(dir);
235 } catch (const std::exception&) {}
236
237 std::string fullpath = dir + "/" + filename;
238 std::ofstream out(fullpath);
239 if (!out.is_open()) return;
240
241 double dx = (x_max - x_min) / resolution;
242 double dy = (y_max - y_min) / resolution;
243
244 for (int i = 0; i <= resolution; ++i) {
245 double x = x_min + i * dx;
246 for (int j = 0; j <= resolution; ++j) {
247 double y = y_min + j * dy;
248 std::vector<double> p = {x, y};
249 double val = func(p);
250 // Write format: X Y Value
251 out << x << " " << y << " " << val << "\n";
252 }
253 out << "\n"; // Blank line required for Gnuplot pm3d mode
254 }
255 out.close();
256}
257
262template <typename ParticleT>
263inline void saveSwarmFrame(const std::string& basename, size_t iteration, const std::vector<ParticleT>& swarm) {
264 // Determine directory based on basename
265 std::string dir = "./pso_frames";
266 if (basename.find("ga_") != std::string::npos) {
267 dir = "./ga_frames";
268 }
269
270 // Create directory if it doesn't exist
271 try {
272 std::filesystem::create_directories(dir);
273 } catch (const std::exception&) {}
274
275 std::string filename = dir + "/" + basename + "_iter_" + std::to_string(iteration) + ".dat";
276 std::ofstream out(filename);
277 if (!out.is_open()) return;
278
279 for (const auto& p : swarm) {
280 if (p.position.size() >= 3) {
281 // Write X Y Z
282 out << p.position[0] << " " << p.position[1] << " " << p.position[2] << "\n";
283 } else if (p.position.size() == 2) {
284 // Fallback for 2D (Z=0)
285 out << p.position[0] << " " << p.position[1] << " 0.0\n";
286 }
287 }
288 out.close();
289}
290
295inline void createPSOAnimationScript(const std::string& scriptName,
296 const std::string& gridFile,
297 const std::string& swarmBasename,
298 size_t max_iter,
299 const std::string& title) {
300 // Determine directory based on swarmBasename
301 std::string dir = "./pso_frames";
302 if (swarmBasename.find("ga_") != std::string::npos) {
303 dir = "./ga_frames";
304 }
305
306 std::ofstream gp(scriptName);
307 if (!gp.is_open()) return;
308
309 gp << "set title '" << title << "'\n";
310 gp << "set view map\n"; // Top-down view (Heatmap)
311 gp << "set dgrid3d\n"; // Enable 3D grid data processing
312 gp << "set pm3d interpolate 0,0\n"; // Smooth color interpolation
313 gp << "set palette rgbformulae 33,13,10\n"; // Rainbow palette
314 gp << "unset key\n";
315 gp << "set size square\n";
316
317 // Gnuplot Loop for Animation
318 gp << "do for [i=0:" << (max_iter-1) << "] {\n";
319 gp << " set title sprintf('" << title << " - Iter: %d', i)\n";
320 gp << " plot '" << dir << "/" << gridFile << "' with image, \\\n";
321 gp << " sprintf('" << dir << "/" << swarmBasename << "_iter_%d.dat', i) u 1:2 with points pt 7 ps 1.5 lc rgb 'white'\n";
322 gp << " pause 0.1\n"; // Delay between frames (0.1 seconds)
323 gp << "}\n";
324 gp << "pause mouse close\n"; // Keep window open until clicked
325 gp.close();
326
327 // Execute Gnuplot in background
328 std::string command = "gnuplot " + scriptName + " > /dev/null 2>&1 &";
329 std::system(command.c_str());
330}
331
337template <typename Func>
338inline void saveFunctionSlices3D(const std::string& filename, const Func& func,
339 double min, double max, int resolution = 50) {
340 // Extract directory from filename
341 std::string dir = "./pso_frames";
342 if (filename.find("ga_") != std::string::npos) {
343 dir = "./ga_frames";
344 }
345
346 // Create directory if it doesn't exist
347 try {
348 std::filesystem::create_directories(dir);
349 } catch (const std::exception&) {}
350
351 std::string fullpath = dir + "/" + filename;
352 std::ofstream out(fullpath);
353 if (!out.is_open()) return;
354
355 double step = (max - min) / resolution;
356
357 // 1. XY Plane (at Z = min) - "Floor"
358 for (int i = 0; i <= resolution; ++i) {
359 double x = min + i * step;
360 for (int j = 0; j <= resolution; ++j) {
361 double y = min + j * step;
362 double z = min; // Bottom
363 double val = func({x, y, z});
364 out << x << " " << y << " " << z << " " << val << "\n";
365 }
366 out << "\n";
367 }
368 out << "\n\n";
369
370 // 2. XZ Plane (at Y = min) - "Back Right Wall" (FIXED: was max)
371 // Changing to Y=min moves the wall to the back, opening the view.
372 for (int i = 0; i <= resolution; ++i) {
373 double x = min + i * step;
374 for (int k = 0; k <= resolution; ++k) {
375 double z = min + k * step;
376 double y = min; // Fixed at back (min)
377 double val = func({x, y, z});
378 out << x << " " << y << " " << z << " " << val << "\n";
379 }
380 out << "\n";
381 }
382 out << "\n\n";
383
384 // 3. YZ Plane (at X = min) - "Back Left Wall"
385 for (int j = 0; j <= resolution; ++j) {
386 double y = min + j * step;
387 for (int k = 0; k <= resolution; ++k) {
388 double z = min + k * step;
389 double x = min; // Fixed at left (min)
390 double val = func({x, y, z});
391 out << x << " " << y << " " << z << " " << val << "\n";
392 }
393 out << "\n";
394 }
395 out.close();
396}
397
403inline void createPSOAnimationScript3D(const std::string& scriptName,
404 const std::string& slicesFile,
405 const std::string& swarmBasename,
406 size_t max_iter,
407 const std::string& title,
408 double min_bound, double max_bound) {
409 // Determine subdirectory based on filename
410 std::string dir = "pso_frames";
411 if (swarmBasename.find("ga_") != std::string::npos) {
412 dir = "ga_frames";
413 }
414
415 // Ensure subdirectory exists
416 try {
417 std::filesystem::create_directories(dir);
418 } catch (const std::exception& e) {
419 std::cerr << "Error creating directory " << dir << ": " << e.what() << std::endl;
420 }
421
422 // Prepend directory to file paths
423 std::string fullSlicesFile = dir + "/" + slicesFile;
424 std::string fullSwarmBasename = dir + "/" + swarmBasename;
425
426 std::ofstream gp(scriptName);
427 if (!gp.is_open()) return;
428
429 gp << "set title '" << title << "'\n";
430
431 // View from the "open" side of the cube towards the back walls
432 gp << "set view 60, 120\n";
433
434 gp << "set grid\n";
435 gp << "set xtics\n";
436 gp << "set ytics\n";
437 gp << "set ztics\n";
438
439 // Use 'explicit' to prevent pm3d from interfering with points
440 gp << "set pm3d explicit\n";
441 gp << "set palette rgbformulae 33,13,10\n";
442 gp << "unset colorbox\n";
443
444 // Fixed range
445 gp << "set xrange [" << min_bound << ":" << max_bound << "]\n";
446 gp << "set yrange [" << min_bound << ":" << max_bound << "]\n";
447 gp << "set zrange [" << min_bound << ":" << max_bound << "]\n";
448
449 // Animation Loop
450 gp << "do for [i=0:" << (max_iter-1) << "] {\n";
451 gp << " set title sprintf('" << title << " - Iter: %d', i)\n";
452
453 // Draw slices first (background), then particles (foreground)
454 gp << " splot '" << fullSlicesFile << "' u 1:2:3:4 with pm3d nocontour title '', \\\n";
455 gp << " sprintf('" << fullSwarmBasename << "_iter_%d.dat', i) u 1:2:3 with points pt 7 ps 1.5 lc rgb 'white' title 'Particles'\n";
456
457 gp << " pause 0.1\n";
458 gp << "}\n";
459 gp << "pause mouse close\n";
460 gp.close();
461
462 // Execute visibly
463 std::cout << "Executing Gnuplot 3D script: " << scriptName << std::endl;
464 std::string command = "gnuplot -p " + scriptName;
465 int ret = std::system(command.c_str());
466 if (ret != 0) {
467 std::cerr << "GNUPLOT ERROR (Code " << ret << "): Ensure gnuplot is installed." << std::endl;
468 }
469}
470
476inline void createDroneVisualizationScript(const std::string& scriptName,
477 const std::string& geometryFile,
478 const std::string& title = "Drone Arm Domain Geometry") {
479 std::ofstream gp(scriptName);
480 if (!gp.is_open()) {
481 std::cerr << "Could not create gnuplot script: " << scriptName << std::endl;
482 return;
483 }
484
485 gp << "#!/usr/bin/gnuplot\n";
486 gp << "# Visualization script for drone arm domain geometry\n";
487 gp << "# Auto-generated by drone_optimization\n\n";
488
489 gp << "set title '" << title << "'\n";
490 gp << "set xlabel 'X'\n";
491 gp << "set ylabel 'Y'\n";
492 gp << "set zlabel 'Z'\n";
493
494 gp << "# Equal aspect ratio to prevent distortion\n";
495 gp << "set view equal xyz\n";
496 gp << "set view 60, 30\n";
497 gp << "set grid\n\n";
498
499 gp << "# Data file location\n";
500 gp << "datafile = '" << geometryFile << "'\n\n";
501
502 gp << "# Plot arm (blue) and motor (red) with smaller points for volume effect\n";
503 // Use stringcolumn(1) since column 1 contains strings (arm/motor)
504 gp << "splot datafile u (stringcolumn(1) eq \"arm\" ? $2 : 1/0):3:4 with points pt 7 ps 0.5 lc rgb '#3366cc' title 'Arm', \\\n";
505 gp << " datafile u (stringcolumn(1) eq \"motor\" ? $2 : 1/0):3:4 with points pt 7 ps 0.5 lc rgb '#dc3912' title 'Motor'\n\n";
506
507 gp << "pause mouse close\n";
508 gp.close();
509
510 std::cout << "Gnuplot visualization script created: " << scriptName << std::endl;
511 std::cout << "To visualize: gnuplot -persist " << scriptName << std::endl;
512}
513
514} // namespace mc::utils
515
516#endif //MONTECARLO_1_PLOTTER_HPP
Abstract base class for N-dimensional integration domains.
virtual bool isInside(const mc::geom::Point< dim > &point) const =0
Test whether a point lies inside the domain.
virtual mc::geom::Bounds< dim > getBounds() const =0
Get the axis-aligned bounding box of the domain.
N-dimensional point representation.
Definition geometry.hpp:32
constexpr int dim
Default dimensionality for integration.
Definition main.cpp:36
void createFunctionGnuplotScript(const std::string &tempRawDataFile, const mc::domains::IntegrationDomain< dim > &domain, const Func &func, size_t currentSamples)
Definition plotter.hpp:134
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 createDroneVisualizationScript(const std::string &scriptName, const std::string &geometryFile, const std::string &title="Drone Arm Domain Geometry")
Definition plotter.hpp:476
void saveSwarmFrame(const std::string &basename, size_t iteration, const std::vector< ParticleT > &swarm)
Definition plotter.hpp:263
void createGnuplotScript(const std::string &tempRawDataFile, const mc::domains::IntegrationDomain< dim > &domain, size_t currentSamples)
Definition plotter.hpp:39
void saveFunctionSlices3D(const std::string &filename, const Func &func, double min, double max, int resolution=50)
Definition plotter.hpp:338
std::string formatTitle(std::string name)
Definition plotter.hpp:26
void closeGnuplotWindows()
Utility to close all currently open Gnuplot windows.
Definition plotter.hpp:22