Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
drone_optimization.cpp
Go to the documentation of this file.
1
35#include <iostream>
36#include <vector>
37#include <cmath>
38#include <memory>
39#include <iomanip>
40#include <cstdint>
41#include <omp.h>
42#include <filesystem>
43#include <random>
44#include <limits>
45#include <functional>
46#include "../montecarlo/rng/rng_global.hpp"
47#include "../montecarlo/rng/rng_factory.hpp"
48
49#include "../montecarlo/geometry.hpp"
50#include "../montecarlo/integrators/MCintegrator.hpp"
51#include "../montecarlo/domains/hyperrectangle.hpp"
52#include "../montecarlo/domains/hypersphere.hpp"
53#include "../montecarlo/domains/hypercylinder.hpp"
54#include "../montecarlo/domains/polytope.hpp"
55#include "../montecarlo/optimizers/PSO.hpp"
56#include "../montecarlo/utils/plotter.hpp"
57#include <fstream>
58#include <stdexcept>
59
61constexpr size_t DIM = 3;
62
63// =================================================================================
64// HELPER FUNCTIONS FOR READING POLYTOPE DATA
65// =================================================================================
66template <int dim>
67std::vector<mc::geom::Point<dim>> read_points_from_file_drone(const std::string& filename)
68{
69 std::ifstream in(filename);
70 if (!in.is_open()) {
71 throw std::runtime_error("Cannot open file: " + filename);
72 }
73
74 std::size_t num_points = 0;
75 std::size_t file_dim = 0;
76
77 in >> num_points >> file_dim;
78 if (!in.good()) {
79 throw std::runtime_error("Error reading header from file: " + filename);
80 }
81
82 if (file_dim != static_cast<std::size_t>(dim)) {
83 throw std::runtime_error(
84 "Dimension mismatch: file has dim = " + std::to_string(file_dim) +
85 " but template expects dim = " + std::to_string(dim));
86 }
87
88 std::vector<mc::geom::Point<dim>> points;
89 points.reserve(num_points);
90
91 for (std::size_t i = 0; i < num_points; ++i) {
93 for (int k = 0; k < dim; ++k) {
94 if (!(in >> p[k])) {
95 throw std::runtime_error(
96 "Error reading coordinate " + std::to_string(k) +
97 " of point " + std::to_string(i) +
98 " from file: " + filename);
99 }
100 }
101 points.push_back(p);
102 }
103
104 return points;
105}
106
107template <int dim>
109 const std::string& filename,
110 std::vector<std::array<double, dim>>& normals,
111 std::vector<double>& offsets)
112{
113 std::ifstream in(filename);
114 if (!in.is_open()) {
115 throw std::runtime_error("Cannot open normals file: " + filename);
116 }
117
118 std::size_t file_dim = 0;
119 std::size_t num_facets = 0;
120
121 in >> file_dim >> num_facets;
122 if (!in.good()) {
123 throw std::runtime_error("Error reading header (dim, num_facets) from: " + filename);
124 }
125
126 if (file_dim != dim + 1) {
127 throw std::runtime_error(
128 "Dimension mismatch in normals file: file has dim = " +
129 std::to_string(file_dim) + " but template expects dim = " +
130 std::to_string(dim));
131 }
132
133 normals.clear();
134 offsets.clear();
135 normals.reserve(num_facets);
136 offsets.reserve(num_facets);
137
138 for (std::size_t f = 0; f < num_facets; ++f) {
139 std::array<double, dim> n{};
140 double d = 0.0;
141
142 for (std::size_t k = 0; k < dim; ++k) {
143 if (!(in >> n[k])) {
144 throw std::runtime_error(
145 "Error reading normal component " + std::to_string(k) +
146 " for facet " + std::to_string(f) +
147 " from: " + filename);
148 }
149 }
150
151 if (!(in >> d)) {
152 throw std::runtime_error(
153 "Error reading offset d for facet " + std::to_string(f) +
154 " from: " + filename);
155 }
156
157 double b = -d;
158 normals.push_back(n);
159 offsets.push_back(b);
160 }
161}
162
163// =================================================================================
164// 1. COMPLEX DOMAIN DEFINITION (Arm + Motor Housing)
165// =================================================================================
167public:
168 // Geometric components
169 std::unique_ptr<mc::domains::HyperRectangle<DIM>> arm;
170 std::unique_ptr<mc::domains::HyperCylinder<DIM>> motor_housing;
171 std::unique_ptr<mc::domains::PolyTope<DIM>> cabin; // Optional: PolyTope for cabin geometry
172
173 // Offsets relative to origin
174 std::array<double, DIM> motor_offset;
175 std::array<double, DIM> cabin_offset;
176
178 // 1. Arm: 10 units long, 2 units wide, 1 unit tall, centered at origin
179 std::array<double, DIM> arm_dims = {10.0, 2.0, 1.0};
180 arm = std::make_unique<mc::domains::HyperRectangle<DIM>>(arm_dims);
181
182 // 2. Motor housing: cylinder radius 1.5, height 1.2 (along Z-axis)
183 motor_housing = std::make_unique<mc::domains::HyperCylinder<DIM>>(1.5, 1.2);
184
185 // Position motor at arm tip (x = 5.0)
186 motor_offset = {5.0, 0.0, -0.6};
187
188 // 3. CABIN LOADING (PolyTope) - OPTIONAL
189 // Attempts to load cabin from files if available
190 try {
191 auto points = read_points_from_file_drone<DIM>("../drone_assets/cabin_points.txt");
192
193 std::vector<std::array<double, DIM>> normals;
194 std::vector<double> offsets;
195 read_normals_and_offsets_from_file_drone<DIM>("../drone_assets/cabin_hull.txt", normals, offsets);
196
197 cabin = std::make_unique<mc::domains::PolyTope<DIM>>(points, normals, offsets);
198 cabin_offset = {-2.0, 0.0, 0.5};
199 } catch (const std::exception&) {
200 cabin = nullptr;
201 cabin_offset = {0.0, 0.0, 0.0};
202 }
203 }
204
205 // Bounding box for Monte Carlo sampling (union of component bounds)
208 b[0] = {-6.0, 8.0}; // X
209 b[1] = {-3.0, 3.0}; // Y
210 b[2] = {-2.0, 2.0}; // Z
211 return b;
212 }
213
214 double getBoxVolume() const override {
215 return (14.0) * (6.0) * (4.0);
216 }
217
218 // Constructive Solid Geometry (CSG): Union of Arm ∪ Motor ∪ Cabin
219 bool isInside(const mc::geom::Point<DIM> &p) const override {
220 // Check arm
221 if (arm->isInside(p)) return true;
222
223 // Check motor (with offset)
224 mc::geom::Point<DIM> p_local;
225 for(size_t i=0; i<DIM; ++i) p_local[i] = p[i] - motor_offset[i];
226 if (motor_housing->isInside(p_local)) return true;
227
228 // Check cabin (PolyTope) if loaded
229 if (cabin) {
230 mc::geom::Point<DIM> p_cabin;
231 for(size_t i=0; i<DIM; ++i) p_cabin[i] = p[i] - cabin_offset[i];
232 if (cabin->isInside(p_cabin)) return true;
233 }
234
235 return false;
236 }
237
238 // Export domain geometry to file for visualization
239 // Export geometry with hole visualization
240 // Parameters: hole center (hx, hy, hz) and radius (hr)
241 void exportGeometry(const std::string& output_dir,
242 double hx, double hy, double hz, double hr) const {
243 // Create output directory if it doesn't exist
244 try {
245 std::filesystem::create_directories(output_dir);
246 } catch (const std::exception& e) {
247 std::cerr << "Could not create output directory: " << e.what() << std::endl;
248 return;
249 }
250
251 std::string filename = output_dir + "/drone_domain.txt";
252 std::ofstream out(filename);
253 if (!out.is_open()) {
254 std::cerr << "Could not open file for export: " << filename << std::endl;
255 return;
256 }
257
258 out << "# Drone arm domain geometry export (with optimized hole)\n";
259 out << "# Format: point_type x y z\n";
260 out << "# Hole: center=(" << hx << "," << hy << "," << hz << "), radius=" << hr << "\n";
261
262 // Lambda to check if a point is inside the hole
263 auto is_in_hole = [&](double x, double y, double z) {
264 double dist_sq = std::pow(x - hx, 2) + std::pow(y - hy, 2) + std::pow(z - hz, 2);
265 return dist_sq <= (hr * hr);
266 };
267
268 // Sample arm with higher resolution, excluding hole
269 out << "# Arm (sampled points, hole excluded)\n";
270 double step = 0.15; // Fine sampling for good visual quality
271
272 // Sample over arm bounding region
273 for (double x = -5.0; x <= 5.0; x += step) {
274 for (double y = -1.0; y <= 1.0; y += step) {
275 for (double z = -0.5; z <= 0.5; z += step) {
277 p[0] = x;
278 p[1] = y;
279 p[2] = z;
280
281 // Draw only if inside arm AND outside hole
282 if (arm->isInside(p) && !is_in_hole(x, y, z)) {
283 out << "arm " << x << " " << y << " " << z << "\n";
284 }
285 }
286 }
287 }
288
289 // Sample motor with higher resolution, excluding hole
290 out << "# Motor (sampled points, hole excluded)\n";
291 double step_motor = 0.15; // Fine sampling for cylindrical shape
292
293 // Sample over motor bounding region (centered around x=5, radius=1.5, height=1.2)
294 for (double x = 3.5; x <= 6.5; x += step_motor) {
295 for (double y = -1.5; y <= 1.5; y += step_motor) {
296 for (double z = -0.6; z <= 0.6; z += step_motor) {
298 p[0] = x;
299 p[1] = y;
300 p[2] = z;
301
302 // Transform to motor local coordinates
303 mc::geom::Point<DIM> p_motor;
304 for(size_t i=0; i<DIM; ++i) p_motor[i] = p[i] - motor_offset[i];
305
306 // Draw only if inside motor AND outside hole
307 if (motor_housing->isInside(p_motor) && !is_in_hole(x, y, z)) {
308 out << "motor " << x << " " << y << " " << z << "\n";
309 }
310 }
311 }
312 }
313
314 out.close();
315 std::cout << "Geometry (with hole) exported to: " << filename << std::endl;
316 }
317};
318
319// =================================================================================
320// 2. OBJECTIVE FUNCTION FOR OPTIMIZER
321// =================================================================================
322// Input: optimization variables [x, y, z, radius] of hole
323// Output: distance between calculated center of mass and target
324
325// Deterministic hash of parameters to stabilize Monte Carlo noise across identical queries
326uint32_t hash_params(const std::vector<double>& params) {
327 uint32_t seed = 0;
328 std::hash<double> hasher;
329 for (double p : params) {
330 seed ^= hasher(p) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
331 }
332 return seed;
333}
334
335double objective_function(const std::vector<double>& params) {
336 double hole_x = params[0];
337 double hole_y = params[1];
338 double hole_z = params[2];
339 double hole_r = params[3];
340
341 // Geometric guard: prevent optimizing a hole center outside the body
342 DroneArmDomain domain;
343 mc::geom::Point<DIM> hole_center;
344 hole_center[0] = hole_x;
345 hole_center[1] = hole_y;
346 hole_center[2] = hole_z;
347 if (!domain.isInside(hole_center)) {
348 return 1e6; // Soft penalty to push PSO back into the body
349 }
350
351 // Deterministic RNG: same params -> same seed -> stationary noise surface for PSO
352 uint32_t local_seed = hash_params(params) + mc::rng::get_global_seed();
353 std::mt19937 rng(local_seed);
354
355 auto bounds = domain.getBounds();
356 std::uniform_real_distribution<double> dist_x(bounds[0].first, bounds[0].second);
357 std::uniform_real_distribution<double> dist_y(bounds[1].first, bounds[1].second);
358 std::uniform_real_distribution<double> dist_z(bounds[2].first, bounds[2].second);
359
360 int n_samples = 20000; // Fast during PSO; high-precision check is later
361 double hole_r_sq = hole_r * hole_r;
362
363 double sum_mass = 0.0;
364 double sum_mx = 0.0;
365 double sum_my = 0.0;
366 double sum_mz = 0.0;
367
368 // Single Monte Carlo pass: inside domain and outside hole -> accumulate mass and moments
369 for (int i = 0; i < n_samples; ++i) {
371 p[0] = dist_x(rng);
372 p[1] = dist_y(rng);
373 p[2] = dist_z(rng);
374
375 if (domain.isInside(p)) {
376 double dist_sq = std::pow(p[0] - hole_x, 2) +
377 std::pow(p[1] - hole_y, 2) +
378 std::pow(p[2] - hole_z, 2);
379 if (dist_sq > hole_r_sq) {
380 sum_mass += 1.0;
381 sum_mx += p[0];
382 sum_my += p[1];
383 sum_mz += p[2];
384 }
385 }
386 }
387
388 if (sum_mass <= 1e-9) return 1e6; // Penalty if we remove all material
389
390 double cm_x = sum_mx / sum_mass;
391 double cm_y = sum_my / sum_mass;
392 double cm_z = sum_mz / sum_mass;
393
394 double error = std::sqrt(std::pow(cm_x - 1.0, 2) +
395 std::pow(cm_y - 0.0, 2) +
396 std::pow(cm_z - 0.0, 2));
397
398 return error;
399}
400
401// =================================================================================
402// 3. MAIN
403// =================================================================================
404int main(int argc, char* argv[]) {
405 // Parse seed from command line if provided
406 int num_threads = omp_get_max_threads(); // Default: max available threads
407 std::uint32_t seed = 12345; // Default seed
408
409 if (argc > 1) {
410 std::string seed_arg = argv[1];
411 // Check if user wants to keep default seed (using "-" as placeholder)
412 if (seed_arg == "-") {
413 std::cout << "Using default seed: " << seed << std::endl;
414 } else {
415 try {
416 seed = static_cast<std::uint32_t>(std::stoul(seed_arg));
417 std::cout << "Using custom seed: " << seed << std::endl;
418 } catch (const std::exception&) {
419 std::cerr << "Invalid seed argument. Using default seed: " << seed << std::endl;
420 }
421 }
422 } else {
423 std::cout << "Using default seed: " << seed << std::endl;
424 }
425
426 // Set the global seed for all library components
428
429 // Second argument: number of threads (0 = sequential, positive = number of threads)
430 if (argc > 2) {
431 try {
432 int requested_threads = std::stoi(argv[2]);
433 if (requested_threads == 0) {
434 num_threads = 1;
435 omp_set_num_threads(1);
436 std::cout << "Running SEQUENTIAL (1 thread)" << std::endl;
437 } else if (requested_threads > 0) {
438 num_threads = requested_threads;
439 omp_set_num_threads(requested_threads);
440 std::cout << "Running with " << num_threads << " threads" << std::endl;
441 }
442 } catch (const std::exception&) {
443 std::cout << "Invalid thread argument. Using max available: " << num_threads << std::endl;
444 }
445 } else {
446 std::cout << "Using max available threads: " << num_threads << std::endl;
447 std::cout << "(Usage: ./drone_optimization [seed|-] [num_threads])" << std::endl;
448 std::cout << " <num_threads>: 0=sequential, N>0=N threads" << std::endl;
449 }
450 std::cout << std::endl;
451
452 std::cout << "--- Drone Arm Center of Mass Optimization ---" << std::endl;
453 std::cout << "Optimizing hole position and size to shift CM to (1.0, 0, 0)..." << std::endl;
454 std::cout << std::endl;
455
456 // =================================================================================
457 // PRE-COMPUTE BASELINE: Full drone mass/moments (no hole) using high-precision MC
458 // =================================================================================
459 std::cout << "--- Pre-computing Static Drone Properties (Baseline) ---" << std::endl;
460
461 DroneArmDomain static_domain;
462 mc::integrators::MontecarloIntegrator<DIM> static_integrator(static_domain);
463
464 // High sample count for accurate baseline (reused later for ground truth)
465 int n_baseline = 1000000;
466
467 // Density functions for full body (no hole consideration)
468 auto f_mass = [](const mc::geom::Point<DIM>& p) { return 1.0; };
469 auto f_mx = [](const mc::geom::Point<DIM>& p) { return p[0]; };
470 auto f_my = [](const mc::geom::Point<DIM>& p) { return p[1]; };
471 auto f_mz = [](const mc::geom::Point<DIM>& p) { return p[2]; };
472
473 double base_mass = static_integrator.OLDintegrate(f_mass, n_baseline);
474 double base_mx = static_integrator.OLDintegrate(f_mx, n_baseline);
475 double base_my = static_integrator.OLDintegrate(f_my, n_baseline);
476 double base_mz = static_integrator.OLDintegrate(f_mz, n_baseline);
477
478 std::cout << std::fixed << std::setprecision(8);
479 std::cout << "Baseline Mass (Full Body): " << base_mass << std::endl;
480 std::cout << "Baseline CM: (" << base_mx/base_mass << ", "
481 << base_my/base_mass << ", "
482 << base_mz/base_mass << ")" << std::endl;
483 std::cout << std::endl;
484
485 // PSO configuration
487 config.population_size = 30;
488 config.max_iterations = 50;
489 config.cognitive_coeff = 1.5;
490 config.social_coeff = 1.5;
491 config.inertia_weight = 0.7;
492
493 mc::optim::PSO pso(config);
494
495 // Set objective function
497
498 // Define search bounds for hole [x, y, z, r]
499 std::vector<double> lower_bounds = {-4.0, -1.0, -0.5, 0.1};
500 std::vector<double> upper_bounds = { 4.0, 1.0, 0.5, 0.9};
501
502 pso.setBounds(lower_bounds, upper_bounds);
504
505 // Progress callback
506 pso.setCallback([](const mc::optim::Solution& best, int iter) {
507 if (iter % 10 == 0) {
508 std::cout << "Iter " << iter << " | Error: " << best.value << " | ";
509 std::cout << "Hole: x=" << best.params[0] << " r=" << best.params[3] << std::endl;
510 }
511 });
512
513 // Run optimization
514 mc::optim::Solution best_sol = pso.optimize();
515
516 std::cout << "\n--- Optimization Complete ---" << std::endl;
517 std::cout << "Final Error: " << best_sol.value << std::endl;
518 std::cout << "Optimal Hole Configuration:" << std::endl;
519 std::cout << " Position: (" << best_sol.params[0] << ", "
520 << best_sol.params[1] << ", "
521 << best_sol.params[2] << ")" << std::endl;
522 std::cout << " Radius: " << best_sol.params[3] << std::endl;
523
524 // Retrieve optimal parameters
525 double best_x = best_sol.params[0];
526 double best_y = best_sol.params[1];
527 double best_z = best_sol.params[2];
528 double best_r = best_sol.params[3];
529
530 // =================================================================================
531 // HIGH-PRECISION VERIFICATION (single-pass, correlated mass/moment sampling)
532 // =================================================================================
533 std::cout << "\n--- HIGH-PRECISION VERIFICATION ---" << std::endl;
534
535 DroneArmDomain domain_verify;
536
537 auto bounds = domain_verify.getBounds();
538 int n_verify = 1000000;
539 double best_r_sq = best_r * best_r;
540
541 long double total_hits = 0.0;
542 long double total_mx = 0.0;
543 long double total_my = 0.0;
544 long double total_mz = 0.0;
545
546 #pragma omp parallel reduction(+:total_hits, total_mx, total_my, total_mz)
547 {
548 int tid = omp_get_thread_num();
549 // Deterministic verification RNG per thread; stable across runs
550 auto local_rng = mc::rng::make_thread_engine(9999);
551
552 #pragma omp for
553 for (int i = 0; i < n_verify; ++i) {
555 double rx = std::generate_canonical<double, 32>(local_rng);
556 double ry = std::generate_canonical<double, 32>(local_rng);
557 double rz = std::generate_canonical<double, 32>(local_rng);
558 p[0] = bounds[0].first + rx * (bounds[0].second - bounds[0].first);
559 p[1] = bounds[1].first + ry * (bounds[1].second - bounds[1].first);
560 p[2] = bounds[2].first + rz * (bounds[2].second - bounds[2].first);
561
562 if (domain_verify.isInside(p)) {
563 double dist_sq = std::pow(p[0] - best_x, 2) +
564 std::pow(p[1] - best_y, 2) +
565 std::pow(p[2] - best_z, 2);
566 if (dist_sq > best_r_sq) {
567 total_hits += 1.0;
568 total_mx += p[0];
569 total_my += p[1];
570 total_mz += p[2];
571 }
572 }
573 }
574 }
575
576 if (total_hits <= 0.0) {
577 std::cout << "No mass remains after hole removal; verification aborting." << std::endl;
578 return 0;
579 }
580
581 double box_vol = domain_verify.getBoxVolume();
582 double mass_high_prec = (static_cast<double>(total_hits) / n_verify) * box_vol;
583
584 double cm_x_final = static_cast<double>(total_mx / total_hits);
585 double cm_y_final = static_cast<double>(total_my / total_hits);
586 double cm_z_final = static_cast<double>(total_mz / total_hits);
587
588 const mc::geom::Point<DIM> CM_target_verify = [](){ mc::geom::Point<DIM> p; p[0] = 1.0; p[1] = 0.0; p[2] = 0.0; return p; }();
589 double error_final = std::sqrt(std::pow(cm_x_final - CM_target_verify[0], 2) +
590 std::pow(cm_y_final - CM_target_verify[1], 2) +
591 std::pow(cm_z_final - CM_target_verify[2], 2));
592
593 std::cout << std::fixed << std::setprecision(8);
594 std::cout << "\nResults with " << n_verify << " samples (high-precision verification):" << std::endl;
595 std::cout << "Total mass estimated: " << mass_high_prec << std::endl;
596 std::cout << "Current center of mass: (" << cm_x_final << ", "
597 << cm_y_final << ", "
598 << cm_z_final << ")" << std::endl;
599 std::cout << "Target center of mass: (" << CM_target_verify[0] << ", "
600 << CM_target_verify[1] << ", "
601 << CM_target_verify[2] << ")" << std::endl;
602 std::cout << "Residual error (verified):" << error_final << std::endl;
603 std::cout << "\nComparison:" << std::endl;
604 std::cout << " Optimization error (few samples): " << best_sol.value << std::endl;
605 std::cout << " Verification error (many samples): " << error_final << std::endl;
606
607 // =================================================================================
608 // HYBRID GROUND TRUTH: Baseline Monte Carlo - Analytic Hole Subtraction
609 // =================================================================================
610 std::cout << "\n--- HYBRID VERIFICATION (Baseline MC + Analytic Hole) ---" << std::endl;
611
612 // Exact spherical hole volume and moments (analytic)
613 const double pi = std::acos(-1.0);
614 double vol_hole = (4.0 / 3.0) * pi * std::pow(best_r, 3);
615 double mom_x_hole = vol_hole * best_x;
616 double mom_y_hole = vol_hole * best_y;
617 double mom_z_hole = vol_hole * best_z;
618
619 // Subtract hole from baseline (measured earlier with high precision)
620 double final_mass_exact = base_mass - vol_hole;
621 double final_mx_exact = base_mx - mom_x_hole;
622 double final_my_exact = base_my - mom_y_hole;
623 double final_mz_exact = base_mz - mom_z_hole;
624
625 if (final_mass_exact <= 0.0) {
626 std::cout << "Invalid: hole removes all mass (should not happen with valid PSO solution)" << std::endl;
627 } else {
628 double true_cm_x = final_mx_exact / final_mass_exact;
629 double true_cm_y = final_my_exact / final_mass_exact;
630 double true_cm_z = final_mz_exact / final_mass_exact;
631
632 double exact_error = std::sqrt(std::pow(true_cm_x - 1.0, 2) +
633 std::pow(true_cm_y - 0.0, 2) +
634 std::pow(true_cm_z - 0.0, 2));
635
636 std::cout << "Hybrid Mass (Baseline - Hole): " << final_mass_exact << std::endl;
637 std::cout << "Hybrid CM (Ground Truth): (" << true_cm_x << ", "
638 << true_cm_y << ", "
639 << true_cm_z << ")" << std::endl;
640 std::cout << "Hybrid Error from Target: " << exact_error << std::endl;
641
642 double method_diff = std::abs(error_final - exact_error);
643 std::cout << "Delta (MC Verify vs Hybrid): " << method_diff << std::endl;
644
645 if (method_diff < 0.05) {
646 std::cout << "\n✓ SUCCESS: Monte Carlo converges to physical ground truth." << std::endl;
647 } else {
648 std::cout << "\n⚠ WARNING: Residual discrepancy detected (consider increasing n_baseline or n_verify)." << std::endl;
649 }
650 }
651
652 if (error_final < 0.01) {
653 std::cout << "\n✓ ROBUST solution: error remains small even with many samples!" << std::endl;
654 } else if (error_final < 0.1) {
655 std::cout << "\n◎ MODERATE solution: could improve with more PSO iterations." << std::endl;
656 } else {
657 std::cout << "\n✗ UNSTABLE solution: consider increasing PSO population/iterations." << std::endl;
658 }
659
660 // Export domain geometry for visualization (with optimized hole)
661 std::cout << "\nExporting domain geometry to drone_frames/" << std::endl;
662 domain_verify.exportGeometry("./drone_frames", best_x, best_y, best_z, best_r);
663
664 // Generate gnuplot visualization script
665 mc::utils::createDroneVisualizationScript("visualize_drone.gp", "drone_frames/drone_domain.txt");
666
667 return 0;
668}
std::array< double, DIM > cabin_offset
void exportGeometry(const std::string &output_dir, double hx, double hy, double hz, double hr) const
std::unique_ptr< mc::domains::PolyTope< DIM > > cabin
double getBoxVolume() const override
Compute the volume of the bounding box.
bool isInside(const mc::geom::Point< DIM > &p) const override
std::unique_ptr< mc::domains::HyperRectangle< DIM > > arm
std::unique_ptr< mc::domains::HyperCylinder< DIM > > motor_housing
mc::geom::Bounds< DIM > getBounds() const override
Get the axis-aligned bounding box of the domain.
std::array< double, DIM > motor_offset
Abstract base class for N-dimensional integration domains.
N-dimensional axis-aligned bounding box.
Definition geometry.hpp:70
N-dimensional point representation.
Definition geometry.hpp:32
Uniform-sampling Monte Carlo integrator for N-dimensional domains.
double OLDintegrate(const std::function< double(const mc::geom::Point< dim > &)> &f, int n_samples)
Legacy integration routine (deprecated).
Particle Swarm Optimization algorithm.
Definition PSO.hpp:40
Solution optimize() override
Execute the optimization loop for max_iterations.
Definition PSO.cpp:167
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
void read_normals_and_offsets_from_file_drone(const std::string &filename, std::vector< std::array< double, dim > > &normals, std::vector< double > &offsets)
uint32_t hash_params(const std::vector< double > &params)
double objective_function(const std::vector< double > &params)
constexpr size_t DIM
Dimensionality of the problem space (3D geometry)
std::vector< mc::geom::Point< dim > > read_points_from_file_drone(const std::string &filename)
constexpr int dim
Default dimensionality for integration.
Definition main.cpp:36
std::mt19937 make_thread_engine(std::uint64_t stream_id)
Create a deterministic RNG engine for the current thread (OpenMP if available)
bool set_global_seed(std::uint32_t s)
Set the global seed used by all library RNG components.
std::uint32_t get_global_seed()
Get the current global seed.
void createDroneVisualizationScript(const std::string &scriptName, const std::string &geometryFile, const std::string &title="Drone Arm Domain Geometry")
Definition plotter.hpp:476
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