2 * Copyright Nick Thompson, 2017
3 * Use, modification and distribution are subject to the
4 * Boost Software License, Version 1.0. (See accompanying file
5 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 #define BOOST_TEST_MODULE naive_monte_carlo_test
10 #include <boost/lexical_cast.hpp>
11 #include <boost/type_index.hpp>
12 #include <boost/test/included/unit_test.hpp>
14 #include <boost/test/floating_point_comparison.hpp>
15 #include <boost/math/constants/constants.hpp>
16 #include <boost/math/quadrature/naive_monte_carlo.hpp>
21 using boost::math::constants::pi
;
22 using boost::math::quadrature::naive_monte_carlo
;
26 void test_pi_multithreaded()
28 std::cout
<< "Testing pi is calculated correctly (multithreaded) using Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
29 auto g
= [](std::vector
<Real
> const & x
)->Real
{
30 Real r
= x
[0]*x
[0]+x
[1]*x
[1];
37 std::vector
<std::pair
<Real
, Real
>> bounds
{{Real(0), Real(1)}, {Real(0), Real(1)}};
38 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.0005,
39 /*singular =*/ false,/* threads = */ 2, /* seed = */ 17);
40 auto task
= mc
.integrate();
41 Real pi_estimated
= task
.get();
42 if (abs(pi_estimated
- pi
<Real
>())/pi
<Real
>() > 0.005) {
43 std::cout
<< "Error in estimation of pi too high, function calls: " << mc
.calls() << "\n";
44 BOOST_CHECK_CLOSE_FRACTION(pi_estimated
, pi
<Real
>(), 0.005);
51 std::cout
<< "Testing pi is calculated correctly using Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
52 auto g
= [](std::vector
<Real
> const & x
)->Real
54 Real r
= x
[0]*x
[0]+x
[1]*x
[1];
62 std::vector
<std::pair
<Real
, Real
>> bounds
{{Real(0), Real(1)}, {Real(0), Real(1)}};
63 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.0005,
64 /*singular =*/ false,/* threads = */ 1, /* seed = */ 17);
65 auto task
= mc
.integrate();
66 Real pi_estimated
= task
.get();
67 if (abs(pi_estimated
- pi
<Real
>())/pi
<Real
>() > 0.005)
69 std::cout
<< "Error in estimation of pi too high, function calls: " << mc
.calls() << "\n";
70 BOOST_CHECK_CLOSE_FRACTION(pi_estimated
, pi
<Real
>(), 0.005);
78 std::cout
<< "Testing constants are integrated correctly using Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
79 auto g
= [](std::vector
<Real
> const &)->Real
84 std::vector
<std::pair
<Real
, Real
>> bounds
{{Real(0), Real(1)}, { Real(0), Real(1)}};
85 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.0001,
86 /* singular = */ false, /* threads = */ 1, /* seed = */ 87);
88 auto task
= mc
.integrate();
89 Real one
= task
.get();
90 BOOST_CHECK_CLOSE_FRACTION(one
, 1, 0.001);
91 BOOST_CHECK_SMALL(mc
.current_error_estimate(), std::numeric_limits
<Real
>::epsilon());
92 BOOST_CHECK(mc
.calls() > 1000);
97 void test_exception_from_integrand()
99 std::cout
<< "Testing that a reasonable action is performed by the Monte-Carlo integrator when the integrand throws an exception on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
100 auto g
= [](std::vector
<Real
> const & x
)->Real
102 if (x
[0] > 0.5 && x
[0] < 0.5001)
104 throw std::domain_error("You have done something wrong.\n");
109 std::vector
<std::pair
<Real
, Real
>> bounds
{{ Real(0), Real(1)}, { Real(0), Real(1)}};
110 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.0001);
112 auto task
= mc
.integrate();
113 bool caught_exception
= false;
116 Real result
= task
.get();
117 // Get rid of unused variable warning:
118 std::ostream
cnull(0);
121 catch(std::exception
const &)
123 caught_exception
= true;
125 BOOST_CHECK(caught_exception
);
130 void test_cancel_and_restart()
132 std::cout
<< "Testing that cancellation and restarting works on naive Monte-Carlo integration on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
133 Real exact
= boost::lexical_cast
<Real
>("1.3932039296856768591842462603255");
134 BOOST_CONSTEXPR
const Real A
= 1.0 / (pi
<Real
>() * pi
<Real
>() * pi
<Real
>());
135 auto g
= [&](std::vector
<Real
> const & x
)->Real
137 return A
/ (1.0 - cos(x
[0])*cos(x
[1])*cos(x
[2]));
139 vector
<pair
<Real
, Real
>> bounds
{{ Real(0), pi
<Real
>()}, { Real(0), pi
<Real
>()}, { Real(0), pi
<Real
>()}};
140 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.05, true, 1, 888889);
142 auto task
= mc
.integrate();
144 double y
= task
.get();
145 // Super low tolerance; because it got canceled so fast:
146 BOOST_CHECK_CLOSE_FRACTION(y
, exact
, 1.0);
148 mc
.update_target_error((Real
) 0.01);
149 task
= mc
.integrate();
151 BOOST_CHECK_CLOSE_FRACTION(y
, exact
, 0.1);
155 void test_finite_singular_boundary()
157 std::cout
<< "Testing that finite singular boundaries work on naive Monte-Carlo integration on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
160 auto g
= [](std::vector
<Real
> const & x
)->Real
162 // The first term is singular at x = 0.
163 // The second at x = 1:
164 return pow(log(1.0/x
[0]), 2) + log1p(-x
[0]);
166 vector
<pair
<Real
, Real
>> bounds
{{Real(0), Real(1)}};
167 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.01, true, 1, 1922);
169 auto task
= mc
.integrate();
171 double y
= task
.get();
172 BOOST_CHECK_CLOSE_FRACTION(y
, 1.0, 0.1);
176 void test_multithreaded_variance()
178 std::cout
<< "Testing that variance computed by naive Monte-Carlo integration converges to integral formula on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
179 Real exact_variance
= (Real
) 1/(Real
) 12;
180 auto g
= [&](std::vector
<Real
> const & x
)->Real
184 vector
<pair
<Real
, Real
>> bounds
{{ Real(0), Real(1)}};
185 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, false, 2, 12341);
187 auto task
= mc
.integrate();
189 BOOST_CHECK_CLOSE_FRACTION(y
, 0.5, 0.01);
190 BOOST_CHECK_CLOSE_FRACTION(mc
.variance(), exact_variance
, 0.05);
196 std::cout
<< "Testing that variance computed by naive Monte-Carlo integration converges to integral formula on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
197 Real exact_variance
= (Real
) 1/(Real
) 12;
198 auto g
= [&](std::vector
<Real
> const & x
)->Real
202 vector
<pair
<Real
, Real
>> bounds
{{ Real(0), Real(1)}};
203 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, false, 1, 12341);
205 auto task
= mc
.integrate();
207 BOOST_CHECK_CLOSE_FRACTION(y
, 0.5, 0.01);
208 BOOST_CHECK_CLOSE_FRACTION(mc
.variance(), exact_variance
, 0.05);
211 template<class Real
, size_t dimension
>
214 std::cout
<< "Testing that product functions are integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
215 auto g
= [&](std::vector
<Real
> const & x
)->Real
218 for (size_t i
= 0; i
< x
.size(); ++i
)
225 vector
<pair
<Real
, Real
>> bounds(dimension
);
226 for (size_t i
= 0; i
< dimension
; ++i
)
228 bounds
[i
] = std::make_pair
<Real
, Real
>(0, 1);
230 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, false, 1, 13999);
232 auto task
= mc
.integrate();
234 BOOST_CHECK_CLOSE_FRACTION(y
, 1, 0.01);
236 Real exact_variance
= pow(4.0/3.0, dimension
) - 1;
237 BOOST_CHECK_CLOSE_FRACTION(mc
.variance(), exact_variance
, 0.1);
240 template<class Real
, size_t dimension
>
241 void test_alternative_rng()
243 std::cout
<< "Testing that alternative RNGs work correctly using naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
244 auto g
= [&](std::vector
<Real
> const & x
)->Real
247 for (size_t i
= 0; i
< x
.size(); ++i
)
254 vector
<pair
<Real
, Real
>> bounds(dimension
);
255 for (size_t i
= 0; i
< dimension
; ++i
)
257 bounds
[i
] = std::make_pair
<Real
, Real
>(0, 1);
259 naive_monte_carlo
<Real
, decltype(g
), std::mt19937
> mc(g
, bounds
, (Real
) 0.001, false, 1, 1882);
261 auto task
= mc
.integrate();
263 BOOST_CHECK_CLOSE_FRACTION(y
, 1, 0.01);
265 Real exact_variance
= pow(4.0/3.0, dimension
) - 1;
266 BOOST_CHECK_CLOSE_FRACTION(mc
.variance(), exact_variance
, 0.05);
270 void test_upper_bound_infinite()
272 std::cout
<< "Testing that infinite upper bounds are integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
273 auto g
= [](std::vector
<Real
> const & x
)->Real
275 return 1.0/(x
[0]*x
[0] + 1.0);
278 vector
<pair
<Real
, Real
>> bounds(1);
279 for (size_t i
= 0; i
< bounds
.size(); ++i
)
281 bounds
[i
] = std::make_pair
<Real
, Real
>(0, std::numeric_limits
<Real
>::infinity());
283 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, true, 1, 8765);
284 auto task
= mc
.integrate();
286 BOOST_CHECK_CLOSE_FRACTION(y
, boost::math::constants::half_pi
<Real
>(), 0.01);
290 void test_lower_bound_infinite()
292 std::cout
<< "Testing that infinite lower bounds are integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
293 auto g
= [](std::vector
<Real
> const & x
)->Real
295 return 1.0/(x
[0]*x
[0] + 1.0);
298 vector
<pair
<Real
, Real
>> bounds(1);
299 for (size_t i
= 0; i
< bounds
.size(); ++i
)
301 bounds
[i
] = std::make_pair
<Real
, Real
>(-std::numeric_limits
<Real
>::infinity(), 0);
303 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, true, 1, 1208);
305 auto task
= mc
.integrate();
307 BOOST_CHECK_CLOSE_FRACTION(y
, boost::math::constants::half_pi
<Real
>(), 0.01);
311 void test_lower_bound_infinite2()
313 std::cout
<< "Testing that infinite lower bounds (2) are integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
314 auto g
= [](std::vector
<Real
> const & x
)->Real
316 // If x[0] = inf, this should blow up:
317 return (x
[0]*x
[0])/(x
[0]*x
[0]*x
[0]*x
[0] + 1.0);
320 vector
<pair
<Real
, Real
>> bounds(1);
321 for (size_t i
= 0; i
< bounds
.size(); ++i
)
323 bounds
[i
] = std::make_pair
<Real
, Real
>(-std::numeric_limits
<Real
>::infinity(), 0);
325 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, true, 1, 1208);
326 auto task
= mc
.integrate();
328 BOOST_CHECK_CLOSE_FRACTION(y
, boost::math::constants::half_pi
<Real
>()/boost::math::constants::root_two
<Real
>(), 0.01);
332 void test_double_infinite()
334 std::cout
<< "Testing that double infinite bounds are integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
335 auto g
= [](std::vector
<Real
> const & x
)->Real
337 return 1.0/(x
[0]*x
[0] + 1.0);
340 vector
<pair
<Real
, Real
>> bounds(1);
341 for (size_t i
= 0; i
< bounds
.size(); ++i
)
343 bounds
[i
] = std::make_pair
<Real
, Real
>(-std::numeric_limits
<Real
>::infinity(), std::numeric_limits
<Real
>::infinity());
345 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, (Real
) 0.001, true, 1, 1776);
347 auto task
= mc
.integrate();
349 BOOST_CHECK_CLOSE_FRACTION(y
, boost::math::constants::pi
<Real
>(), 0.01);
352 template<class Real
, size_t dimension
>
355 // See: Generalized Halton Sequences in 2008: A Comparative Study, function g1:
356 std::cout
<< "Testing that the Radovic function is integrated correctly by naive Monte-Carlo on type " << boost::typeindex::type_id
<Real
>().pretty_name() << "\n";
357 auto g
= [](std::vector
<Real
> const & x
)->Real
360 Real alpha
= (Real
)0.01;
362 for (size_t i
= 0; i
< dimension
; ++i
)
364 z
*= (abs(4*x
[i
]-2) + alpha
)/(1+alpha
);
369 vector
<pair
<Real
, Real
>> bounds(dimension
);
370 for (size_t i
= 0; i
< bounds
.size(); ++i
)
372 bounds
[i
] = std::make_pair
<Real
, Real
>(0, 1);
374 Real error_goal
= 0.001;
375 naive_monte_carlo
<Real
, decltype(g
)> mc(g
, bounds
, error_goal
, false, 1, 1982);
377 auto task
= mc
.integrate();
379 if (abs(y
- 1) > 0.01)
381 std::cout
<< "Error in estimation of Radovic integral too high, function calls: " << mc
.calls() << "\n";
382 std::cout
<< "Final error estimate: " << mc
.current_error_estimate() << std::endl
;
383 std::cout
<< "Error goal : " << error_goal
<< std::endl
;
384 std::cout
<< "Variance estimate : " << mc
.variance() << std::endl
;
385 BOOST_CHECK_CLOSE_FRACTION(y
, 1, 0.01);
390 BOOST_AUTO_TEST_CASE(naive_monte_carlo_test
)
392 #if !defined(TEST) || TEST == 1
393 test_finite_singular_boundary
<double>();
394 test_finite_singular_boundary
<float>();
396 #if !defined(TEST) || TEST == 2
399 test_pi_multithreaded
<float>();
400 //test_pi<long double>();
402 #if !defined(TEST) || TEST == 3
403 test_constant
<float>();
404 test_constant
<double>();
405 //test_constant<long double>();
407 #if !defined(TEST) || TEST == 4
408 test_cancel_and_restart
<float>();
409 test_exception_from_integrand
<float>();
410 test_variance
<float>();
412 #if !defined(TEST) || TEST == 5
413 test_variance
<double>();
414 test_multithreaded_variance
<double>();
415 test_product
<float, 1>();
416 test_product
<float, 2>();
418 #if !defined(TEST) || TEST == 6
419 test_product
<float, 3>();
420 test_product
<float, 4>();
421 test_product
<float, 5>();
423 #if !defined(TEST) || TEST == 7
424 test_product
<float, 6>();
425 test_product
<double, 1>();
426 test_product
<double, 2>();
428 #if !defined(TEST) || TEST == 8
429 test_product
<double, 3>();
430 test_product
<double, 4>();
432 #if !defined(TEST) || TEST == 9
433 test_upper_bound_infinite
<float>();
434 test_upper_bound_infinite
<double>();
435 test_lower_bound_infinite
<float>();
436 test_lower_bound_infinite
<double>();
437 test_lower_bound_infinite2
<float>();
439 #if !defined(TEST) || TEST == 10
440 test_double_infinite
<float>();
441 test_double_infinite
<double>();
442 test_radovic
<float, 1>();
444 #if !defined(TEST) || TEST == 11
445 test_radovic
<float, 2>();
446 test_radovic
<float, 3>();
447 test_radovic
<double, 1>();
449 #if !defined(TEST) || TEST == 12
450 test_radovic
<double, 2>();
451 test_radovic
<double, 3>();
452 test_radovic
<double, 4>();
453 test_radovic
<double, 5>();
454 test_alternative_rng
<float, 3>();
455 test_alternative_rng
<double, 3>();