single_pass.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // (C) Copyright Nick Thompson 2018
  2. // (C) Copyright Matt Borland 2020
  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)
  6. #ifndef BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_DETAIL_SINGLE_PASS_HPP
  7. #define BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_DETAIL_SINGLE_PASS_HPP
  8. #include <boost/math/tools/config.hpp>
  9. #include <boost/math/tools/assert.hpp>
  10. #include <tuple>
  11. #include <iterator>
  12. #include <type_traits>
  13. #include <cmath>
  14. #include <algorithm>
  15. #include <valarray>
  16. #include <stdexcept>
  17. #include <functional>
  18. #include <vector>
  19. #ifdef BOOST_MATH_HAS_THREADS
  20. #include <future>
  21. #include <thread>
  22. #endif
  23. namespace boost { namespace math { namespace statistics { namespace detail {
  24. template<typename ReturnType, typename ForwardIterator>
  25. ReturnType mean_sequential_impl(ForwardIterator first, ForwardIterator last)
  26. {
  27. const std::size_t elements {static_cast<std::size_t>(std::distance(first, last))};
  28. std::valarray<ReturnType> mu {0, 0, 0, 0};
  29. std::valarray<ReturnType> temp {0, 0, 0, 0};
  30. ReturnType i {1};
  31. const ForwardIterator end {std::next(first, elements - (elements % 4))};
  32. ForwardIterator it {first};
  33. while(it != end)
  34. {
  35. const ReturnType inv {ReturnType(1) / i};
  36. temp = {static_cast<ReturnType>(*it++), static_cast<ReturnType>(*it++), static_cast<ReturnType>(*it++), static_cast<ReturnType>(*it++)};
  37. temp -= mu;
  38. mu += (temp *= inv);
  39. i += 1;
  40. }
  41. const ReturnType num1 {ReturnType(elements - (elements % 4))/ReturnType(4)};
  42. const ReturnType num2 {num1 + ReturnType(elements % 4)};
  43. while(it != last)
  44. {
  45. mu[3] += (*it-mu[3])/i;
  46. i += 1;
  47. ++it;
  48. }
  49. return (num1 * std::valarray<ReturnType>(mu[std::slice(0,3,1)]).sum() + num2 * mu[3]) / ReturnType(elements);
  50. }
  51. // Higham, Accuracy and Stability, equation 1.6a and 1.6b:
  52. // Calculates Mean, M2, and variance
  53. template<typename ReturnType, typename ForwardIterator>
  54. ReturnType variance_sequential_impl(ForwardIterator first, ForwardIterator last)
  55. {
  56. using Real = typename std::tuple_element<0, ReturnType>::type;
  57. Real M = *first;
  58. Real Q = 0;
  59. Real k = 2;
  60. Real M2 = 0;
  61. std::size_t n = 1;
  62. for(auto it = std::next(first); it != last; ++it)
  63. {
  64. Real tmp = (*it - M) / k;
  65. Real delta_1 = *it - M;
  66. Q += k*(k-1)*tmp*tmp;
  67. M += tmp;
  68. k += 1;
  69. Real delta_2 = *it - M;
  70. M2 += delta_1 * delta_2;
  71. ++n;
  72. }
  73. return std::make_tuple(M, M2, Q/(k-1), Real(n));
  74. }
  75. // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
  76. template<typename ReturnType, typename ForwardIterator>
  77. ReturnType first_four_moments_sequential_impl(ForwardIterator first, ForwardIterator last)
  78. {
  79. using Real = typename std::tuple_element<0, ReturnType>::type;
  80. using Size = typename std::tuple_element<4, ReturnType>::type;
  81. Real M1 = *first;
  82. Real M2 = 0;
  83. Real M3 = 0;
  84. Real M4 = 0;
  85. Size n = 2;
  86. for (auto it = std::next(first); it != last; ++it)
  87. {
  88. Real delta21 = *it - M1;
  89. Real tmp = delta21/n;
  90. M4 = M4 + tmp*(tmp*tmp*delta21*((n-1)*(n*n-3*n+3)) + 6*tmp*M2 - 4*M3);
  91. M3 = M3 + tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  92. M2 = M2 + tmp*(n-1)*delta21;
  93. M1 = M1 + tmp;
  94. n += 1;
  95. }
  96. return std::make_tuple(M1, M2, M3, M4, n-1);
  97. }
  98. #ifdef BOOST_MATH_HAS_THREADS
  99. // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
  100. // EQN 3.1: https://www.osti.gov/servlets/purl/1426900
  101. template<typename ReturnType, typename ForwardIterator>
  102. ReturnType first_four_moments_parallel_impl(ForwardIterator first, ForwardIterator last)
  103. {
  104. using Real = typename std::tuple_element<0, ReturnType>::type;
  105. const auto elements = std::distance(first, last);
  106. const unsigned max_concurrency = std::thread::hardware_concurrency() == 0 ? 2u : std::thread::hardware_concurrency();
  107. unsigned num_threads = 2u;
  108. // Threading is faster for: 10 + 5.13e-3 N/j <= 5.13e-3N => N >= 10^4j/5.13(j-1).
  109. const auto parallel_lower_bound = 10e4*max_concurrency/(5.13*(max_concurrency-1));
  110. const auto parallel_upper_bound = 10e4*2/5.13; // j = 2
  111. // https://lemire.me/blog/2020/01/30/cost-of-a-thread-in-c-under-linux/
  112. if(elements < parallel_lower_bound)
  113. {
  114. return detail::first_four_moments_sequential_impl<ReturnType>(first, last);
  115. }
  116. else if(elements >= parallel_upper_bound)
  117. {
  118. num_threads = max_concurrency;
  119. }
  120. else
  121. {
  122. for(unsigned i = 3; i < max_concurrency; ++i)
  123. {
  124. if(parallel_lower_bound < 10e4*i/(5.13*(i-1)))
  125. {
  126. num_threads = i;
  127. break;
  128. }
  129. }
  130. }
  131. std::vector<std::future<ReturnType>> future_manager;
  132. const auto elements_per_thread = std::ceil(static_cast<double>(elements) / num_threads);
  133. auto it = first;
  134. for(std::size_t i {}; i < num_threads - 1; ++i)
  135. {
  136. future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, elements_per_thread]() -> ReturnType
  137. {
  138. return first_four_moments_sequential_impl<ReturnType>(it, std::next(it, elements_per_thread));
  139. }));
  140. it = std::next(it, elements_per_thread);
  141. }
  142. future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, last]() -> ReturnType
  143. {
  144. return first_four_moments_sequential_impl<ReturnType>(it, last);
  145. }));
  146. auto temp = future_manager[0].get();
  147. Real M1_a = std::get<0>(temp);
  148. Real M2_a = std::get<1>(temp);
  149. Real M3_a = std::get<2>(temp);
  150. Real M4_a = std::get<3>(temp);
  151. Real range_a = std::get<4>(temp);
  152. for(std::size_t i = 1; i < future_manager.size(); ++i)
  153. {
  154. temp = future_manager[i].get();
  155. Real M1_b = std::get<0>(temp);
  156. Real M2_b = std::get<1>(temp);
  157. Real M3_b = std::get<2>(temp);
  158. Real M4_b = std::get<3>(temp);
  159. Real range_b = std::get<4>(temp);
  160. const Real n_ab = range_a + range_b;
  161. const Real delta = M1_b - M1_a;
  162. M1_a = (range_a * M1_a + range_b * M1_b) / n_ab;
  163. M2_a = M2_a + M2_b + delta * delta * (range_a * range_b / n_ab);
  164. M3_a = M3_a + M3_b + (delta * delta * delta) * range_a * range_b * (range_a - range_b) / (n_ab * n_ab)
  165. + Real(3) * delta * (range_a * M2_b - range_b * M2_a) / n_ab;
  166. M4_a = M4_a + M4_b + (delta * delta * delta * delta) * range_a * range_b * (range_a * range_a - range_a * range_b + range_b * range_b) / (n_ab * n_ab * n_ab)
  167. + Real(6) * delta * delta * (range_a * range_a * M2_b + range_b * range_b * M2_a) / (n_ab * n_ab)
  168. + Real(4) * delta * (range_a * M3_b - range_b * M3_a) / n_ab;
  169. range_a = n_ab;
  170. }
  171. return std::make_tuple(M1_a, M2_a, M3_a, M4_a, elements);
  172. }
  173. #endif // BOOST_MATH_HAS_THREADS
  174. // Follows equation 1.5 of:
  175. // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
  176. template<typename ReturnType, typename ForwardIterator>
  177. ReturnType skewness_sequential_impl(ForwardIterator first, ForwardIterator last)
  178. {
  179. using std::sqrt;
  180. BOOST_MATH_ASSERT_MSG(first != last, "At least one sample is required to compute skewness.");
  181. ReturnType M1 = *first;
  182. ReturnType M2 = 0;
  183. ReturnType M3 = 0;
  184. ReturnType n = 2;
  185. for (auto it = std::next(first); it != last; ++it)
  186. {
  187. ReturnType delta21 = *it - M1;
  188. ReturnType tmp = delta21/n;
  189. M3 += tmp*((n-1)*(n-2)*delta21*tmp - 3*M2);
  190. M2 += tmp*(n-1)*delta21;
  191. M1 += tmp;
  192. n += 1;
  193. }
  194. ReturnType var = M2/(n-1);
  195. if (var == 0)
  196. {
  197. // The limit is technically undefined, but the interpretation here is clear:
  198. // A constant dataset has no skewness.
  199. return ReturnType(0);
  200. }
  201. ReturnType skew = M3/(M2*sqrt(var));
  202. return skew;
  203. }
  204. template<typename ReturnType, typename ForwardIterator>
  205. ReturnType gini_coefficient_sequential_impl(ForwardIterator first, ForwardIterator last)
  206. {
  207. ReturnType i = 1;
  208. ReturnType num = 0;
  209. ReturnType denom = 0;
  210. for(auto it = first; it != last; ++it)
  211. {
  212. num += *it*i;
  213. denom += *it;
  214. ++i;
  215. }
  216. // If the l1 norm is zero, all elements are zero, so every element is the same.
  217. if(denom == 0)
  218. {
  219. return ReturnType(0);
  220. }
  221. else
  222. {
  223. return ((2*num)/denom - i)/(i-1);
  224. }
  225. }
  226. template<typename ReturnType, typename ForwardIterator>
  227. ReturnType gini_range_fraction(ForwardIterator first, ForwardIterator last, std::size_t starting_index)
  228. {
  229. using Real = typename std::tuple_element<0, ReturnType>::type;
  230. std::size_t i = starting_index + 1;
  231. Real num = 0;
  232. Real denom = 0;
  233. for(auto it = first; it != last; ++it)
  234. {
  235. num += *it*i;
  236. denom += *it;
  237. ++i;
  238. }
  239. return std::make_tuple(num, denom, i);
  240. }
  241. #ifdef BOOST_MATH_HAS_THREADS
  242. template<typename ReturnType, typename ExecutionPolicy, typename ForwardIterator>
  243. ReturnType gini_coefficient_parallel_impl(ExecutionPolicy&&, ForwardIterator first, ForwardIterator last)
  244. {
  245. using range_tuple = std::tuple<ReturnType, ReturnType, std::size_t>;
  246. const auto elements = std::distance(first, last);
  247. const unsigned max_concurrency = std::thread::hardware_concurrency() == 0 ? 2u : std::thread::hardware_concurrency();
  248. unsigned num_threads = 2u;
  249. // Threading is faster for: 10 + 10.12e-3 N/j <= 10.12e-3N => N >= 10^4j/10.12(j-1).
  250. const auto parallel_lower_bound = 10e4*max_concurrency/(10.12*(max_concurrency-1));
  251. const auto parallel_upper_bound = 10e4*2/10.12; // j = 2
  252. // https://lemire.me/blog/2020/01/30/cost-of-a-thread-in-c-under-linux/
  253. if(elements < parallel_lower_bound)
  254. {
  255. return gini_coefficient_sequential_impl<ReturnType>(first, last);
  256. }
  257. else if(elements >= parallel_upper_bound)
  258. {
  259. num_threads = max_concurrency;
  260. }
  261. else
  262. {
  263. for(unsigned i = 3; i < max_concurrency; ++i)
  264. {
  265. if(parallel_lower_bound < 10e4*i/(10.12*(i-1)))
  266. {
  267. num_threads = i;
  268. break;
  269. }
  270. }
  271. }
  272. std::vector<std::future<range_tuple>> future_manager;
  273. const auto elements_per_thread = std::ceil(static_cast<double>(elements) / num_threads);
  274. auto it = first;
  275. for(std::size_t i {}; i < num_threads - 1; ++i)
  276. {
  277. future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, elements_per_thread, i]() -> range_tuple
  278. {
  279. return gini_range_fraction<range_tuple>(it, std::next(it, elements_per_thread), i*elements_per_thread);
  280. }));
  281. it = std::next(it, elements_per_thread);
  282. }
  283. future_manager.emplace_back(std::async(std::launch::async | std::launch::deferred, [it, last, num_threads, elements_per_thread]() -> range_tuple
  284. {
  285. return gini_range_fraction<range_tuple>(it, last, (num_threads - 1)*elements_per_thread);
  286. }));
  287. ReturnType num = 0;
  288. ReturnType denom = 0;
  289. for(std::size_t i = 0; i < future_manager.size(); ++i)
  290. {
  291. auto temp = future_manager[i].get();
  292. num += std::get<0>(temp);
  293. denom += std::get<1>(temp);
  294. }
  295. // If the l1 norm is zero, all elements are zero, so every element is the same.
  296. if(denom == 0)
  297. {
  298. return ReturnType(0);
  299. }
  300. else
  301. {
  302. return ((2*num)/denom - elements)/(elements-1);
  303. }
  304. }
  305. #endif // BOOST_MATH_HAS_THREADS
  306. template<typename ForwardIterator, typename OutputIterator>
  307. OutputIterator mode_impl(ForwardIterator first, ForwardIterator last, OutputIterator output)
  308. {
  309. using Z = typename std::iterator_traits<ForwardIterator>::value_type;
  310. using Size = typename std::iterator_traits<ForwardIterator>::difference_type;
  311. std::vector<Z> modes {};
  312. modes.reserve(16);
  313. Size max_counter {0};
  314. while(first != last)
  315. {
  316. Size current_count {0};
  317. ForwardIterator end_it {first};
  318. while(end_it != last && *end_it == *first)
  319. {
  320. ++current_count;
  321. ++end_it;
  322. }
  323. if(current_count > max_counter)
  324. {
  325. modes.resize(1);
  326. modes[0] = *first;
  327. max_counter = current_count;
  328. }
  329. else if(current_count == max_counter)
  330. {
  331. modes.emplace_back(*first);
  332. }
  333. first = end_it;
  334. }
  335. return std::move(modes.begin(), modes.end(), output);
  336. }
  337. }}}}
  338. #endif // BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_DETAIL_SINGLE_PASS_HPP