Guest User

Untitled

a guest
Jan 12th, 2018
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.72 KB | None | 0 0
  1. #ifndef BOOST_MATH_QUADRATURE_NAIVE_MONTE_CARLO_HPP
  2. #define BOOST_MATH_QUADRATURE_NAIVE_MONTE_CARLO_HPP
  3. #include <algorithm>
  4. #include <vector>
  5. #include <atomic>
  6. #include <functional>
  7. #include <future>
  8. #include <thread>
  9. #include <initializer_list>
  10. #include <utility>
  11. #include <random>
  12. #include <chrono>
  13. #include <map>
  14.  
  15. namespace boost { namespace math { namespace quadrature {
  16.  
  17. namespace detail {
  18. enum class limit_classification {FINITE,
  19. LOWER_BOUND_INFINITE,
  20. UPPER_BOUND_INFINITE,
  21. DOUBLE_INFINITE};
  22. }
  23.  
  24. template<class Real, class F, class Policy = boost::math::policies::policy<>>
  25. class naive_monte_carlo
  26. {
  27. public:
  28. naive_monte_carlo(const F& f,
  29. std::vector<std::pair<Real, Real>> const & bounds,
  30. Real error_goal,
  31. size_t threads = std::thread::hardware_concurrency()): m_num_threads{threads}
  32. {
  33. using std::isfinite;
  34. using std::numeric_limits;
  35. size_t n = bounds.size();
  36. m_lbs.resize(n);
  37. m_dxs.resize(n);
  38. m_limit_types.resize(n);
  39. m_volume = 1;
  40. static const char* function = "boost::math::quadrature::naive_monte_carlo<%1%>";
  41. for (size_t i = 0; i < n; ++i)
  42. {
  43. if (bounds[i].second <= bounds[i].first)
  44. {
  45. boost::math::policies::raise_domain_error(function, "The upper bound is <= the lower bound.n", bounds[i].second, Policy());
  46. return;
  47. }
  48. if (bounds[i].first == -numeric_limits<Real>::infinity())
  49. {
  50. if (bounds[i].second == numeric_limits<Real>::infinity())
  51. {
  52. m_limit_types[i] = detail::limit_classification::DOUBLE_INFINITE;
  53. }
  54. else
  55. {
  56. m_limit_types[i] = detail::limit_classification::LOWER_BOUND_INFINITE;
  57. // Ok ok this is bad:
  58. m_lbs[i] = bounds[i].second;
  59. m_dxs[i] = std::numeric_limits<Real>::quiet_NaN();
  60. }
  61. }
  62. else if (bounds[i].second == numeric_limits<Real>::infinity())
  63. {
  64. m_limit_types[i] = detail::limit_classification::UPPER_BOUND_INFINITE;
  65. m_lbs[i] = bounds[i].first;
  66. m_dxs[i] = std::numeric_limits<Real>::quiet_NaN();
  67. }
  68. else
  69. {
  70. m_limit_types[i] = detail::limit_classification::FINITE;
  71. m_lbs[i] = bounds[i].first;
  72. m_dxs[i] = bounds[i].second - m_lbs[i];
  73. m_volume *= m_dxs[i];
  74. }
  75. }
  76.  
  77. m_f = [this, &f](std::vector<Real> & x)->Real
  78. {
  79. Real coeff = m_volume;
  80. for (size_t i = 0; i < x.size(); ++i)
  81. {
  82. // Variable transformation are listed at:
  83. // https://en.wikipedia.org/wiki/Numerical_integration
  84. if (m_limit_types[i] == detail::limit_classification::FINITE)
  85. {
  86. x[i] = m_lbs[i] + x[i]*m_dxs[i];
  87. }
  88. else if (m_limit_types[i] == detail::limit_classification::UPPER_BOUND_INFINITE)
  89. {
  90. Real t = x[i];
  91. Real z = 1/(1-t);
  92. coeff *= (z*z);
  93. x[i] = m_lbs[i] + t*z;
  94. }
  95. else if (m_limit_types[i] == detail::limit_classification::LOWER_BOUND_INFINITE)
  96. {
  97. Real t = x[i];
  98. Real z = 1/t;
  99. coeff *= (z*z);
  100. x[i] = m_lbs[i] + (t-1)*z;
  101. }
  102. else
  103. {
  104. Real t = 2*x[i] - 1;
  105. Real tsq = t*t;
  106. Real z = 1/(1-t);
  107. z /= (1+t);
  108. coeff *= 2*(1+tsq)*z*z;
  109. x[i] = t*z;
  110. }
  111. }
  112. return coeff*f(x);
  113. };
  114.  
  115. // If we don't do a single function call in the constructor,
  116. // we can't do a restart.
  117. std::vector<Real> x(m_lbs.size());
  118. std::random_device rd;
  119. std::mt19937_64 gen(rd());
  120. Real inv_denom = (Real) 1/( (Real) gen.max() + (Real) 2);
  121.  
  122. if (m_num_threads == 0)
  123. {
  124. m_num_threads = 1;
  125. }
  126. Real avg = 0;
  127. for (size_t i = 0; i < m_num_threads; ++i)
  128. {
  129. for (size_t j = 0; j < m_lbs.size(); ++j)
  130. {
  131. x[j] = (gen()+1)*inv_denom;
  132. while (x[j] < std::numeric_limits<Real>::epsilon() || x[j] > 1 - std::numeric_limits<Real>::epsilon())
  133. {
  134. x[j] = (gen()+1)*inv_denom;
  135. }
  136. }
  137. Real y = m_f(x);
  138. m_thread_averages.emplace(i, y);
  139. m_thread_calls.emplace(i, 1);
  140. m_thread_Ss.emplace(i, 0);
  141. avg += y;
  142. }
  143. avg /= m_num_threads;
  144. m_avg = avg;
  145.  
  146. m_error_goal = error_goal;
  147. m_start = std::chrono::system_clock::now();
  148. m_done = false;
  149. m_total_calls = m_num_threads;
  150. m_variance = std::numeric_limits<Real>::max();
  151. }
  152.  
  153. std::future<Real> integrate()
  154. {
  155. // Set done to false in case we wish to restart:
  156. m_done = false;
  157. return std::async(std::launch::async,
  158. &naive_monte_carlo::m_integrate, this);
  159. }
  160.  
  161. void cancel()
  162. {
  163. m_done = true;
  164. }
  165.  
  166. Real variance() const
  167. {
  168. return m_variance.load();
  169. }
  170.  
  171. Real current_error_estimate() const
  172. {
  173. using std::sqrt;
  174. return sqrt(m_variance.load()/m_total_calls.load());
  175. }
  176.  
  177. std::chrono::duration<Real> estimated_time_to_completion() const
  178. {
  179. auto now = std::chrono::system_clock::now();
  180. std::chrono::duration<Real> elapsed_seconds = now - m_start;
  181. Real r = this->current_error_estimate()/m_error_goal.load();
  182. if (r*r <= 1) {
  183. return 0*elapsed_seconds;
  184. }
  185. return (r*r - 1)*elapsed_seconds;
  186. }
  187.  
  188. void update_target_error(Real new_target_error)
  189. {
  190. m_error_goal = new_target_error;
  191. }
  192.  
  193. Real progress() const
  194. {
  195. Real r = m_error_goal.load()/this->current_error_estimate();
  196. if (r*r >= 1)
  197. {
  198. return 1;
  199. }
  200. return r*r;
  201. }
  202.  
  203. Real current_estimate() const
  204. {
  205. return m_avg.load();
  206. }
  207.  
  208. size_t calls() const
  209. {
  210. return m_total_calls.load();
  211. }
  212.  
  213. private:
  214.  
  215. Real m_integrate()
  216. {
  217. m_start = std::chrono::system_clock::now();
  218. std::vector<std::thread> threads(m_num_threads);
  219. for (size_t i = 0; i < threads.size(); ++i)
  220. {
  221. threads[i] = std::thread(&naive_monte_carlo::m_thread_monte, this, i);
  222. }
  223. do {
  224. std::this_thread::sleep_for(std::chrono::milliseconds(500));
  225. size_t total_calls = 0;
  226. for (size_t i = 0; i < m_num_threads; ++i)
  227. {
  228. total_calls += m_thread_calls[i];
  229. }
  230. Real variance = 0;
  231. Real avg = 0;
  232. for (size_t i = 0; i < m_num_threads; ++i)
  233. {
  234. size_t t_calls = m_thread_calls[i];
  235. // Will this overflow? Not hard to remove . . .
  236. avg += m_thread_averages[i]*( (Real) t_calls/ (Real) total_calls);
  237. variance += m_thread_Ss[i];
  238. }
  239. m_avg = avg;
  240. m_variance = variance/(total_calls - 1);
  241. m_total_calls = total_calls;
  242. // Allow cancellation:
  243. if (m_done)
  244. {
  245. break;
  246. }
  247. } while (this->current_error_estimate() > m_error_goal);
  248. // Error bound met; signal the threads:
  249. m_done = true;
  250. std::for_each(threads.begin(), threads.end(),
  251. std::mem_fn(&std::thread::join));
  252. if (m_exception)
  253. {
  254. std::rethrow_exception(m_exception);
  255. }
  256. // Incorporate their work into the final estimate:
  257. size_t total_calls = 0;
  258. for (size_t i = 0; i < m_num_threads; ++i)
  259. {
  260. total_calls += m_thread_calls[i];
  261. }
  262. Real variance = 0;
  263. Real avg = 0;
  264. for (size_t i = 0; i < m_num_threads; ++i)
  265. {
  266. size_t t_calls = m_thread_calls[i];
  267. // Will this overflow? Not hard to remove . . .
  268. avg += m_thread_averages[i]*( (Real) t_calls/ (Real) total_calls);
  269. variance += m_thread_Ss[i];
  270. }
  271. m_avg = avg;
  272. m_variance = variance/(total_calls - 1);
  273. m_total_calls = total_calls;
  274.  
  275. return m_avg.load();
  276. }
  277.  
  278. void m_thread_monte(size_t thread_index)
  279. {
  280. try
  281. {
  282. std::vector<Real> x(m_lbs.size());
  283. std::random_device rd;
  284. // Should we do something different if we have no entropy?
  285. // Apple LLVM version 9.0.0 (clang-900.0.38) has no entropy,
  286. // but rd() returns a reasonable random sequence.
  287. // if (rd.entropy() == 0)
  288. // {
  289. // std::cout << "OMG! we have no entropy.n";
  290. // }
  291. std::mt19937_64 gen(rd());
  292. Real inv_denom = (Real) 1/( (Real) gen.max() + (Real) 2);
  293. Real M1 = m_thread_averages[thread_index];
  294. Real S = m_thread_Ss[thread_index];
  295. // Kahan summation is required. See the implementation discussion.
  296. Real compensator = 0;
  297. size_t k = m_thread_calls[thread_index];
  298. while (!m_done)
  299. {
  300. int j = 0;
  301. // If we don't have a certain number of calls before an update, we can easily terminate prematurely
  302. // because the variance estimate is way too low.
  303. int magic_calls_before_update = 2048;
  304. while (j++ < magic_calls_before_update)
  305. {
  306. for (size_t i = 0; i < m_lbs.size(); ++i)
  307. {
  308. x[i] = (gen()+1)*inv_denom;
  309. while (x[i] < std::numeric_limits<Real>::epsilon() || x[i] > 1 - std::numeric_limits<Real>::epsilon())
  310. {
  311. x[i] = (gen()+1)*inv_denom;
  312. }
  313. }
  314. Real f = m_f(x);
  315. ++k;
  316. Real term = (f - M1)/k;
  317. Real y1 = term - compensator;
  318. Real M2 = M1 + y1;
  319. compensator = (M2 - M1) - y1;
  320. S += (f - M1)*(f - M2);
  321. M1 = M2;
  322. }
  323. m_thread_averages[thread_index] = M1;
  324. m_thread_Ss[thread_index] = S;
  325. m_thread_calls[thread_index] = k;
  326. }
  327. }
  328. catch (...)
  329. {
  330. // Signal the other threads that the computation is ruined:
  331. m_done = true;
  332. m_exception = std::current_exception();
  333. }
  334. }
  335.  
  336. std::function<Real(std::vector<Real> &)> m_f;
  337. size_t m_num_threads;
  338. std::atomic<Real> m_error_goal;
  339. std::atomic<bool> m_done;
  340. std::vector<Real> m_lbs;
  341. std::vector<Real> m_dxs;
  342. std::vector<detail::limit_classification> m_limit_types;
  343. Real m_volume;
  344. std::atomic<size_t> m_total_calls;
  345. // I wanted these to be vectors rather than maps,
  346. // but you can't resize a vector of atomics.
  347. std::map<size_t, std::atomic<size_t>> m_thread_calls;
  348. std::atomic<Real> m_variance;
  349. std::map<size_t, std::atomic<Real>> m_thread_Ss;
  350. std::atomic<Real> m_avg;
  351. std::map<size_t, std::atomic<Real>> m_thread_averages;
  352. std::chrono::time_point<std::chrono::system_clock> m_start;
  353. std::exception_ptr m_exception;
  354. };
  355.  
  356. }}}
  357. #endif
Advertisement
Add Comment
Please, Sign In to add comment