fraction.hpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Copyright 2022 Jay Gohil, Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_ACCUMULATORS_FRACTION_HPP
  7. #define BOOST_HISTOGRAM_ACCUMULATORS_FRACTION_HPP
  8. #include <boost/core/nvp.hpp>
  9. #include <boost/histogram/fwd.hpp> // for fraction<>
  10. #include <boost/histogram/utility/wilson_interval.hpp>
  11. #include <type_traits> // for std::common_type
  12. namespace boost {
  13. namespace histogram {
  14. namespace accumulators {
  15. /**
  16. Accumulate boolean samples and compute the fraction of true samples.
  17. This accumulator should be used to calculate the efficiency or success fraction of a
  18. random process as a function of process parameters. It returns the fraction of
  19. successes, the variance of this fraction, and a two-sided confidence interval with 68.3
  20. % confidence level for this fraction.
  21. There is no unique way to compute an interval for a success fraction. This class returns
  22. the Wilson score interval, because it is widely recommended in the literature for
  23. general use. More interval computers can be found in `boost/histogram/utility`, which
  24. can be used to compute intervals for other confidence levels.
  25. */
  26. template <class ValueType>
  27. class fraction {
  28. public:
  29. using value_type = ValueType;
  30. using const_reference = const value_type&;
  31. using real_type = typename std::conditional<std::is_floating_point<value_type>::value,
  32. value_type, double>::type;
  33. using interval_type = typename utility::wilson_interval<real_type>::interval_type;
  34. fraction() noexcept = default;
  35. /// Initialize to external successes and failures.
  36. fraction(const_reference successes, const_reference failures) noexcept
  37. : succ_(successes), fail_(failures) {}
  38. /// Allow implicit conversion from fraction with a different value type.
  39. template <class T>
  40. fraction(const fraction<T>& e) noexcept
  41. : fraction{static_cast<value_type>(e.successes()),
  42. static_cast<value_type>(e.failures())} {}
  43. /// Insert boolean sample x.
  44. void operator()(bool x) noexcept {
  45. if (x)
  46. ++succ_;
  47. else
  48. ++fail_;
  49. }
  50. /// Add another accumulator.
  51. fraction& operator+=(const fraction& rhs) noexcept {
  52. succ_ += rhs.succ_;
  53. fail_ += rhs.fail_;
  54. return *this;
  55. }
  56. /// Return number of boolean samples that were true.
  57. const_reference successes() const noexcept { return succ_; }
  58. /// Return number of boolean samples that were false.
  59. const_reference failures() const noexcept { return fail_; }
  60. /// Return total number of boolean samples.
  61. value_type count() const noexcept { return succ_ + fail_; }
  62. /// Return success fraction of boolean samples.
  63. real_type value() const noexcept { return static_cast<real_type>(succ_) / count(); }
  64. /// Return variance of the success fraction.
  65. real_type variance() const noexcept {
  66. // We want to compute Var(p) for p = X / n with Var(X) = n p (1 - p)
  67. // For Var(X) see
  68. // https://en.wikipedia.org/wiki/Binomial_distribution#Expected_value_and_variance
  69. // Error propagation: Var(p) = p'(X)^2 Var(X) = p (1 - p) / n
  70. const real_type p = value();
  71. return p * (1 - p) / count();
  72. }
  73. /// Return standard interval with 68.3 % confidence level (Wilson score interval).
  74. interval_type confidence_interval() const noexcept {
  75. return utility::wilson_interval<real_type>()(static_cast<real_type>(successes()),
  76. static_cast<real_type>(failures()));
  77. }
  78. bool operator==(const fraction& rhs) const noexcept {
  79. return succ_ == rhs.succ_ && fail_ == rhs.fail_;
  80. }
  81. bool operator!=(const fraction& rhs) const noexcept { return !operator==(rhs); }
  82. template <class Archive>
  83. void serialize(Archive& ar, unsigned /* version */) {
  84. ar& make_nvp("successes", succ_);
  85. ar& make_nvp("failures", fail_);
  86. }
  87. private:
  88. value_type succ_{};
  89. value_type fail_{};
  90. };
  91. } // namespace accumulators
  92. } // namespace histogram
  93. } // namespace boost
  94. #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
  95. namespace std {
  96. template <class T, class U>
  97. /// Specialization for boost::histogram::accumulators::fraction.
  98. struct common_type<boost::histogram::accumulators::fraction<T>,
  99. boost::histogram::accumulators::fraction<U>> {
  100. using type = boost::histogram::accumulators::fraction<common_type_t<T, U>>;
  101. };
  102. } // namespace std
  103. #endif
  104. #endif