Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:43:43

0001 // Copyright 2015-2018 Hans Dembinski
0002 //
0003 // Distributed under the Boost Software License, version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt
0005 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP
0008 #define BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP
0009 
0010 #include <boost/core/nvp.hpp>
0011 #include <boost/histogram/detail/square.hpp>
0012 #include <boost/histogram/fwd.hpp> // for mean<>
0013 #include <type_traits>             // for std::integral_constant, std::common_type
0014 
0015 namespace boost {
0016 namespace histogram {
0017 namespace accumulators {
0018 
0019 /** Calculates mean and variance of sample.
0020 
0021   Uses Welfords's incremental algorithm to improve the numerical
0022   stability of mean and variance computation.
0023 */
0024 template <class ValueType>
0025 class mean {
0026 public:
0027   using value_type = ValueType;
0028   using const_reference = const value_type&;
0029 
0030   mean() = default;
0031 
0032   /// Allow implicit conversion from mean<T>.
0033   template <class T>
0034   mean(const mean<T>& o) noexcept
0035       : sum_{o.sum_}, mean_{o.mean_}, sum_of_deltas_squared_{o.sum_of_deltas_squared_} {}
0036 
0037   /// Initialize to external count, mean, and variance.
0038   mean(const_reference n, const_reference mean, const_reference variance) noexcept
0039       : sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (n - 1)) {}
0040 
0041   /// Insert sample x.
0042   void operator()(const_reference x) noexcept {
0043     sum_ += static_cast<value_type>(1);
0044     const auto delta = x - mean_;
0045     mean_ += delta / sum_;
0046     sum_of_deltas_squared_ += delta * (x - mean_);
0047   }
0048 
0049   /// Insert sample x with weight w.
0050   void operator()(const weight_type<value_type>& w, const_reference x) noexcept {
0051     sum_ += w.value;
0052     const auto delta = x - mean_;
0053     mean_ += w.value * delta / sum_;
0054     sum_of_deltas_squared_ += w.value * delta * (x - mean_);
0055   }
0056 
0057   /// Add another mean accumulator.
0058   mean& operator+=(const mean& rhs) noexcept {
0059     if (rhs.sum_ == 0) return *this;
0060 
0061     /*
0062       sum_of_deltas_squared
0063         = sum_i (x_i - mu)^2
0064         = sum_i (x_i - mu)^2 + sum_k (x_k - mu)^2
0065         = sum_i (x_i - mu1 + (mu1 - mu))^2 + sum_k (x_k - mu2 + (mu2 - mu))^2
0066 
0067       first part:
0068       sum_i (x_i - mu1 + (mu1 - mu))^2
0069         = sum_i (x_i - mu1)^2 + n1 (mu1 - mu))^2 + 2 (mu1 - mu) sum_i (x_i - mu1)
0070         = sum_i (x_i - mu1)^2 + n1 (mu1 - mu))^2
0071       since sum_i (x_i - mu1) = n1 mu1 - n1 mu1 = 0
0072 
0073       Putting it together:
0074       sum_of_deltas_squared
0075         = sum_of_deltas_squared_1 + n1 (mu1 - mu))^2
0076         + sum_of_deltas_squared_2 + n2 (mu2 - mu))^2
0077     */
0078 
0079     const auto n1 = sum_;
0080     const auto mu1 = mean_;
0081     const auto n2 = rhs.sum_;
0082     const auto mu2 = rhs.mean_;
0083 
0084     sum_ += rhs.sum_;
0085     mean_ = (n1 * mu1 + n2 * mu2) / sum_;
0086     sum_of_deltas_squared_ += rhs.sum_of_deltas_squared_;
0087     sum_of_deltas_squared_ += n1 * detail::square(mean_ - mu1);
0088     sum_of_deltas_squared_ += n2 * detail::square(mean_ - mu2);
0089 
0090     return *this;
0091   }
0092 
0093   /** Scale by value.
0094 
0095    This acts as if all samples were scaled by the value.
0096   */
0097   mean& operator*=(const_reference s) noexcept {
0098     mean_ *= s;
0099     sum_of_deltas_squared_ *= s * s;
0100     return *this;
0101   }
0102 
0103   bool operator==(const mean& rhs) const noexcept {
0104     return sum_ == rhs.sum_ && mean_ == rhs.mean_ &&
0105            sum_of_deltas_squared_ == rhs.sum_of_deltas_squared_;
0106   }
0107 
0108   bool operator!=(const mean& rhs) const noexcept { return !operator==(rhs); }
0109 
0110   /** Return how many samples were accumulated.
0111 
0112     count() should be used to check whether value() and variance() are defined,
0113     see documentation of value() and variance(). count() can be used to compute
0114     the variance of the mean by dividing variance() by count().
0115   */
0116   const_reference count() const noexcept { return sum_; }
0117 
0118   /** Return mean value of accumulated samples.
0119 
0120     The result is undefined, if `count() < 1`.
0121   */
0122   const_reference value() const noexcept { return mean_; }
0123 
0124   /** Return variance of accumulated samples.
0125 
0126     The result is undefined, if `count() < 2`.
0127   */
0128   value_type variance() const noexcept { return sum_of_deltas_squared_ / (sum_ - 1); }
0129 
0130   template <class Archive>
0131   void serialize(Archive& ar, unsigned version) {
0132     if (version == 0) {
0133       // read only
0134       std::size_t sum;
0135       ar& make_nvp("sum", sum);
0136       sum_ = static_cast<value_type>(sum);
0137     } else {
0138       ar& make_nvp("sum", sum_);
0139     }
0140     ar& make_nvp("mean", mean_);
0141     ar& make_nvp("sum_of_deltas_squared", sum_of_deltas_squared_);
0142   }
0143 
0144 private:
0145   value_type sum_{};
0146   value_type mean_{};
0147   value_type sum_of_deltas_squared_{};
0148 };
0149 
0150 } // namespace accumulators
0151 } // namespace histogram
0152 } // namespace boost
0153 
0154 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0155 
0156 namespace boost {
0157 namespace serialization {
0158 
0159 template <class T>
0160 struct version;
0161 
0162 // version 1 for boost::histogram::accumulators::mean<T>
0163 template <class T>
0164 struct version<boost::histogram::accumulators::mean<T>> : std::integral_constant<int, 1> {
0165 };
0166 
0167 } // namespace serialization
0168 } // namespace boost
0169 
0170 namespace std {
0171 template <class T, class U>
0172 /// Specialization for boost::histogram::accumulators::mean.
0173 struct common_type<boost::histogram::accumulators::mean<T>,
0174                    boost::histogram::accumulators::mean<U>> {
0175   using type = boost::histogram::accumulators::mean<common_type_t<T, U>>;
0176 };
0177 } // namespace std
0178 
0179 #endif
0180 
0181 #endif