TheAlgorithms/C++ 1.0.0
All the 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:

Go to the source code of this file.

Namespaces

namespace  math
 for assert
 
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

Definition in file integral_approximation2.cpp.

Macro Definition Documentation

◆ _USE_MATH_DEFINES

#define _USE_MATH_DEFINES

Definition at line 24 of file integral_approximation2.cpp.

Typedef Documentation

◆ Function

using math::monte_carlo::Function
Initial value:
std::function<double(
double&)>

Definition at line 46 of file integral_approximation2.cpp.

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

Definition at line 64 of file integral_approximation2.cpp.

67 {
68 std::vector<double> samples;
69 samples.reserve(num_samples);
70
71 double x_t = start_point;
72
73 std::default_random_engine generator;
74 std::uniform_real_distribution<double> uniform(0.0, 1.0);
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}

◆ 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)

Definition at line 112 of file integral_approximation2.cpp.

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

◆ main()

int main ( void )

Main function.

Returns
0 on exit

Definition at line 215 of file integral_approximation2.cpp.

215 {
216 test(); // run self-test implementations
217 return 0;
218}
static void test()
Self-test implementations.

◆ test()

static void test ( )
static

Self-test implementations.

Returns
void

Definition at line 133 of file integral_approximation2.cpp.

133 {
134 std::cout << "Disclaimer: Because this is a randomized algorithm,"
135 << std::endl;
136 std::cout
137 << "it may happen that singular samples deviate from the true result."
138 << std::endl
139 << std::endl;
140 ;
141
142 math::monte_carlo::Function f;
143 math::monte_carlo::Function pdf;
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.
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.