2 * @file gaussianProposal.tpp
3 * @brief GaussianProposal template implementation.
4 * @details Implements multivariate Gaussian sampling with PDF evaluation.
5 * Samples are drawn from the full Gaussian (no domain truncation).
8 * - sample(rng) draws from the full Gaussian in R^dim (NO rejection).
9 * - pdf(x) returns the corresponding full Gaussian density (NO domain indicator).
11 * Domain constraints (if any) must be handled by the estimator via:
12 * if(domain.isInside(p)) { ... }
14 * This guarantees sample() and pdf() are always coherent with the Proposal interface.
17// gaussianProposal.tpp
18#ifndef MONTECARLO_1_GAUSSIAN_PROPOSAL_TPP
19#define MONTECARLO_1_GAUSSIAN_PROPOSAL_TPP
21#include <limits> // std::numeric_limits
22#include <utility> // std::move
29 * @brief Initialize normalization constant and precision from mean and standard deviation.
30 * @tparam dim Dimensionality parameter.
32 * @details Precomputes:
33 * - inv_sig2[i] = 1 / (σᵢ²) for each dimension
34 * - log_norm_const = log(det(Σ)^(-1/2) / (2π)^(n/2))
35 * = -n/2 * log(2π) + ∑ᵢ log(1/σᵢ)
37 * This avoids recomputation during PDF evaluations.
39 * @throws std::invalid_argument If dimensions mismatch or σᵢ ≤ 0.
42void GaussianProposal<dim>::init_from_mu_sig_()
44 if (mu.size() != dim || sig.size() != dim)
45 throw std::invalid_argument("GaussianProposal: mean and sigma must have size = dim.");
49 double sum_log_inv_sigma = 0.0;
50 for (size_t i = 0; i < dim; ++i) {
51 if (!(sig[i] > 0.0) || !std::isfinite(sig[i]))
52 throw std::invalid_argument("GaussianProposal: sigma must be finite and > 0 for every dimension.");
54 inv_sig2[i] = 1.0 / (sig[i] * sig[i]);
56 sum_log_inv_sigma += std::log(1.0 / sig[i]);
59 const double pi = std::numbers::pi;
60 const double log_2pi = std::log(2.0 * pi);
61 log_norm_const = -0.5 * static_cast<double>(dim) * log_2pi + sum_log_inv_sigma;
65 * @brief Construct a multivariate Gaussian proposal distribution.
66 * @tparam dim Dimensionality parameter.
67 * @param d Integration domain (stored for consistency, may be used later).
68 * @param mean Vector of means (μ) for each dimension. Size must equal dim.
69 * @param sigma Vector of standard deviations (σ) for each dimension. Size must equal dim.
70 * All σᵢ must be > 0 and finite.
72 * @throws std::invalid_argument If vector sizes don't match dim or σᵢ is invalid.
74 * @details Initializes a diagonal Gaussian with independent components:
75 * q(x) = ∏ᵢ N(xᵢ; μᵢ, σᵢ²)
78GaussianProposal<dim>::GaussianProposal(const mc::domains::IntegrationDomain<dim>& d,
79 const std::vector<double>& mean,
80 const std::vector<double>& sigma)
81 : domain(d), mu(mean), sig(sigma)
87 * @brief Draw a random sample from the multivariate Gaussian.
88 * @tparam dim Dimensionality parameter.
89 * @param rng Mersenne Twister random generator.
90 * @return Point sampled from N(μ, Σ) where Σ is diagonal with σᵢ² on diagonal.
92 * @details Generates independent normal samples for each dimension:
93 * xᵢ ~ N(μᵢ, σᵢ²). Note: samples are drawn from the FULL Gaussian,
94 * not truncated to any domain. Domain handling is the caller's responsibility.
97mc::geom::Point<dim> GaussianProposal<dim>::sample(std::mt19937& rng) const
99 mc::geom::Point<dim> x;
100 for (size_t i = 0; i < dim; ++i){
101 std::normal_distribution<double> d(mu[i], sig[i]);
108 * @brief Evaluate the Gaussian probability density function at a point.
109 * @tparam dim Dimensionality parameter.
110 * @param x Query point.
111 * @return q(x) = exp(log_norm_const - 0.5 * ∑ᵢ ((xᵢ - μᵢ)² / σᵢ²))
113 * @details Uses precomputed normalization constant and inverse variances
114 * for efficiency. Returns 0.0 for extreme tails where log is non-finite.
116 * Time complexity: O(dim).
119double GaussianProposal<dim>::pdf(const mc::geom::Point<dim>& x) const
121 // Full Gaussian density on R^dim (no domain indicator).
123 for (size_t i = 0; i < dim; ++i) {
124 const double diff = x[i] - mu[i];
125 quad += diff * diff * inv_sig2[i];
128 // exp(log_norm_const - 0.5 * quad)
129 const double logp = log_norm_const - 0.5 * quad;
131 // Optional safety: avoid returning NaN for extreme tails
132 if (!std::isfinite(logp))
135 return std::exp(logp);
138} // namespace proposals
141#endif // MONTECARLO_1_GAUSSIAN_PROPOSAL_TP