Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
mixtureProposal.tpp
Go to the documentation of this file.
1/**
2 * @file mixtureProposal.tpp
3 * @brief MixtureProposal template implementation.
4 * @details Implements mixture sampling and PDF evaluation.
5 * Stores non-owning pointers to proposal components.
6 */
7
8// mixtureProposal.tpp
9#ifndef MONTECARLO_1_MIXTURE_PROPOSAL_TPP
10#define MONTECARLO_1_MIXTURE_PROPOSAL_TPP
11
12#include <numeric> // std::accumulate
13#include <cmath> // std::isfinite
14
15namespace mc {
16namespace proposals {
17
18/**
19 * @brief Validate input components and weights.
20 * @tparam dim Dimensionality parameter.
21 * @param components Vector of proposal pointers (must be non-empty, all non-null).
22 * @param weights Vector of component weights (must match size, all ≥ 0, sum > 0).
23 *
24 * @throws std::invalid_argument If validation fails.
25 */
26template <size_t dim>
27void MixtureProposal<dim>::validateInputs(const std::vector<const Proposal<dim>*>& components,
28 const std::vector<double>& weights)
29{
30 if (components.empty()) {
31 throw std::invalid_argument("MixtureProposal: components must be non-empty.");
32 }
33 if (components.size() != weights.size()) {
34 throw std::invalid_argument("MixtureProposal: components and weights must have the same size.");
35 }
36
37 for (std::size_t i = 0; i < components.size(); ++i) {
38 if (components[i] == nullptr) {
39 throw std::invalid_argument("MixtureProposal: component pointer cannot be null.");
40 }
41 if (!std::isfinite(weights[i]) || weights[i] < 0.0) {
42 throw std::invalid_argument("MixtureProposal: weights must be finite and >= 0.");
43 }
44 }
45
46 const double sumw = std::accumulate(weights.begin(), weights.end(), 0.0);
47 if (!std::isfinite(sumw) || sumw <= 0.0) {
48 throw std::invalid_argument("MixtureProposal: sum of weights must be > 0 and finite.");
49 }
50}
51
52/**
53 * @brief Normalize weight vector to sum to 1.0.
54 * @tparam dim Dimensionality parameter.
55 * @param weights Unnormalized weights (must sum to > 0).
56 * @return Vector where weights[i] / sum(weights).
57 */
58template <size_t dim>
59std::vector<double> MixtureProposal<dim>::normalizeWeights(const std::vector<double>& weights)
60{
61 const double sumw = std::accumulate(weights.begin(), weights.end(), 0.0);
62
63 std::vector<double> out;
64 out.reserve(weights.size());
65
66 for (double wi : weights) {
67 out.push_back(wi / sumw);
68 }
69 return out;
70}
71
72/**
73 * @brief Construct a mixture from proposal components and weights.
74 * @tparam dim Dimensionality parameter.
75 * @param components Vector of non-owning pointers to Proposal<dim> (q_k).
76 * @param weights Vector of component weights (w_k), will be normalized.
77 *
78 * @throws std::invalid_argument If validation fails.
79 *
80 * @details Creates a mixture distribution:
81 * q(x) = ∑ₖ wₖ q_k(x)
82 *
83 * where components q_k are sampled from via categorical selection.
84 * IMPORTANT: Components must outlive this MixtureProposal instance.
85 */
86template <size_t dim>
87MixtureProposal<dim>::MixtureProposal(std::vector<const Proposal<dim>*> components,
88 std::vector<double> weights)
89 : comps(std::move(components))
90{
91 validateInputs(comps, weights);
92 w = normalizeWeights(weights);
93
94 // Build categorical distribution for sampling component indices.
95 cat = std::discrete_distribution<std::size_t>(w.begin(), w.end());
96}
97
98/**
99 * @brief Sample from the mixture distribution.
100 * @tparam dim Dimensionality parameter.
101 * @param rng Mersenne Twister random generator.
102 * @return Point sampled by: (1) selecting component k ~ Cat(w), (2) sampling from q_k.
103 *
104 * @details Two-stage sampling:
105 * 1. Draw component index k from categorical distribution over weights
106 * 2. Draw sample x ~ q_k(x)
107 */
108template <size_t dim>
109mc::geom::Point<dim> MixtureProposal<dim>::sample(std::mt19937& rng) const
110{
111 const std::size_t k = cat(rng);
112 return comps[k]->sample(rng);
113}
114
115/**
116 * @brief Evaluate the mixture probability density.
117 * @tparam dim Dimensionality parameter.
118 * @param x Query point.
119 * @return q(x) = ∑ₖ wₖ q_k(x)
120 *
121 * @details Weighted sum of component PDFs. Time complexity: O(num_components * dim).
122 */
123template <size_t dim>
124double MixtureProposal<dim>::pdf(const mc::geom::Point<dim>& x) const
125{
126 double acc = 0.0;
127 for (std::size_t k = 0; k < comps.size(); ++k) {
128 acc += w[k] * comps[k]->pdf(x);
129 }
130 return acc;
131}
132
133} // namespace proposals
134} // namespace mc
135
136#endif // MONTECARLO_1_MIXTURE_PROPOSAL_TPP