Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
ISMeanEstimator.tpp
Go to the documentation of this file.
1/**
2 * @file ISMeanEstimator.tpp
3 * @brief ISMeanEstimator template implementation.
4 * @details Implements importance sampling mean estimation with parallel sampling,
5 * proposal weighting, and standard error computation.
6 */
7
8#ifndef MONTECARLO_DGL_ISMEANESTIMATOR_TPP
9#define MONTECARLO_DGL_ISMEANESTIMATOR_TPP
10
11#include <algorithm>
12#include <cmath>
13#include <optional>
14#include <stdexcept>
15
16#include <omp.h>
17
18#include "montecarlo/rng/rng_factory.hpp"
19
20namespace mc::estimators {
21
22/**
23 * @brief Estimates the mean of a function over a domain using importance sampling.
24 * @tparam dim The dimensionality of the integration domain.
25 *
26 * @param domain The integration domain constraint.
27 * @param seed Random seed for reproducibility.
28 * @param n_samples Number of samples to generate.
29 * @param proposal The proposal distribution for importance sampling.
30 * @param f The function to estimate the mean of.
31 *
32 * @return ImportanceEstimate<dim> containing:
33 * - n_samples: Total samples generated
34 * - n_inside: Samples that fell within the domain
35 * - mean: Estimated mean value (sum of weighted samples / n_samples)
36 * - stderr: Standard error of the estimate
37 *
38 * @throws std::invalid_argument If n_samples is zero.
39 *
40 * @details
41 * The estimator uses importance sampling with a proposal distribution q(x).
42 * Each sample p from the proposal is weighted by f(p)/q(p). The algorithm:
43 * 1. Samples from the proposal distribution
44 * 2. Rejects samples outside the domain
45 * 3. Computes weighted contribution f(p)/q(p)
46 * 4. Accumulates first and second moments
47 * 5. Computes sample variance for standard error
48 *
49 * The computation is parallelized across available threads with each thread
50 * maintaining independent accumulators to avoid synchronization overhead.
51 *
52 * @note Uses OpenMP for parallel sampling.
53 */
54template <std::size_t dim>
55ImportanceEstimate<dim> ISMeanEstimator<dim>::estimate(const mc::domains::IntegrationDomain<dim>& domain,
56 std::uint32_t seed,
57 std::size_t n_samples,
58 const mc::proposals::Proposal<dim>& proposal,
59 const std::function<double(const mc::geom::Point<dim>&)>& f) const
60{
61 if (n_samples == 0) throw std::invalid_argument("n_samples must be > 0");
62
63 const int T = omp_get_max_threads();
64 const std::size_t base = n_samples / static_cast<std::size_t>(T);
65 const std::size_t rem = n_samples % static_cast<std::size_t>(T);
66
67 double sum = 0.0;
68 double sum2 = 0.0;
69 std::size_t inside_total = 0;
70
71#pragma omp parallel for reduction(+:sum,sum2,inside_total)
72 for (int tid = 0; tid < T; ++tid) {
73 const std::size_t n_local = base + (static_cast<std::size_t>(tid) < rem ? 1u : 0u);
74 auto rng = mc::rng::make_engine_with_seed(std::optional<std::uint32_t>{seed}, static_cast<std::uint64_t>(tid));
75
76 for (std::size_t i = 0; i < n_local; ++i) {
77 mc::geom::Point<dim> p = proposal.sample(rng);
78 if (!domain.isInside(p)) continue;
79
80 const double q = proposal.pdf(p);
81 if (q <= 0.0) continue;
82
83 const double term = f(p) / q;
84 sum += term;
85 sum2 += term * term;
86 inside_total += 1;
87 }
88 }
89
90 ImportanceEstimate<dim> out;
91 out.n_samples = n_samples;
92 out.n_inside = inside_total;
93 out.mean = sum / static_cast<double>(n_samples);
94
95 const double e2 = sum2 / static_cast<double>(n_samples);
96 const double var = std::max(0.0, e2 - out.mean * out.mean);
97 out.stderr = std::sqrt(var / static_cast<double>(n_samples));
98
99 return out;
100}
101
102} // namespace mc::estimators
103
104#endif // MONTECARLO_DGL_ISMEANESTIMATOR_TPP