Algorithms_in_C++ 1.0.0
Set of algorithms implemented in C++.
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 assert
 
namespace  monte_carlo
 Functions for the Monte Carlo Integration implementation.
 

Typedefs

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

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 More...
 
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. More...
 
static void test ()
 Self-test implementations. More...
 
int main ()
 Main function. More...
 

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

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
53 {
54 std::vector<double> samples;
55 samples.reserve(num_samples);
56
57 double x_t = start_point;
58
61 std::normal_distribution<double> normal(0.0, 1.0);
62 generator.seed(time(nullptr));
63
64 for(uint32_t t = 0; t < num_samples + discard; ++t) {
65 // Generate a new proposal according to some mutation strategy.
66 // This is arbitrary and can be swapped.
67 double x_dash = normal(generator) + x_t;
68 double acceptance_probability = std::min(pdf(x_dash)/pdf(x_t), 1.0);
69 double u = uniform(generator);
70
71 // Accept "new state" according to the acceptance_probability
72 if(u <= acceptance_probability) {
73 x_t = x_dash;
74 }
75
76 if(t >= discard) {
77 samples.push_back(x_t);
78 }
79 }
80
81 return samples;
82}
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)
97 {
98 double integral = 0.0;
99 std::vector<double> samples = generate_samples(start_point, pdf, num_samples);
100
101 for(double sample : samples) {
102 integral += function(sample) / pdf(sample);
103 }
104
105 return integral / static_cast<double>(samples.size());
106}
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:53
Here is the call graph for this function:

◆ main()

int main ( void  )

Main function.

Returns
0 on exit
193 {
194 test(); // run self-test implementations
195 return 0;
196}
static void test()
Self-test implementations.
Definition: integral_approximation2.cpp:115
Here is the call graph for this function:

◆ test()

static void test ( )
static

Self-test implementations.

Returns
void
115 {
116 std::cout << "Disclaimer: Because this is a randomized algorithm," << std::endl;
117 std::cout << "it may happen that singular samples deviate from the true result." << std::endl << std::endl;;
118
121 double integral = 0;
122 double lower_bound = 0, upper_bound = 0;
123
124 /* \int_{-2}^{2} -x^2 + 4 dx */
125 f = [&](double& x) {
126 return -x*x + 4.0;
127 };
128
129 lower_bound = -2.0;
130 upper_bound = 2.0;
131 pdf = [&](double& x) {
132 if(x >= lower_bound && x <= -1.0) {
133 return 0.1;
134 }
135 if(x <= upper_bound && x >= 1.0) {
136 return 0.1;
137 }
138 if(x > -1.0 && x < 1.0) {
139 return 0.4;
140 }
141 return 0.0;
142 };
143
144 integral = math::monte_carlo::integral_monte_carlo((upper_bound - lower_bound) / 2.0, f, pdf);
145
146 std::cout << "This number should be close to 10.666666: " << integral << std::endl;
147
148 /* \int_{0}^{1} e^x dx */
149 f = [&](double& x) {
150 return std::exp(x);
151 };
152
153 lower_bound = 0.0;
154 upper_bound = 1.0;
155 pdf = [&](double& x) {
156 if(x >= lower_bound && x <= 0.2) {
157 return 0.1;
158 }
159 if(x > 0.2 && x <= 0.4) {
160 return 0.4;
161 }
162 if(x > 0.4 && x < upper_bound) {
163 return 1.5;
164 }
165 return 0.0;
166 };
167
168 integral = math::monte_carlo::integral_monte_carlo((upper_bound - lower_bound) / 2.0, f, pdf);
169
170 std::cout << "This number should be close to 1.7182818: " << integral << std::endl;
171
172 /* \int_{-\infty}^{\infty} sinc(x) dx, sinc(x) = sin(pi * x) / (pi * x)
173 This is a difficult integral because of its infinite domain.
174 Therefore, it may deviate largely from the expected result.
175 */
176 f = [&](double& x) {
177 return std::sin(M_PI * x) / (M_PI * x);
178 };
179
180 pdf = [&](double& x) {
181 return 1.0 / std::sqrt(2.0 * M_PI) * std::exp(-x * x / 2.0);
182 };
183
184 integral = math::monte_carlo::integral_monte_carlo(0.0, f, pdf, 10000000);
185
186 std::cout << "This number should be close to 1.0: " << integral << std::endl;
187}
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:97
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: