Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
integration_benchmarks.cpp
Go to the documentation of this file.
1
27//
28// Integration benchmarks for Monte Carlo methods
29//
30
31#include "apps/benchmarks.hpp"
35#include <cmath>
36#include <cstdint>
37
38// --- Helper for formatted console output ---
39
56template <size_t dim, typename Func>
57void executeBenchmark(const std::string& title,
58 const std::string& filename,
60 const mc::domains::IntegrationDomain<dim>& domain, // Needed for plotting
61 Func&& f,
62 bool useGnuplot,
63 const std::string& rawDataFile,
64 const std::string& functionExpr) // Function string for display/save
65{
66 // --- Helper for console output ---
67 auto printLine = [&](std::size_t n,
68 const std::string& label,
69 double result,
70 long time_ms)
71 {
72 std::cout << std::setw(8) << n << " | "
73 << std::setw(30) << label << " | "
74 << std::setw(20) << std::fixed << std::setprecision(6) << result << " | "
75 << std::setw(5) << time_ms << " ms\n";
76 };
77
78 std::cout << "------------------------------------------------" << std::endl;
79 std::cout << title << ":" << std::endl;
80 std::cout << "Integrating Function: " << functionExpr << std::endl << std::endl;
81
82 std::vector<results> testResults;
83
86 std::vector<double> init_mean(dim, 0.0);
87 std::vector<double> init_sigma(dim, 2.5);
88 auto bounds = domain.getBounds();
89 for (size_t i = 0; i < dim; ++i) {
90 init_mean[i] = 0.5 * (bounds[i].first + bounds[i].second);
91 init_sigma[i] = (bounds[i].second - bounds[i].first) / 3.0; // oppure /2.0
92 }
93 mc::proposals::GaussianProposal<dim> gprop(domain, init_mean, init_sigma);
94 mc::proposals::MixtureProposal<dim> mix({&uprop, &gprop}, {0.5, 0.5});
95
96 // Header table for console output
97 std::cout << std::string(107, '-') << '\n';
98 std::cout << std::setw(8) << "Samples" << " | "
99 << std::setw(30) << "Method" << " | "
100 << std::setw(20) << "Result" << " | "
101 << std::setw(6) << "Time" << '\n';
102 std::cout << std::string(107, '-') << '\n';
103
104 for (size_t n_i : n_samples_vector) {
105 // 1. Normal MC (Data points are written to rawDataFile by the integrator)
106 auto startTimer1 = std::chrono::high_resolution_clock::now();
107 double result1 = integrator.OLDintegrate(f, n_i);
108 auto endTimer1 = std::chrono::high_resolution_clock::now();
109 auto duration1 = std::chrono::duration_cast<std::chrono::milliseconds>(endTimer1 - startTimer1);
110
111 // 2. Uniform IS (Data points are written to rawDataFile by the integrator)
112 auto startTimer2 = std::chrono::high_resolution_clock::now();
113 double result2 = isIntegrator.integrate(f, n_i, uprop, mc::rng::get_global_seed());
114 auto endTimer2 = std::chrono::high_resolution_clock::now();
115 auto duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(endTimer2 - startTimer2);
116
117 // 3. Gaussian IS (Data points are written to rawDataFile by the integrator)
118 auto startTimer3 = std::chrono::high_resolution_clock::now();
119 double result3 = isIntegrator.integrate(f, n_i, gprop, mc::rng::get_global_seed());
120 auto endTimer3 = std::chrono::high_resolution_clock::now();
121 auto duration3 = std::chrono::duration_cast<std::chrono::milliseconds>(endTimer3 - startTimer3);
122
123 // 4. Mixture IS (Data points are written to rawDataFile by the integrator)
124 auto startTimer4 = std::chrono::high_resolution_clock::now();
125 double result4 = isIntegrator.integrate(f, n_i, mix, mc::rng::get_global_seed());
126 auto endTimer4 = std::chrono::high_resolution_clock::now();
127 auto duration4 = std::chrono::duration_cast<std::chrono::milliseconds>(endTimer4 - startTimer4);
128
129 // Store results
130 results newLine;
131 newLine.n_samples = n_i;
132 newLine.integration_result = std::to_string(result1);
133 newLine.duration = std::to_string(duration1.count());
134 testResults.push_back(newLine);
135
136 // Console Output
137 printLine(n_i, "Normal Sampling", result1, duration1.count());
138 printLine(n_i, "Uniform Importance Sampling", result2, duration2.count());
139 printLine(n_i, "Gaussian Importance Sampling", result3, duration3.count());
140 printLine(n_i, "Mixture Importance Sampling", result4, duration4.count());
141 std::cout << std::string(107, '-') << '\n';
142
143 // 2. Plotting (Inside the loop to show progress for each sample size)
144 if (useGnuplot) {
145 // Plot Domain Geometry (Inside/Outside points)
146 mc::utils::createGnuplotScript(rawDataFile, domain, n_i);
147
148 // Plot Function Value (f(x))
149 mc::utils::createFunctionGnuplotScript(rawDataFile, domain, f, n_i);
150 }
151 }
152
153 std::cout << "\nSaved txt file: " << filename << "\n";
154 saveResults(filename, testResults, functionExpr);
155}
156
157// --- Domain-Specific Benchmark Wrappers ---
158
159template <typename Func>
160void runCircleBenchmark(Func f, const std::string& modeLabel, bool useGnuplot, const std::string& funcStr) {
161 mc::domains::Hypersphere<2> circle(5.0);
163 std::string title = "2D Circle Integration (Radius 5) [" + modeLabel + "]";
164 std::string filename = "resultsCircle_" + modeLabel + ".txt";
165 std::string dataFile = "hsphere_samples.dat"; // File name must match what Integrator writes
166
167 executeBenchmark(title, filename, integrator, circle, f, useGnuplot, dataFile, funcStr);
168}
169
170template <typename Func>
171void runSphereBenchmark(Func f, const std::string& modeLabel, bool useGnuplot, const std::string& funcStr) {
172 double radius = 10.0;
173 mc::domains::Hypersphere<4> sphere(radius);
175 std::string title = "4D Hypersphere Integration [" + modeLabel + "]";
176 std::string filename = "resultsSphere4D_" + modeLabel + ".txt";
177 std::string dataFile = "hsphere_samples.dat";
178
179 executeBenchmark(title, filename, integrator, sphere, f, useGnuplot, dataFile, funcStr);
180}
181
182template <typename Func>
183void runRectBenchmark(Func f, const std::string& modeLabel, bool useGnuplot, const std::string& funcStr) {
184 std::array<double, 4> sides = {10.0, 5.0, 10.0, 5.0};
185 mc::domains::HyperRectangle<4> rectangle(sides);
186 mc::integrators::MontecarloIntegrator<4> integrator(rectangle);
187 std::string title = "4D HyperRectangle Integration [" + modeLabel + "]";
188 std::string filename = "resultsRectangle4D_" + modeLabel + ".txt";
189 std::string dataFile = "hrectangle_samples.dat";
190
191 executeBenchmark(title, filename, integrator, rectangle, f, useGnuplot, dataFile, funcStr);
192}
193
194template <typename Func>
195void runCylinderBenchmark(Func f, const std::string& modeLabel, bool useGnuplot, const std::string& funcStr) {
196 double radius = 5.0;
197 double height = 10.0;
198 mc::domains::HyperCylinder<4> cylinder(radius, height);
199 mc::integrators::MontecarloIntegrator<4> integrator(cylinder);
200 std::string title = "4D HyperCylinder Integration [" + modeLabel + "]";
201 std::string filename = "resultsCylinder4D_" + modeLabel + ".txt";
202 std::string dataFile = "cylinder_samples.dat";
203
204 executeBenchmark(title, filename, integrator, cylinder, f, useGnuplot, dataFile, funcStr);
205}
206
207
208// --- Specific Implementations (Hardcoded vs Parser) ---
209
210// 1. HARDCODED (C++ Lambda)
211// Manually defining the function string description for the output file/console.
212
213void circleIntegration(bool useGnuplot) {
214 auto f = [](const mc::geom::Point<2> &x) { return x[0] * x[0] - x[1] * x[1]; };
215 std::string funcStr = "x[0]^2 - x[1]^2";
216 runCircleBenchmark(f, "Hardcoded", useGnuplot, funcStr);
217}
218
219void sphereIntegration(bool useGnuplot) {
220 auto f = [](const mc::geom::Point<4> &x) { return x[0]*x[0] + x[1]*x[1] + x[2]*x[2] + x[3]*x[3]; };
221 std::string funcStr = "x[0]^2 + x[1]^2 + x[2]^2 + x[3]^2";
222 runSphereBenchmark(f, "Hardcoded", useGnuplot, funcStr);
223}
224
225void rectangularIntegration(bool useGnuplot) {
226 auto f = [](const mc::geom::Point<4> &x) { return x[0]*x[0] + x[1]*x[1] + x[2]*x[2] + x[3]*x[3]; };
227 std::string funcStr = "x[0]^2 + x[1]^2 + x[2]^2 + x[3]^2";
228 runRectBenchmark(f, "Hardcoded", useGnuplot, funcStr);
229}
230
231void cylinderIntegration(bool useGnuplot) {
232 auto f = [](const mc::geom::Point<4> &x) { return x[0]*x[0] + x[1]*x[1] + x[2]*x[2] + x[3]*x[3]; };
233 std::string funcStr = "x[0]^2 + x[1]^2 + x[2]^2 + x[3]^2";
234 runCylinderBenchmark(f, "Hardcoded", useGnuplot, funcStr);
235}
236
237// 2. PARSER (muParserX)
238// Using the 'expr' string directly read from file.
239
240// 2. PARSER (muParserX) - THREAD SAFE WRAPPER
241
242void circleIntegrationParser(const std::string& expr, bool useGnuplot) {
244 Parser base(expr);
245
246 auto f = [base](const mc::geom::Point<2>& x) -> double {
247 thread_local Parser p = base; // una copia per thread
248 return p(x);
249 };
250
251 runCircleBenchmark(f, "Parser", useGnuplot, expr);
252}
253
254void sphereIntegrationParser(const std::string& expr, bool useGnuplot) {
256 Parser base(expr);
257
258 auto f = [base](const mc::geom::Point<4>& x) -> double {
259 thread_local Parser p = base;
260 return p(x);
261 };
262
263 runSphereBenchmark(f, "Parser", useGnuplot, expr);
264}
265
266void rectangularIntegrationParser(const std::string& expr, bool useGnuplot) {
268 Parser base(expr);
269
270 auto f = [base](const mc::geom::Point<4>& x) -> double {
271 thread_local Parser p = base;
272 return p(x);
273 };
274
275 runRectBenchmark(f, "Parser", useGnuplot, expr);
276}
277
278void cylinderIntegrationParser(const std::string& expr, bool useGnuplot) {
280 Parser base(expr);
281
282 auto f = [base](const mc::geom::Point<4>& x) -> double {
283 thread_local Parser p = base;
284 return p(x);
285 };
286
287 runCylinderBenchmark(f, "Parser", useGnuplot, expr);
288}
289
290// --- Main Entry Points ---
291
292void runBenchmarks(bool useGnuplot) {
293 n_threads = std::thread::hardware_concurrency();
294 if (n_threads == 0) n_threads = 16;
295
296 circleIntegration(useGnuplot);
297 sphereIntegration(useGnuplot);
298 rectangularIntegration(useGnuplot);
299 cylinderIntegration(useGnuplot);
300}
301
302#include <cmath>
303#include <iostream>
304#include <functional>
305#include <limits>
306#include <random>
307#include <string>
308
310 n_threads = std::thread::hardware_concurrency();
311 if (n_threads == 0) n_threads = 16;
312
313 constexpr double R = 10.0;
315
316 std::function<double(const mc::geom::Point<2>&)> indicator =
317 [&domain](const mc::geom::Point<2>& x) -> double {
318 return domain.isInside(x) ? 1.0 : 0.0;
319 };
320
321 const double deviation = 0.15;
322 const std::size_t burn_in = 20'000;
323 const std::size_t thinning = 10;
324 const std::size_t n_samples = 1'000'000;
325 const std::size_t n_samples_volume = 200'000;
326
328
331 mc::proposals::UniformProposal<2> dummy_proposal(domain);
332
333 mhintegrator.setConfig(
334 burn_in,
335 thinning,
336 n_samples_volume,
337 deviation,
338 indicator,
339 x0
340 );
341
342 auto run_case = [&](const std::string& name,
343 const std::function<double(const mc::geom::Point<2>&)>& f,
344 double exact_or_nan)
345 {
346 const unsigned int seed = mc::rng::get_global_seed();
347
348 const double mh_est = mhintegrator.integrate(
349 f, static_cast<int>(n_samples), dummy_proposal, seed);
350
351 const double mc_est = integrator.OLDintegrate(f, n_samples);
352
353 auto print_line = [&](const std::string& method, double est) {
354 std::cout << " " << method << ": " << est;
355 if (!std::isnan(exact_or_nan)) {
356 const double abs_err = std::abs(est - exact_or_nan);
357 const double rel_err = abs_err / (std::abs(exact_or_nan) + 1e-30);
358 std::cout << " | abs_err=" << abs_err << " | rel_err=" << rel_err;
359 }
360 std::cout << "\n";
361 };
362
363 std::cout << "\n===========================================\n";
364 std::cout << "Case: " << name << "\n";
365 if (!std::isnan(exact_or_nan))
366 std::cout << "Exact: " << exact_or_nan << "\n";
367 else
368 std::cout << "Exact: (no closed form used here)\n";
369
370 print_line("MH", mh_est);
371 print_line("MC", mc_est);
372 };
373
374 std::cout << "Running Benchmarks (MC vs MH) on disk R=" << R << "\n";
375 std::cout << "Config: n_samples=" << n_samples
376 << " burn_in=" << burn_in
377 << " thinning=" << thinning
378 << " deviation=" << deviation
379 << " n_samples_volume=" << n_samples_volume
380 << "\n";
381
382 // --------------------------
383 // A) Sanity check: r^2
384 // f(x)=x^2+y^2, exact = 5000*pi for R=10
385 // --------------------------
386 {
387 auto fA = [](const mc::geom::Point<2>& x) -> double {
388 return x[0]*x[0] + x[1]*x[1];
389 };
390 const double exactA = 0.5 * M_PI * std::pow(R, 4); // π R^4 / 2
391 run_case("A) f=r^2 (sanity check)", fA, exactA);
392 }
393
394 // --------------------------
395 // B) Volume test: f(x)=1
396 // exact = area = pi*R^2
397 // --------------------------
398 {
399 auto fB = [](const mc::geom::Point<2>&) -> double { return 1.0; };
400 const double exactB = M_PI * R * R;
401 run_case("B) f=1 (volume)", fB, exactB);
402 }
403
404 // --------------------------
405 // C) Centered Gaussian: exp(-alpha r^2)
406 // exact = (pi/alpha)*(1 - exp(-alpha R^2))
407 // --------------------------
408 {
409 const double alpha = 1.0;
410 auto fC = [alpha](const mc::geom::Point<2>& x) -> double {
411 const double r2 = x[0]*x[0] + x[1]*x[1];
412 return std::exp(-alpha * r2);
413 };
414 const double exactC = (M_PI/alpha) * (1.0 - std::exp(-alpha * R * R));
415 run_case("C) f=exp(-alpha r^2), alpha=1", fC, exactC);
416 }
417
418 // --------------------------
419 // D) Thin ring: exp(-beta (r-r0)^2)
420 // (closed form on finite disk is messy -> skip exact)
421 // --------------------------
422 {
423 const double r0 = 8.0;
424 const double beta = 50.0;
425 auto fD = [r0,beta](const mc::geom::Point<2>& x) -> double {
426 const double r = std::sqrt(x[0]*x[0] + x[1]*x[1]);
427 const double d = r - r0;
428 return std::exp(-beta * d * d);
429 };
430 run_case("D) f=exp(-beta (r-r0)^2) (thin ring)", fD,
431 std::numeric_limits<double>::quiet_NaN());
432 }
433
434 // --------------------------
435 // E) Boundary-stressing function: 1/sqrt((R-r)+eps)
436 // (integrable; exact exists but not worth here -> skip exact)
437 // --------------------------
438 {
439 const double eps = 1e-3;
440 auto fE = [eps](const mc::geom::Point<2>& x) -> double {
441 const double r = std::sqrt(x[0]*x[0] + x[1]*x[1]);
442 return 1.0 / std::sqrt((R - r) + eps);
443 };
444 run_case("E) f=1/sqrt((R-r)+eps) (boundary layer)", fE,
445 std::numeric_limits<double>::quiet_NaN());
446 }
447}
448
449void runBenchmarks(const std::string& expression, bool useGnuplot) {
450 n_threads = std::thread::hardware_concurrency();
451 if (n_threads == 0) n_threads = 16;
452
453 circleIntegrationParser(expression, useGnuplot);
454 sphereIntegrationParser(expression, useGnuplot);
455 rectangularIntegrationParser(expression, useGnuplot);
456 cylinderIntegrationParser(expression, useGnuplot);
457}
Importance sampling Monte Carlo integration engine.
void saveResults(const std::string &filename, const std::vector< results > &results, const std::string &function_expr)
Export benchmark results to CSV file for analysis.
const std::vector< size_t > n_samples_vector
Global sample count vector used across all benchmarks for convergence testing.
unsigned int n_threads
Global OpenMP thread count (set at runtime)
Benchmarking framework for Monte Carlo integration algorithms.
void cylinderIntegration()
3D cylindrical integration: hypercylinder base with height
void sphereIntegration()
3D spherical integration: f(x,y,z) = x²+y²+z² over unit ball
void rectangularIntegration()
2D rectangular integration over axis-aligned box
void circleIntegration()
2D circular integration: f(x,y) = x²+y² over unit disk
N-dimensional hypercylinder.
Axis-aligned hyperrectangular domain.
N-dimensional ball (solid sphere).
bool isInside(const mc::geom::Point< dim > &point) const override
Test if a point is inside the hypersphere.
Abstract base class for N-dimensional integration domains.
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
Importance sampling Monte Carlo integrator.
double integrate(const std::function< double(const mc::geom::Point< dim > &)> &f, int n_samples, const mc::proposals::Proposal< dim > &proposal, std::uint32_t seed) override
Compute the integral using importance sampling.
Metropolis-Hastings MCMC integrator for complex domains.
void setConfig(std::size_t burn_in_, std::size_t thinning_, std::size_t n_samples_volume_, double deviation_, Func p_, Point x0_)
Configure the MCMC sampler parameters.
double integrate(const Func &f, int n_samples, const mc::proposals::Proposal< dim > &proposal, std::uint32_t seed) override
Compute the integral using MH sampling combined with volume estimation.
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).
Uniform distribution over a domain.
void cylinderIntegrationParser(const std::string &expr, bool useGnuplot)
void runBenchmarks(bool useGnuplot)
Run integration benchmarks with hardcoded integrands.
void runRectBenchmark(Func f, const std::string &modeLabel, bool useGnuplot, const std::string &funcStr)
void runSphereBenchmark(Func f, const std::string &modeLabel, bool useGnuplot, const std::string &funcStr)
void rectangularIntegrationParser(const std::string &expr, bool useGnuplot)
void executeBenchmark(const std::string &title, const std::string &filename, mc::integrators::MontecarloIntegrator< dim > &integrator, const mc::domains::IntegrationDomain< dim > &domain, Func &&f, bool useGnuplot, const std::string &rawDataFile, const std::string &functionExpr)
Generic execution loop for integration benchmarks.
void runCylinderBenchmark(Func f, const std::string &modeLabel, bool useGnuplot, const std::string &funcStr)
void runBenchmarksMH()
Run Metropolis-Hastings MCMC integration benchmarks.
void sphereIntegrationParser(const std::string &expr, bool useGnuplot)
void runCircleBenchmark(Func f, const std::string &modeLabel, bool useGnuplot, const std::string &funcStr)
void circleIntegrationParser(const std::string &expr, bool useGnuplot)
constexpr int dim
Default dimensionality for integration.
Definition main.cpp:36
std::uint32_t get_global_seed()
Get the current global seed.
void createFunctionGnuplotScript(const std::string &tempRawDataFile, const mc::domains::IntegrationDomain< dim > &domain, const Func &func, size_t currentSamples)
Definition plotter.hpp:134
void createGnuplotScript(const std::string &tempRawDataFile, const mc::domains::IntegrationDomain< dim > &domain, size_t currentSamples)
Definition plotter.hpp:39
Stores benchmark result for a single sample count.
size_t n_samples
std::string integration_result
std::string duration