cumulative_stats.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /* Copyright 2024 Joaquin M Lopez Munoz.
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * See https://www.boost.org/libs/unordered for library home page.
  7. */
  8. #ifndef BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP
  9. #define BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP
  10. #include <array>
  11. #include <boost/config.hpp>
  12. #include <boost/mp11/tuple.hpp>
  13. #include <cmath>
  14. #include <cstddef>
  15. #if defined(BOOST_HAS_THREADS)
  16. #include <boost/unordered/detail/foa/rw_spinlock.hpp>
  17. #include <mutex>
  18. #endif
  19. namespace boost{
  20. namespace unordered{
  21. namespace detail{
  22. namespace foa{
  23. /* Cumulative one-pass calculation of the average, variance and deviation of
  24. * running sequences.
  25. */
  26. struct sequence_stats_data
  27. {
  28. double m=0.0;
  29. double m_prior=0.0;
  30. double s=0.0;
  31. };
  32. struct welfords_algorithm /* 0-based */
  33. {
  34. template<typename T>
  35. int operator()(T&& x,sequence_stats_data& d)const noexcept
  36. {
  37. static_assert(
  38. noexcept(static_cast<double>(x)),
  39. "Argument conversion to double must not throw.");
  40. d.m_prior=d.m;
  41. d.m+=(static_cast<double>(x)-d.m)/static_cast<double>(n);
  42. d.s+=(n!=1)*
  43. (static_cast<double>(x)-d.m_prior)*(static_cast<double>(x)-d.m);
  44. return 0; /* mp11::tuple_transform requires that return type not be void */
  45. }
  46. std::size_t n;
  47. };
  48. struct sequence_stats_summary
  49. {
  50. double average;
  51. double variance;
  52. double deviation;
  53. };
  54. /* Stats calculated jointly for N same-sized sequences to save the space
  55. * for count.
  56. */
  57. template<std::size_t N>
  58. class cumulative_stats
  59. {
  60. public:
  61. struct summary
  62. {
  63. std::size_t count;
  64. std::array<sequence_stats_summary,N> sequence_summary;
  65. };
  66. void reset()noexcept{*this=cumulative_stats();}
  67. template<typename... Ts>
  68. void add(Ts&&... xs)noexcept
  69. {
  70. static_assert(
  71. sizeof...(Ts)==N,"A sample must be provided for each sequence.");
  72. if(BOOST_UNLIKELY(++n==0)){ /* wraparound */
  73. reset();
  74. n=1;
  75. }
  76. mp11::tuple_transform(
  77. welfords_algorithm{n},
  78. std::forward_as_tuple(std::forward<Ts>(xs)...),
  79. data);
  80. }
  81. summary get_summary()const noexcept
  82. {
  83. summary res;
  84. res.count=n;
  85. for(std::size_t i=0;i<N;++i){
  86. double average=data[i].m,
  87. variance=n!=0?data[i].s/static_cast<double>(n):0.0, /* biased */
  88. deviation=std::sqrt(variance);
  89. res.sequence_summary[i]={average,variance,deviation};
  90. }
  91. return res;
  92. }
  93. private:
  94. std::size_t n=0;
  95. std::array<sequence_stats_data,N> data;
  96. };
  97. #if defined(BOOST_HAS_THREADS)
  98. template<std::size_t N>
  99. class concurrent_cumulative_stats:cumulative_stats<N>
  100. {
  101. using super=cumulative_stats<N>;
  102. using lock_guard=std::lock_guard<rw_spinlock>;
  103. public:
  104. using summary=typename super::summary;
  105. concurrent_cumulative_stats()noexcept:super{}{}
  106. concurrent_cumulative_stats(const concurrent_cumulative_stats& x)noexcept:
  107. concurrent_cumulative_stats{x,lock_guard{x.mut}}{}
  108. concurrent_cumulative_stats&
  109. operator=(const concurrent_cumulative_stats& x)noexcept
  110. {
  111. auto x1=x;
  112. lock_guard lck{mut};
  113. static_cast<super&>(*this)=x1;
  114. return *this;
  115. }
  116. void reset()noexcept
  117. {
  118. lock_guard lck{mut};
  119. super::reset();
  120. }
  121. template<typename... Ts>
  122. void add(Ts&&... xs)noexcept
  123. {
  124. lock_guard lck{mut};
  125. super::add(std::forward<Ts>(xs)...);
  126. }
  127. summary get_summary()const noexcept
  128. {
  129. lock_guard lck{mut};
  130. return super::get_summary();
  131. }
  132. private:
  133. concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{}
  134. mutable rw_spinlock mut;
  135. };
  136. #else
  137. template<std::size_t N>
  138. using concurrent_cumulative_stats=cumulative_stats<N>;
  139. #endif
  140. } /* namespace foa */
  141. } /* namespace detail */
  142. } /* namespace unordered */
  143. } /* namespace boost */
  144. #endif