mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-11 05:07:58 +00:00
Accumulator fixes
* Bug-fix of weighted_sum: variance was not calculated correctly when normal histogram was added to weighted histogram with weighted_sum storage * Several minor improvements to accumulator interfaces
This commit is contained in:
parent
9e5a4c4f24
commit
3dff0438ea
@ -43,7 +43,9 @@ install:
|
|||||||
- sh: ./bootstrap.sh; ./b2 headers; cd libs/histogram
|
- sh: ./bootstrap.sh; ./b2 headers; cd libs/histogram
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
|
# on windows
|
||||||
- cmd: ..\..\b2 %B2_OPTS% cxxstd=latest test//minimal test//serialization
|
- cmd: ..\..\b2 %B2_OPTS% cxxstd=latest test//minimal test//serialization
|
||||||
|
# on linux
|
||||||
- sh:
|
- sh:
|
||||||
B2="../../b2 ${B2_OPTS}";
|
B2="../../b2 ${B2_OPTS}";
|
||||||
$B2 toolset=gcc-7 cxxstd=14 exception-handling=off rtti=off test//minimal &&
|
$B2 toolset=gcc-7 cxxstd=14 exception-handling=off rtti=off test//minimal &&
|
||||||
|
@ -25,7 +25,7 @@ matrix:
|
|||||||
- cd build
|
- cd build
|
||||||
- cmake ..
|
- cmake ..
|
||||||
script:
|
script:
|
||||||
ctest -j2
|
ctest -j2 --output-on-failure
|
||||||
|
|
||||||
- name: osx and superproject cmake and b2 minimal
|
- name: osx and superproject cmake and b2 minimal
|
||||||
os: osx
|
os: osx
|
||||||
@ -50,7 +50,7 @@ matrix:
|
|||||||
script:
|
script:
|
||||||
mkdir build && cd build &&
|
mkdir build && cd build &&
|
||||||
cmake -DBOOST_ENABLE_CMAKE=1 -DBoost_VERBOSE=1 .. &&
|
cmake -DBOOST_ENABLE_CMAKE=1 -DBoost_VERBOSE=1 .. &&
|
||||||
ctest --output-on-failure -R boost_histogram &&
|
ctest -j2 --output-on-failure -R boost_histogram &&
|
||||||
cd .. &&
|
cd .. &&
|
||||||
./bootstrap.sh &&
|
./bootstrap.sh &&
|
||||||
./b2 headers &&
|
./b2 headers &&
|
||||||
|
@ -26,18 +26,21 @@ namespace accumulators {
|
|||||||
template <class RealType>
|
template <class RealType>
|
||||||
class mean {
|
class mean {
|
||||||
public:
|
public:
|
||||||
|
using value_type = RealType;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
|
||||||
mean() = default;
|
mean() = default;
|
||||||
mean(const RealType& n, const RealType& mean, const RealType& variance) noexcept
|
mean(const_reference n, const_reference mean, const_reference variance) noexcept
|
||||||
: sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (n - 1)) {}
|
: sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (n - 1)) {}
|
||||||
|
|
||||||
void operator()(const RealType& x) noexcept {
|
void operator()(const_reference x) noexcept {
|
||||||
sum_ += static_cast<RealType>(1);
|
sum_ += static_cast<value_type>(1);
|
||||||
const auto delta = x - mean_;
|
const auto delta = x - mean_;
|
||||||
mean_ += delta / sum_;
|
mean_ += delta / sum_;
|
||||||
sum_of_deltas_squared_ += delta * (x - mean_);
|
sum_of_deltas_squared_ += delta * (x - mean_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(const weight_type<RealType>& w, const RealType& x) noexcept {
|
void operator()(const weight_type<value_type>& w, const_reference x) noexcept {
|
||||||
sum_ += w.value;
|
sum_ += w.value;
|
||||||
const auto delta = x - mean_;
|
const auto delta = x - mean_;
|
||||||
mean_ += w.value * delta / sum_;
|
mean_ += w.value * delta / sum_;
|
||||||
@ -47,15 +50,15 @@ public:
|
|||||||
template <class T>
|
template <class T>
|
||||||
mean& operator+=(const mean<T>& rhs) noexcept {
|
mean& operator+=(const mean<T>& rhs) noexcept {
|
||||||
if (sum_ != 0 || rhs.sum_ != 0) {
|
if (sum_ != 0 || rhs.sum_ != 0) {
|
||||||
const auto tmp = mean_ * sum_ + static_cast<RealType>(rhs.mean_ * rhs.sum_);
|
const auto tmp = mean_ * sum_ + static_cast<value_type>(rhs.mean_ * rhs.sum_);
|
||||||
sum_ += rhs.sum_;
|
sum_ += rhs.sum_;
|
||||||
mean_ = tmp / sum_;
|
mean_ = tmp / sum_;
|
||||||
}
|
}
|
||||||
sum_of_deltas_squared_ += static_cast<RealType>(rhs.sum_of_deltas_squared_);
|
sum_of_deltas_squared_ += static_cast<value_type>(rhs.sum_of_deltas_squared_);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
mean& operator*=(const RealType& s) noexcept {
|
mean& operator*=(const_reference s) noexcept {
|
||||||
mean_ *= s;
|
mean_ *= s;
|
||||||
sum_of_deltas_squared_ *= s * s;
|
sum_of_deltas_squared_ *= s * s;
|
||||||
return *this;
|
return *this;
|
||||||
@ -72,9 +75,9 @@ public:
|
|||||||
return !operator==(rhs);
|
return !operator==(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const RealType& count() const noexcept { return sum_; }
|
const_reference count() const noexcept { return sum_; }
|
||||||
const RealType& value() const noexcept { return mean_; }
|
const_reference value() const noexcept { return mean_; }
|
||||||
RealType variance() const noexcept { return sum_of_deltas_squared_ / (sum_ - 1); }
|
value_type variance() const noexcept { return sum_of_deltas_squared_ / (sum_ - 1); }
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, unsigned version) {
|
void serialize(Archive& ar, unsigned version) {
|
||||||
@ -82,7 +85,7 @@ public:
|
|||||||
// read only
|
// read only
|
||||||
std::size_t sum;
|
std::size_t sum;
|
||||||
ar& make_nvp("sum", sum);
|
ar& make_nvp("sum", sum);
|
||||||
sum_ = static_cast<RealType>(sum);
|
sum_ = static_cast<value_type>(sum);
|
||||||
} else {
|
} else {
|
||||||
ar& make_nvp("sum", sum_);
|
ar& make_nvp("sum", sum_);
|
||||||
}
|
}
|
||||||
@ -91,7 +94,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealType sum_ = 0, mean_ = 0, sum_of_deltas_squared_ = 0;
|
value_type sum_{};
|
||||||
|
value_type mean_{};
|
||||||
|
value_type sum_of_deltas_squared_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace accumulators
|
} // namespace accumulators
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <boost/core/nvp.hpp>
|
#include <boost/core/nvp.hpp>
|
||||||
#include <boost/histogram/fwd.hpp> // for sum<>
|
#include <boost/histogram/fwd.hpp> // for sum<>
|
||||||
#include <cmath>
|
#include <cmath> // std::abs
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
@ -19,35 +19,44 @@ namespace accumulators {
|
|||||||
/**
|
/**
|
||||||
Uses Neumaier algorithm to compute accurate sums.
|
Uses Neumaier algorithm to compute accurate sums.
|
||||||
|
|
||||||
The algorithm uses memory for two floats and is three to
|
The algorithm is an improved Kahan algorithm
|
||||||
five times slower compared to a simple floating point
|
(https://en.wikipedia.org/wiki/Kahan_summation_algorithm). The algorithm uses memory for
|
||||||
number used to accumulate a sum, but the relative error
|
two numbers and is three to five times slower compared to using a single number to
|
||||||
of the sum is at the level of the machine precision,
|
accumulate a sum, but the relative error of the sum is at the level of the machine
|
||||||
independent of the number of samples.
|
precision, independent of the number of samples.
|
||||||
|
|
||||||
A. Neumaier, Zeitschrift fuer Angewandte Mathematik
|
A. Neumaier, Zeitschrift fuer Angewandte Mathematik und Mechanik 54 (1974) 39-51.
|
||||||
und Mechanik 54 (1974) 39-51.
|
|
||||||
*/
|
*/
|
||||||
template <class RealType>
|
template <class RealType>
|
||||||
class sum {
|
class sum {
|
||||||
public:
|
public:
|
||||||
|
using value_type = RealType;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
|
||||||
sum() = default;
|
sum() = default;
|
||||||
|
|
||||||
/// Initialize sum to value
|
/// Initialize sum to value and allow implicit conversion
|
||||||
explicit sum(const RealType& value) noexcept : large_(value) {}
|
sum(const_reference value) noexcept : sum(value, 0) {}
|
||||||
|
|
||||||
/// Set sum to value
|
template <class T>
|
||||||
sum& operator=(const RealType& value) noexcept {
|
sum(const sum<T>& s) noexcept : sum(s.large(), s.small()) {}
|
||||||
large_ = value;
|
|
||||||
small_ = 0;
|
/// Initialize sum explicitly with large and small parts
|
||||||
|
sum(const_reference large, const_reference small) noexcept
|
||||||
|
: large_(large), small_(small) {}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
sum& operator=(const sum<T>& s) noexcept {
|
||||||
|
large_ = s.large_;
|
||||||
|
small_ = s.small_;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increment sum by one
|
/// Increment sum by one
|
||||||
sum& operator++() { return operator+=(1); }
|
sum& operator++() noexcept { return operator+=(1); }
|
||||||
|
|
||||||
/// Increment sum by value
|
/// Increment sum by value
|
||||||
sum& operator+=(const RealType& value) {
|
sum& operator+=(const_reference value) noexcept {
|
||||||
// prevent compiler optimization from destroying the algorithm
|
// prevent compiler optimization from destroying the algorithm
|
||||||
// when -ffast-math is enabled
|
// when -ffast-math is enabled
|
||||||
volatile RealType l;
|
volatile RealType l;
|
||||||
@ -66,31 +75,34 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add another sum
|
||||||
|
sum& operator+=(const sum& s) noexcept {
|
||||||
|
operator+=(s.large_);
|
||||||
|
small_ += s.small_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/// Scale by value
|
/// Scale by value
|
||||||
sum& operator*=(const RealType& value) {
|
sum& operator*=(const_reference value) noexcept {
|
||||||
large_ *= value;
|
large_ *= value;
|
||||||
small_ *= value;
|
small_ *= value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
bool operator==(const sum& rhs) const noexcept {
|
||||||
bool operator==(const sum<T>& rhs) const noexcept {
|
return large_ + small_ == rhs.large_ + rhs.small_;
|
||||||
return large_ == rhs.large_ && small_ == rhs.small_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
bool operator!=(const sum& rhs) const noexcept { return !operator==(rhs); }
|
||||||
bool operator!=(const T& rhs) const noexcept {
|
|
||||||
return !operator==(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return large part of the sum.
|
/// Return large part of the sum.
|
||||||
const RealType& large() const { return large_; }
|
const_reference large() const noexcept { return large_; }
|
||||||
|
|
||||||
/// Return small part of the sum.
|
/// Return small part of the sum.
|
||||||
const RealType& small() const { return small_; }
|
const_reference small() const noexcept { return small_; }
|
||||||
|
|
||||||
// allow implicit conversion to RealType
|
// lossy conversion to value type must be explicit
|
||||||
operator RealType() const { return large_ + small_; }
|
explicit operator value_type() const noexcept { return large_ + small_; }
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, unsigned /* version */) {
|
void serialize(Archive& ar, unsigned /* version */) {
|
||||||
@ -98,9 +110,55 @@ public:
|
|||||||
ar& make_nvp("small", small_);
|
ar& make_nvp("small", small_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// begin: extra operators to make sum behave like a regular number
|
||||||
|
|
||||||
|
sum& operator*=(const sum& rhs) noexcept {
|
||||||
|
const auto scale = static_cast<value_type>(rhs);
|
||||||
|
large_ *= scale;
|
||||||
|
small_ *= scale;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum operator*(const sum& rhs) const noexcept {
|
||||||
|
sum s = *this;
|
||||||
|
s *= rhs;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum& operator/=(const sum& rhs) noexcept {
|
||||||
|
const auto scale = 1.0 / static_cast<value_type>(rhs);
|
||||||
|
large_ *= scale;
|
||||||
|
small_ *= scale;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum operator/(const sum& rhs) const noexcept {
|
||||||
|
sum s = *this;
|
||||||
|
s /= rhs;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const sum& rhs) const noexcept {
|
||||||
|
return operator value_type() < rhs.operator value_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const sum& rhs) const noexcept {
|
||||||
|
return operator value_type() > rhs.operator value_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const sum& rhs) const noexcept {
|
||||||
|
return operator value_type() <= rhs.operator value_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const sum& rhs) const noexcept {
|
||||||
|
return operator value_type() >= rhs.operator value_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
// end: extra operators
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealType large_ = RealType();
|
value_type large_{};
|
||||||
RealType small_ = RealType();
|
value_type small_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace accumulators
|
} // namespace accumulators
|
||||||
|
@ -26,18 +26,21 @@ namespace accumulators {
|
|||||||
template <class RealType>
|
template <class RealType>
|
||||||
class weighted_mean {
|
class weighted_mean {
|
||||||
public:
|
public:
|
||||||
|
using value_type = RealType;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
|
||||||
weighted_mean() = default;
|
weighted_mean() = default;
|
||||||
weighted_mean(const RealType& wsum, const RealType& wsum2, const RealType& mean,
|
weighted_mean(const_reference wsum, const_reference wsum2, const_reference mean,
|
||||||
const RealType& variance)
|
const_reference variance)
|
||||||
: sum_of_weights_(wsum)
|
: sum_of_weights_(wsum)
|
||||||
, sum_of_weights_squared_(wsum2)
|
, sum_of_weights_squared_(wsum2)
|
||||||
, weighted_mean_(mean)
|
, weighted_mean_(mean)
|
||||||
, sum_of_weighted_deltas_squared_(
|
, sum_of_weighted_deltas_squared_(
|
||||||
variance * (sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_)) {}
|
variance * (sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_)) {}
|
||||||
|
|
||||||
void operator()(const RealType& x) { operator()(weight(1), x); }
|
void operator()(const_reference x) { operator()(weight(1), x); }
|
||||||
|
|
||||||
void operator()(const weight_type<RealType>& w, const RealType& x) {
|
void operator()(const weight_type<value_type>& w, const_reference x) {
|
||||||
sum_of_weights_ += w.value;
|
sum_of_weights_ += w.value;
|
||||||
sum_of_weights_squared_ += w.value * w.value;
|
sum_of_weights_squared_ += w.value * w.value;
|
||||||
const auto delta = x - weighted_mean_;
|
const auto delta = x - weighted_mean_;
|
||||||
@ -49,9 +52,9 @@ public:
|
|||||||
weighted_mean& operator+=(const weighted_mean<T>& rhs) {
|
weighted_mean& operator+=(const weighted_mean<T>& rhs) {
|
||||||
if (sum_of_weights_ != 0 || rhs.sum_of_weights_ != 0) {
|
if (sum_of_weights_ != 0 || rhs.sum_of_weights_ != 0) {
|
||||||
const auto tmp = weighted_mean_ * sum_of_weights_ +
|
const auto tmp = weighted_mean_ * sum_of_weights_ +
|
||||||
static_cast<RealType>(rhs.weighted_mean_ * rhs.sum_of_weights_);
|
static_cast<value_type>(rhs.weighted_mean_ * rhs.sum_of_weights_);
|
||||||
sum_of_weights_ += static_cast<RealType>(rhs.sum_of_weights_);
|
sum_of_weights_ += static_cast<value_type>(rhs.sum_of_weights_);
|
||||||
sum_of_weights_squared_ += static_cast<RealType>(rhs.sum_of_weights_squared_);
|
sum_of_weights_squared_ += static_cast<value_type>(rhs.sum_of_weights_squared_);
|
||||||
weighted_mean_ = tmp / sum_of_weights_;
|
weighted_mean_ = tmp / sum_of_weights_;
|
||||||
}
|
}
|
||||||
sum_of_weighted_deltas_squared_ +=
|
sum_of_weighted_deltas_squared_ +=
|
||||||
@ -59,7 +62,7 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
weighted_mean& operator*=(const RealType& s) {
|
weighted_mean& operator*=(const_reference s) {
|
||||||
weighted_mean_ *= s;
|
weighted_mean_ *= s;
|
||||||
sum_of_weighted_deltas_squared_ *= s * s;
|
sum_of_weighted_deltas_squared_ *= s * s;
|
||||||
return *this;
|
return *this;
|
||||||
@ -78,12 +81,12 @@ public:
|
|||||||
return !operator==(rhs);
|
return !operator==(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const RealType& sum_of_weights() const noexcept { return sum_of_weights_; }
|
const_reference sum_of_weights() const noexcept { return sum_of_weights_; }
|
||||||
const RealType& sum_of_weights_squared() const noexcept {
|
const_reference sum_of_weights_squared() const noexcept {
|
||||||
return sum_of_weights_squared_;
|
return sum_of_weights_squared_;
|
||||||
}
|
}
|
||||||
const RealType& value() const noexcept { return weighted_mean_; }
|
const_reference value() const noexcept { return weighted_mean_; }
|
||||||
RealType variance() const {
|
value_type variance() const {
|
||||||
return sum_of_weighted_deltas_squared_ /
|
return sum_of_weighted_deltas_squared_ /
|
||||||
(sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_);
|
(sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_);
|
||||||
}
|
}
|
||||||
@ -97,8 +100,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealType sum_of_weights_ = RealType(), sum_of_weights_squared_ = RealType(),
|
value_type sum_of_weights_{};
|
||||||
weighted_mean_ = RealType(), sum_of_weighted_deltas_squared_ = RealType();
|
value_type sum_of_weights_squared_{};
|
||||||
|
value_type weighted_mean_{};
|
||||||
|
value_type sum_of_weighted_deltas_squared_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace accumulators
|
} // namespace accumulators
|
||||||
|
@ -20,54 +20,63 @@ template <class RealType>
|
|||||||
class weighted_sum {
|
class weighted_sum {
|
||||||
public:
|
public:
|
||||||
using value_type = RealType;
|
using value_type = RealType;
|
||||||
using const_reference = const RealType&;
|
using const_reference = const value_type&;
|
||||||
|
|
||||||
weighted_sum() = default;
|
weighted_sum() = default;
|
||||||
explicit weighted_sum(const RealType& value) noexcept
|
|
||||||
: sum_of_weights_(value), sum_of_weights_squared_(value) {}
|
/// Initialize sum to value and allow implicit conversion
|
||||||
weighted_sum(const RealType& value, const RealType& variance) noexcept
|
weighted_sum(const_reference value) noexcept : weighted_sum(value, value) {}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
weighted_sum(const weighted_sum<T>& s) noexcept
|
||||||
|
: weighted_sum(s.value(), s.variance()) {}
|
||||||
|
|
||||||
|
/// Initialize sum to value and variance
|
||||||
|
weighted_sum(const_reference value, const_reference variance) noexcept
|
||||||
: sum_of_weights_(value), sum_of_weights_squared_(variance) {}
|
: sum_of_weights_(value), sum_of_weights_squared_(variance) {}
|
||||||
|
|
||||||
/// Increment by one.
|
|
||||||
weighted_sum& operator++() { return operator+=(1); }
|
|
||||||
|
|
||||||
/// Increment by value.
|
|
||||||
template <class T>
|
template <class T>
|
||||||
weighted_sum& operator+=(const T& value) {
|
weighted_sum& operator=(const weighted_sum<T>& s) noexcept {
|
||||||
sum_of_weights_ += value;
|
sum_of_weights_ = s.sum_of_weights_;
|
||||||
sum_of_weights_squared_ += value * value;
|
sum_of_weights_squared_ = s.sum_of_weights_squared_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment by one.
|
||||||
|
weighted_sum& operator++() {
|
||||||
|
++sum_of_weights_;
|
||||||
|
++sum_of_weights_squared_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increment by weight.
|
||||||
|
template <class T>
|
||||||
|
weighted_sum& operator+=(const weight_type<T>& w) {
|
||||||
|
sum_of_weights_ += w.value;
|
||||||
|
sum_of_weights_squared_ += w.value * w.value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Added another weighted sum.
|
/// Added another weighted sum.
|
||||||
template <class T>
|
weighted_sum& operator+=(const weighted_sum& rhs) {
|
||||||
weighted_sum& operator+=(const weighted_sum<T>& rhs) {
|
sum_of_weights_ += rhs.sum_of_weights_;
|
||||||
sum_of_weights_ += static_cast<RealType>(rhs.sum_of_weights_);
|
sum_of_weights_squared_ += rhs.sum_of_weights_squared_;
|
||||||
sum_of_weights_squared_ += static_cast<RealType>(rhs.sum_of_weights_squared_);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scale by value.
|
/// Scale by value.
|
||||||
weighted_sum& operator*=(const RealType& x) {
|
weighted_sum& operator*=(const_reference x) {
|
||||||
sum_of_weights_ *= x;
|
sum_of_weights_ *= x;
|
||||||
sum_of_weights_squared_ *= x * x;
|
sum_of_weights_squared_ *= x * x;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const RealType& rhs) const noexcept {
|
bool operator==(const weighted_sum& rhs) const noexcept {
|
||||||
return sum_of_weights_ == rhs && sum_of_weights_squared_ == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
bool operator==(const weighted_sum<T>& rhs) const noexcept {
|
|
||||||
return sum_of_weights_ == rhs.sum_of_weights_ &&
|
return sum_of_weights_ == rhs.sum_of_weights_ &&
|
||||||
sum_of_weights_squared_ == rhs.sum_of_weights_squared_;
|
sum_of_weights_squared_ == rhs.sum_of_weights_squared_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
bool operator!=(const weighted_sum& rhs) const noexcept { return !operator==(rhs); }
|
||||||
bool operator!=(const T& rhs) const noexcept {
|
|
||||||
return !operator==(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return value of the sum.
|
/// Return value of the sum.
|
||||||
const_reference value() const noexcept { return sum_of_weights_; }
|
const_reference value() const noexcept { return sum_of_weights_; }
|
||||||
@ -85,8 +94,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
value_type sum_of_weights_ = value_type();
|
value_type sum_of_weights_{};
|
||||||
value_type sum_of_weights_squared_ = value_type();
|
value_type sum_of_weights_squared_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace accumulators
|
} // namespace accumulators
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <boost/histogram/detail/linearize.hpp>
|
#include <boost/histogram/detail/linearize.hpp>
|
||||||
#include <boost/histogram/detail/make_default.hpp>
|
#include <boost/histogram/detail/make_default.hpp>
|
||||||
#include <boost/histogram/detail/optional_index.hpp>
|
#include <boost/histogram/detail/optional_index.hpp>
|
||||||
|
#include <boost/histogram/detail/priority.hpp>
|
||||||
#include <boost/histogram/detail/tuple_slice.hpp>
|
#include <boost/histogram/detail/tuple_slice.hpp>
|
||||||
#include <boost/histogram/fwd.hpp>
|
#include <boost/histogram/fwd.hpp>
|
||||||
#include <boost/mp11/algorithm.hpp>
|
#include <boost/mp11/algorithm.hpp>
|
||||||
@ -134,50 +135,62 @@ struct storage_grower {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <class T, class... Us>
|
template <class T, class... Us>
|
||||||
void fill_storage_4(mp11::mp_false, T&& t, Us&&... args) noexcept {
|
auto fill_storage_element_impl(priority<2>, T&& t, const Us&... args) noexcept
|
||||||
t(std::forward<Us>(args)...);
|
-> decltype(t(args...), void()) {
|
||||||
}
|
t(args...);
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void fill_storage_4(mp11::mp_true, T&& t) noexcept {
|
|
||||||
++t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class U>
|
template <class T, class U>
|
||||||
void fill_storage_4(mp11::mp_true, T&& t, U&& w) noexcept {
|
auto fill_storage_element_impl(priority<1>, T&& t, const weight_type<U>& w) noexcept
|
||||||
|
-> decltype(t += w, void()) {
|
||||||
|
t += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for arithmetic types and accumulators that do not handle the weight
|
||||||
|
template <class T, class U>
|
||||||
|
auto fill_storage_element_impl(priority<0>, T&& t, const weight_type<U>& w) noexcept
|
||||||
|
-> decltype(t += w.value, void()) {
|
||||||
t += w.value;
|
t += w.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class... Us>
|
template <class T>
|
||||||
void fill_storage_3(T&& t, Us&&... args) noexcept {
|
auto fill_storage_element_impl(priority<1>, T&& t) noexcept -> decltype(++t, void()) {
|
||||||
fill_storage_4(has_operator_preincrement<std::decay_t<T>>{}, std::forward<T>(t),
|
++t;
|
||||||
std::forward<Us>(args)...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T, class... Us>
|
||||||
|
auto fill_storage_element(T&& t, const Us&... args) noexcept {
|
||||||
|
fill_storage_element_impl(priority<2>{}, std::forward<T>(t), args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||||
template <class IW, class IS, class T, class U>
|
template <class IW, class IS, class T, class U>
|
||||||
void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
|
void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
|
||||||
mp11::tuple_apply(
|
mp11::tuple_apply(
|
||||||
[&](auto&&... args) {
|
[&](const auto&... args) {
|
||||||
fill_storage_3(std::forward<T>(t), std::get<IW::value>(u), args...);
|
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u), args...);
|
||||||
},
|
},
|
||||||
std::get<IS::value>(u).value);
|
std::get<IS::value>(u).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||||
template <class IS, class T, class U>
|
template <class IS, class T, class U>
|
||||||
void fill_storage_2(mp11::mp_int<-1>, IS, T&& t, const U& u) noexcept {
|
void fill_storage_2(mp11::mp_int<-1>, IS, T&& t, const U& u) noexcept {
|
||||||
mp11::tuple_apply(
|
mp11::tuple_apply(
|
||||||
[&](const auto&... args) { fill_storage_3(std::forward<T>(t), args...); },
|
[&](const auto&... args) { fill_storage_element(std::forward<T>(t), args...); },
|
||||||
std::get<IS::value>(u).value);
|
std::get<IS::value>(u).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||||
template <class IW, class T, class U>
|
template <class IW, class T, class U>
|
||||||
void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
|
void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
|
||||||
fill_storage_3(std::forward<T>(t), std::get<IW::value>(u));
|
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||||
template <class T, class U>
|
template <class T, class U>
|
||||||
void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
|
void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
|
||||||
fill_storage_3(std::forward<T>(t));
|
fill_storage_element(std::forward<T>(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class IW, class IS, class Storage, class Index, class Args>
|
template <class IW, class IS, class Storage, class Index, class Args>
|
||||||
|
@ -159,7 +159,7 @@ template <class S, class Index, class... Ts>
|
|||||||
void fill_n_storage(S& s, const Index idx, Ts&&... p) noexcept {
|
void fill_n_storage(S& s, const Index idx, Ts&&... p) noexcept {
|
||||||
if (is_valid(idx)) {
|
if (is_valid(idx)) {
|
||||||
BOOST_ASSERT(idx < s.size());
|
BOOST_ASSERT(idx < s.size());
|
||||||
fill_storage_3(s[idx], *p.first...);
|
fill_storage_element(s[idx], *p.first...);
|
||||||
}
|
}
|
||||||
fold((p.second ? ++p.first : 0)...);
|
fold((p.second ? ++p.first : 0)...);
|
||||||
}
|
}
|
||||||
@ -168,8 +168,7 @@ template <class S, class Index, class T, class... Ts>
|
|||||||
void fill_n_storage(S& s, const Index idx, weight_type<T>&& w, Ts&&... ps) noexcept {
|
void fill_n_storage(S& s, const Index idx, weight_type<T>&& w, Ts&&... ps) noexcept {
|
||||||
if (is_valid(idx)) {
|
if (is_valid(idx)) {
|
||||||
BOOST_ASSERT(idx < s.size());
|
BOOST_ASSERT(idx < s.size());
|
||||||
fill_storage_3(s[idx], weight_type<decltype(*w.value.first)>{*w.value.first},
|
fill_storage_element(s[idx], weight(*w.value.first), *ps.first...);
|
||||||
*ps.first...);
|
|
||||||
}
|
}
|
||||||
if (w.value.second) ++w.value.first;
|
if (w.value.second) ++w.value.first;
|
||||||
fold((ps.second ? ++ps.first : 0)...);
|
fold((ps.second ? ++ps.first : 0)...);
|
||||||
|
28
include/boost/histogram/detail/priority.hpp
Normal file
28
include/boost/histogram/detail/priority.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2019 Hans Dembinski
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt
|
||||||
|
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#ifndef BOOST_HISTOGRAM_DETAIL_PRIORITY_HPP
|
||||||
|
#define BOOST_HISTOGRAM_DETAIL_PRIORITY_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace histogram {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// priority is used to priorise ambiguous overloads
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
struct priority : priority<(N - 1)> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct priority<0> {};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace histogram
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
|
#endif
|
@ -247,8 +247,8 @@ struct map_impl : T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class... Ts>
|
template <class... Ts>
|
||||||
decltype(auto) operator()(Ts&&... args) {
|
auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
|
||||||
return map->operator[](idx)(std::forward<Ts>(args)...);
|
return (*map)[idx](args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
map_impl* map;
|
map_impl* map;
|
||||||
|
@ -77,8 +77,8 @@ int main(int argc, char** argv) {
|
|||||||
const auto filename =
|
const auto filename =
|
||||||
join(argv[1], "accumulators_serialization_test_weighted_sum.xml");
|
join(argv[1], "accumulators_serialization_test_weighted_sum.xml");
|
||||||
accumulators::weighted_sum<> a;
|
accumulators::weighted_sum<> a;
|
||||||
a += 1;
|
a += weight(1);
|
||||||
a += 10;
|
a += weight(10);
|
||||||
print_xml(filename, a);
|
print_xml(filename, a);
|
||||||
|
|
||||||
accumulators::weighted_sum<> b;
|
accumulators::weighted_sum<> b;
|
||||||
|
@ -48,7 +48,7 @@ int main() {
|
|||||||
BOOST_TEST_EQ(w, 1);
|
BOOST_TEST_EQ(w, 1);
|
||||||
BOOST_TEST_NE(w, 2);
|
BOOST_TEST_NE(w, 2);
|
||||||
|
|
||||||
w += 2;
|
w += weight(2);
|
||||||
BOOST_TEST_EQ(w.value(), 3);
|
BOOST_TEST_EQ(w.value(), 3);
|
||||||
BOOST_TEST_EQ(w.variance(), 5);
|
BOOST_TEST_EQ(w.variance(), 5);
|
||||||
BOOST_TEST_EQ(w, w_t(3, 5));
|
BOOST_TEST_EQ(w, w_t(3, 5));
|
||||||
@ -66,7 +66,7 @@ int main() {
|
|||||||
BOOST_TEST_EQ(u, w_t(2, 4));
|
BOOST_TEST_EQ(u, w_t(2, 4));
|
||||||
|
|
||||||
w_t v(0);
|
w_t v(0);
|
||||||
v += 2;
|
v += weight(2);
|
||||||
BOOST_TEST_EQ(u, v);
|
BOOST_TEST_EQ(u, v);
|
||||||
|
|
||||||
// conversion to RealType
|
// conversion to RealType
|
||||||
@ -185,6 +185,19 @@ int main() {
|
|||||||
BOOST_TEST_EQ(sum.large(), 0);
|
BOOST_TEST_EQ(sum.large(), 0);
|
||||||
BOOST_TEST_EQ(sum.small(), 2);
|
BOOST_TEST_EQ(sum.small(), 2);
|
||||||
|
|
||||||
|
sum = s_t{1e100, 1};
|
||||||
|
sum += s_t{1e100, 1};
|
||||||
|
BOOST_TEST_EQ(sum, (s_t{2e100, 2}));
|
||||||
|
sum = s_t{1e100, 1};
|
||||||
|
sum += s_t{1, 0};
|
||||||
|
BOOST_TEST_EQ(sum, (s_t{1e100, 2}));
|
||||||
|
sum = s_t{1, 0};
|
||||||
|
sum += s_t{1e100, 1};
|
||||||
|
BOOST_TEST_EQ(sum, (s_t{1e100, 2}));
|
||||||
|
sum = s_t{0, 1};
|
||||||
|
sum += s_t{1, 0};
|
||||||
|
BOOST_TEST_EQ(sum, (s_t{1, 1}));
|
||||||
|
|
||||||
accumulators::sum<double> a(3), b(2), c(3);
|
accumulators::sum<double> a(3), b(2), c(3);
|
||||||
BOOST_TEST_LT(b, c);
|
BOOST_TEST_LT(b, c);
|
||||||
BOOST_TEST_LE(b, c);
|
BOOST_TEST_LE(b, c);
|
||||||
@ -201,9 +214,9 @@ int main() {
|
|||||||
s_t w;
|
s_t w;
|
||||||
|
|
||||||
++w;
|
++w;
|
||||||
w += 1e100;
|
w += weight(1e100);
|
||||||
++w;
|
++w;
|
||||||
w += -1e100;
|
w += weight(-1e100);
|
||||||
|
|
||||||
BOOST_TEST_EQ(w.value(), 2);
|
BOOST_TEST_EQ(w.value(), 2);
|
||||||
BOOST_TEST_EQ(w.variance(), 2e200);
|
BOOST_TEST_EQ(w.variance(), 2e200);
|
||||||
|
@ -153,6 +153,16 @@ void run_tests() {
|
|||||||
BOOST_TEST_EQ(d.at(0).variance(), 1);
|
BOOST_TEST_EQ(d.at(0).variance(), 1);
|
||||||
BOOST_TEST_EQ(d.at(1).value(), 3);
|
BOOST_TEST_EQ(d.at(1).value(), 3);
|
||||||
BOOST_TEST_EQ(d.at(1).variance(), 9);
|
BOOST_TEST_EQ(d.at(1).variance(), 9);
|
||||||
|
|
||||||
|
// add unweighted histogram
|
||||||
|
auto e = make_s(Tag(), std::vector<int>(), ia);
|
||||||
|
std::fill(e.begin(), e.end(), 2);
|
||||||
|
|
||||||
|
d += e;
|
||||||
|
BOOST_TEST_EQ(d.at(0).value(), 3);
|
||||||
|
BOOST_TEST_EQ(d.at(0).variance(), 3);
|
||||||
|
BOOST_TEST_EQ(d.at(1).value(), 5);
|
||||||
|
BOOST_TEST_EQ(d.at(1).variance(), 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bad operations
|
// bad operations
|
||||||
|
@ -410,7 +410,7 @@ void run_tests() {
|
|||||||
BOOST_TEST_EQ(a[4], 3);
|
BOOST_TEST_EQ(a[4], 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// histogram_reset
|
// histogram reset
|
||||||
{
|
{
|
||||||
auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2));
|
auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2));
|
||||||
h(0);
|
h(0);
|
||||||
@ -424,10 +424,9 @@ void run_tests() {
|
|||||||
BOOST_TEST_EQ(algorithm::sum(h), 0);
|
BOOST_TEST_EQ(algorithm::sum(h), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// using containers or input and output
|
// using containers for input and output
|
||||||
{
|
{
|
||||||
auto h = make_s(Tag(), weight_storage(), axis::integer<>(0, 2),
|
auto h = make(Tag(), axis::integer<>(0, 2), axis::integer<double>(2, 4));
|
||||||
axis::integer<double>(2, 4));
|
|
||||||
// tuple in
|
// tuple in
|
||||||
h(std::make_tuple(0, 2.0));
|
h(std::make_tuple(0, 2.0));
|
||||||
h(std::make_tuple(1, 3.0));
|
h(std::make_tuple(1, 3.0));
|
||||||
@ -436,9 +435,9 @@ void run_tests() {
|
|||||||
auto i11 = std::make_tuple(1, 1);
|
auto i11 = std::make_tuple(1, 1);
|
||||||
|
|
||||||
// tuple out
|
// tuple out
|
||||||
BOOST_TEST_EQ(h.at(i00).value(), 1);
|
BOOST_TEST_EQ(h.at(i00), 1);
|
||||||
BOOST_TEST_EQ(h[i00].value(), 1);
|
BOOST_TEST_EQ(h[i00], 1);
|
||||||
BOOST_TEST_EQ(h[i11].value(), 1);
|
BOOST_TEST_EQ(h[i11], 1);
|
||||||
|
|
||||||
// iterable out
|
// iterable out
|
||||||
int j11[] = {1, 1};
|
int j11[] = {1, 1};
|
||||||
@ -455,10 +454,8 @@ void run_tests() {
|
|||||||
h(std::make_tuple(weight(2), 0, 2.0));
|
h(std::make_tuple(weight(2), 0, 2.0));
|
||||||
h(std::make_tuple(1, 3.0, weight(2)));
|
h(std::make_tuple(1, 3.0, weight(2)));
|
||||||
|
|
||||||
BOOST_TEST_EQ(h.at(i00).value(), 3);
|
BOOST_TEST_EQ(h.at(i00), 3);
|
||||||
BOOST_TEST_EQ(h[i00].value(), 3);
|
BOOST_TEST_EQ(h[i00], 3);
|
||||||
BOOST_TEST_EQ(h.at(i11).variance(), 5);
|
|
||||||
BOOST_TEST_EQ(h[i11].variance(), 5);
|
|
||||||
|
|
||||||
// test special case of 1-dimensional histogram, which should unpack
|
// test special case of 1-dimensional histogram, which should unpack
|
||||||
// 1-dimensional tuple normally, but forward larger tuples to the axis
|
// 1-dimensional tuple normally, but forward larger tuples to the axis
|
||||||
|
@ -218,7 +218,7 @@ int main() {
|
|||||||
++a[0];
|
++a[0];
|
||||||
a[0] += 1;
|
a[0] += 1;
|
||||||
a[0] += 2;
|
a[0] += 2;
|
||||||
a[0] += accumulators::weighted_sum<double>(1, 0);
|
a[0] += accumulators::weighted_sum<double>(1, 2);
|
||||||
BOOST_TEST_EQ(a[0].value(), 5);
|
BOOST_TEST_EQ(a[0].value(), 5);
|
||||||
BOOST_TEST_EQ(a[0].variance(), 6);
|
BOOST_TEST_EQ(a[0].variance(), 6);
|
||||||
a[0] *= 2;
|
a[0] *= 2;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user