Algorithms_in_C++ 1.0.0
Set of algorithms implemented in C++.
Loading...
Searching...
No Matches
integral_approximation2.cpp File Reference

Monte Carlo Integration More...

#include <cmath>
#include <cstdint>
#include <ctime>
#include <functional>
#include <iostream>
#include <random>
#include <vector>
Include dependency graph for integral_approximation2.cpp:

Namespaces

namespace  math
 for IO operations
 
namespace  monte_carlo
 Functions for the Monte Carlo Integration implementation.
 

Typedefs

using math::monte_carlo::Function
 

Functions

std::vector< double > math::monte_carlo::generate_samples (const double &start_point, const Function &pdf, const uint32_t &num_samples, const uint32_t &discard=100000)
 short-hand for std::functions used in this implementation
 
double math::monte_carlo::integral_monte_carlo (const double &start_point, const Function &function, const Function &pdf, const uint32_t &num_samples=1000000)
 Compute an approximation of an integral using Monte Carlo integration.
 
static void test ()
 Self-test implementations.
 
int main ()
 Main function.
 

Detailed Description

Monte Carlo Integration

In mathematics, Monte Carlo integration is a technique for numerical integration using random numbers. It is a particular Monte Carlo method that numerically computes a definite integral. While other algorithms usually evaluate the integrand at a regular grid, Monte Carlo randomly chooses points at which the integrand is evaluated. This method is particularly useful for higher-dimensional integrals.

This implementation supports arbitrary pdfs. These pdfs are sampled using the Metropolis-Hastings algorithm. This can be swapped out by every other sampling techniques for example the inverse method. Metropolis-Hastings was chosen because it is the most general and can also be extended for a higher dimensional sampling space.

Author
Domenic Zingsheim

Typedef Documentation

◆ Function

Initial value:
std::function<double(
double&)>

Function Documentation

◆ generate_samples()

std::vector< double > math::monte_carlo::generate_samples ( const double & start_point,
const Function & pdf,
const uint32_t & num_samples,
const uint32_t & discard = 100000 )

short-hand for std::functions used in this implementation

Generate samples according to some pdf

This function uses Metropolis-Hastings to generate random numbers. It generates a sequence of random numbers by using a markov chain. Therefore, we need to define a start_point and the number of samples we want to generate. Because the first samples generated by the markov chain may not be distributed according to the given pdf, one can specify how many samples should be discarded before storing samples.

Parameters
start_pointThe starting point of the markov chain
pdfThe pdf to sample
num_samplesThe number of samples to generate
discardHow many samples should be discarded at the start
Returns
A vector of size num_samples with samples distributed according to the pdf
67 {
68 std::vector<double> samples;
69 samples.reserve(num_samples);
70
71 double x_t = start_point;
72
75 std::normal_distribution<double> normal(0.0, 1.0);
76 generator.seed(time(nullptr));
77
78 for (uint32_t t = 0; t < num_samples + discard; ++t) {
79 // Generate a new proposal according to some mutation strategy.
80 // This is arbitrary and can be swapped.
81 double x_dash = normal(generator) + x_t;
82 double acceptance_probability = std::min(pdf(x_dash) / pdf(x_t), 1.0);
83 double u = uniform(generator);
84
85 // Accept "new state" according to the acceptance_probability
86 if (u <= acceptance_probability) {
87 x_t = x_dash;
88 }
89
90 if (t >= discard) {
91 samples.push_back(x_t);
92 }
93 }
94
95 return samples;
96}
T min(T... args)
T push_back(T... args)
T reserve(T... args)
T time(T... args)
Here is the call graph for this function:

◆ integral_monte_carlo()

double math::monte_carlo::integral_monte_carlo ( const double & start_point,
const Function & function,
const Function & pdf,
const uint32_t & num_samples = 1000000 )

Compute an approximation of an integral using Monte Carlo integration.

The integration domain [a,b] is given by the pdf. The pdf has to fulfill the following conditions: 1) for all x \in [a,b] : p(x) > 0 2) for all x \not\in [a,b] : p(x) = 0 3) \int_a^b p(x) dx = 1

Parameters
start_pointThe start point of the Markov Chain (see generate_samples)
functionThe function to integrate
pdfThe pdf to sample
num_samplesThe number of samples used to approximate the integral
Returns
The approximation of the integral according to 1/N \sum_{i}^N f(x_i) / p(x_i)
114 {
115 double integral = 0.0;
116 std::vector<double> samples =
117 generate_samples(start_point, pdf, num_samples);
118
119 for (double sample : samples) {
120 integral += function(sample) / pdf(sample);
121 }
122
123 return integral / static_cast<double>(samples.size());
124}
std::vector< double > generate_samples(const double &start_point, const Function &pdf, const uint32_t &num_samples, const uint32_t &discard=100000)
short-hand for std::functions used in this implementation
Definition integral_approximation2.cpp:64
T size(T... args)
Here is the call graph for this function:

◆ main()

int main ( void )

Main function.

Returns
0 on exit
215 {
216 test(); // run self-test implementations
217 return 0;
218}
static void test()
Self-test implementations.
Definition integral_approximation2.cpp:133
Here is the call graph for this function:

◆ test()

static void test ( )
static

Self-test implementations.

Returns
void
133 {
134 std::cout << "Disclaimer: Because this is a randomized algorithm,"
135 << std::endl;
137 << "it may happen that singular samples deviate from the true result."
138 << std::endl
139 << std::endl;
140 ;
141
144 double integral = 0;
145 double lower_bound = 0, upper_bound = 0;
146
147 /* \int_{-2}^{2} -x^2 + 4 dx */
148 f = [&](double& x) { return -x * x + 4.0; };
149
150 lower_bound = -2.0;
151 upper_bound = 2.0;
152 pdf = [&](double& x) {
153 if (x >= lower_bound && x <= -1.0) {
154 return 0.1;
155 }
156 if (x <= upper_bound && x >= 1.0) {
157 return 0.1;
158 }
159 if (x > -1.0 && x < 1.0) {
160 return 0.4;
161 }
162 return 0.0;
163 };
164
166 (upper_bound - lower_bound) / 2.0, f, pdf);
167
168 std::cout << "This number should be close to 10.666666: " << integral
169 << std::endl;
170
171 /* \int_{0}^{1} e^x dx */
172 f = [&](double& x) { return std::exp(x); };
173
174 lower_bound = 0.0;
175 upper_bound = 1.0;
176 pdf = [&](double& x) {
177 if (x >= lower_bound && x <= 0.2) {
178 return 0.1;
179 }
180 if (x > 0.2 && x <= 0.4) {
181 return 0.4;
182 }
183 if (x > 0.4 && x < upper_bound) {
184 return 1.5;
185 }
186 return 0.0;
187 };
188
190 (upper_bound - lower_bound) / 2.0, f, pdf);
191
192 std::cout << "This number should be close to 1.7182818: " << integral
193 << std::endl;
194
195 /* \int_{-\infty}^{\infty} sinc(x) dx, sinc(x) = sin(pi * x) / (pi * x)
196 This is a difficult integral because of its infinite domain.
197 Therefore, it may deviate largely from the expected result.
198 */
199 f = [&](double& x) { return std::sin(M_PI * x) / (M_PI * x); };
200
201 pdf = [&](double& x) {
202 return 1.0 / std::sqrt(2.0 * M_PI) * std::exp(-x * x / 2.0);
203 };
204
205 integral = math::monte_carlo::integral_monte_carlo(0.0, f, pdf, 10000000);
206
207 std::cout << "This number should be close to 1.0: " << integral
208 << std::endl;
209}
double f(double x)
A function f(x) that will be used to test the method.
Definition composite_simpson_rule.cpp:113
T endl(T... args)
T exp(T... args)
double integral_monte_carlo(const double &start_point, const Function &function, const Function &pdf, const uint32_t &num_samples=1000000)
Compute an approximation of an integral using Monte Carlo integration.
Definition integral_approximation2.cpp:112
T lower_bound(T... args)
T sin(T... args)
T sqrt(T... args)
T upper_bound(T... args)
Here is the call graph for this function: