Extended custom accumulator section in user guide and added count accumulator

This commit is contained in:
Hans Dembinski 2020-01-03 19:40:41 +01:00 committed by GitHub
parent 9fb9b5fe31
commit 9aef54cf70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 661 additions and 279 deletions

View File

@ -7,7 +7,7 @@
[section:Accumulator Accumulator]
An [*Accumulator] is a functor which consumes the argument to update some internal state. The state can be read with member functions or free functions. Must be [@https://en.cppreference.com/w/cpp/named_req/DefaultConstructible DefaultConstructible], [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible], and [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable].
An [*Accumulator] is a functor which consumes the argument to update some internal state. Must be [@https://en.cppreference.com/w/cpp/named_req/DefaultConstructible DefaultConstructible], [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible], and [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable].
[heading Required features]

View File

@ -37,7 +37,7 @@ The axis configuration is passed to `make_histogram` as a `std::vector<axis::var
Run-time configurable histograms are a slower than their compile-time brethren, but still pretty fast.
[note
If you know already at compile-time, that you will only use one axis type, `axis::regular<>` for example, but not how many per histogram, then you can also pass a `std::vector<axis::regular<>>` to `make_histogram`. You get almost the same speed as in the very first case, where both the axis configuration was fully known at compile-time.
If you know already at compile-time that you will only use one axis type, `axis::regular<>` for example, but not how many per histogram, then you can also pass a `std::vector<axis::regular<>>` to `make_histogram`. You get almost the same speed as in the very first case, where both the axis configuration was fully known at compile-time.
]
[note

View File

@ -402,16 +402,25 @@ The library provides several accumulators:
Users can easily write their own accumulators and plug them into the histogram, if they adhere to the [link histogram.concepts.Accumulator [*Accumulator] concept].
The first example shows how to make and use a histogram that uses one of the the builtin accumulators.
[import ../examples/guide_custom_accumulators_1.cpp]
[guide_custom_accumulators_1]
[import ../examples/guide_custom_accumulators_builtin.cpp]
[guide_custom_accumulators_builtin]
The second example shows how to use a simple custom accumulator.
[import ../examples/guide_custom_accumulators_2.cpp]
[guide_custom_accumulators_2]
The simplest way to make a custom accumulator is to inherit from one of the builtin accumulators. The following example shows how to add arbitrary metadata to each histogram cell by inheriting a custom accumulator from a builtin accumulator.
[import ../examples/guide_custom_accumulators_with_metadata.cpp]
[guide_custom_accumulators_with_metadata]
The third example shows how to make and use an accumulator that accepts multiple samples at once and an optional weight. The accumulator in the example accepts two samples and independently computes the mean for each one. This is more efficient than filling two separate profiles, because the cell lookup has to be done only once.
[import ../examples/guide_custom_accumulators_3.cpp]
[guide_custom_accumulators_3]
The next example shows how to making a custom accumulators completely from scratch. The library was designed to make this as easy as possible.
[import ../examples/guide_custom_accumulators_simple.cpp]
[guide_custom_accumulators_simple]
The next example shows a more complex custom accumulator that accepts two samples at once and an optional weight. It independently computes the mean for each sample. This is more efficient than filling two separate profiles, because the cell lookup has to be done only once.
[import ../examples/guide_custom_accumulators_advanced.cpp]
[guide_custom_accumulators_advanced]
And finally, just for fun, we use a histogram as the accumulator for another histogram.
[import ../examples/guide_custom_accumulators_ouroboros.cpp]
[guide_custom_accumulators_ouroboros]
Note that the axis size of the nested histogram differs from bin to bin. Creating a 2D histogram in this way is not as efficient as the normal way, but it allows one to create a histograms with such a non-rectangular layout of cells.
[endsect]

View File

@ -28,9 +28,11 @@ alias cxx14 :
[ run guide_axis_with_transform.cpp ]
[ run guide_axis_with_uoflow_off.cpp ]
[ run guide_custom_2d_axis.cpp ]
[ run guide_custom_accumulators_1.cpp ]
[ run guide_custom_accumulators_2.cpp ]
[ run guide_custom_accumulators_3.cpp ]
[ run guide_custom_accumulators_builtin.cpp ]
[ run guide_custom_accumulators_with_metadata.cpp ]
[ run guide_custom_accumulators_simple.cpp ]
[ run guide_custom_accumulators_advanced.cpp ]
[ run guide_custom_accumulators_ouroboros.cpp ]
[ run guide_custom_minimal_axis.cpp ]
[ run guide_custom_modified_axis.cpp ]
[ run guide_custom_storage.cpp ]

View File

@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//[ guide_custom_accumulators_3
//[ guide_custom_accumulators_advanced
#include <boost/format.hpp>
#include <boost/histogram.hpp>

View File

@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//[ guide_custom_accumulators_1
//[ guide_custom_accumulators_builtin
#include <boost/format.hpp>
#include <boost/histogram.hpp>

View File

@ -0,0 +1,51 @@
// 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)
//[ guide_custom_accumulators_ouroboros
#include <boost/histogram.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>
int main() {
using namespace boost::histogram;
// First we define the nested histogram type.
using axis_t = axis::category<int, axis::null_type, axis::option::growth_t>;
using base_t = histogram<std::tuple<axis_t>>;
// Now we make an accumulator out of it by using inheritance.
// We only need to implement operator(). A matching version of operator() is actually
// present in base_t, but it is templated and this is not allowed by the accumulator
// concept. Initialization could also happen here. We don't need to initialize anything
// here, because the default constructor of base_t is called automatically and
// sufficient for this example.
struct hist_t : base_t {
void operator()(const double x) { base_t::operator()(x); }
};
auto h = make_histogram_with(dense_storage<hist_t>(), axis::integer<>(1, 4));
auto x = {1, 1, 2, 2};
auto s = {1, 2, 3, 3}; // samples are filled into the nested histograms
h.fill(x, sample(s));
std::ostringstream os;
for (auto&& x : indexed(h)) {
os << x.bin() << " ";
for (auto&& y : indexed(*x)) { os << "(" << y.bin() << ": " << *y << ") "; }
os << "\n";
}
std::cout << os.str() << std::flush;
assert(os.str() == "1 (1: 1) (2: 1) \n"
"2 (3: 2) \n"
"3 \n");
}
//]

View File

@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//[ guide_custom_accumulators_2
//[ guide_custom_accumulators_simple
#include <boost/format.hpp>
#include <boost/histogram.hpp>
@ -15,14 +15,14 @@
int main() {
using namespace boost::histogram;
// Make a custom accumulator, which tracks the maximum of the samples.
// A custom accumulator which tracks the maximum of the samples.
// It must have a call operator that accepts the argument of the `sample` function.
struct maximum {
// return value is ignored, so we use void
void operator()(double x) {
if (x > value) value = x;
}
double value = 0; // value is initialized to zero
double value = 0; // value is public and initialized to zero
};
// Create 1D histogram that uses the custom accumulator.

View File

@ -0,0 +1,47 @@
// 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)
//[ guide_custom_accumulators_with_metadata
#include <boost/histogram.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>
int main() {
using namespace boost::histogram;
// derive custom accumulator from one of the builtins
struct accumulator_with_metadata : accumulators::count<> {
std::string meta; // custom meta data
// arbitrary additional data and interface could be added here
};
// make 1D histogram with custom accmulator
auto h = make_histogram_with(dense_storage<accumulator_with_metadata>(),
axis::integer<>(1, 4));
// fill some weighted entries
auto x = {1, 0, 2, 1};
h.fill(x);
// assigning meta data to two bins
h[0].meta = "Foo";
h[2].meta = "Bar";
std::ostringstream os;
for (auto&& x : indexed(h))
os << x.bin() << " value " << x->value() << " meta " << x->meta << "\n";
std::cout << os.str() << std::flush;
assert(os.str() == "1 value 2 meta Foo\n"
"2 value 1 meta \n"
"3 value 0 meta Bar\n");
}
//]

View File

@ -16,13 +16,11 @@
- [boost/histogram/axis/ostream.hpp][2]
- [boost/histogram/accumulators/ostream.hpp][3]
- [boost/histogram/serialization.hpp][4]
- [boost/histogram/display.hpp][5]
[1]: histogram/reference.html#header.boost.histogram.ostream_hpp
[2]: histogram/reference.html#header.boost.histogram.axis.ostream_hpp
[3]: histogram/reference.html#header.boost.histogram.accumulators.ostream_hpp
[4]: histogram/reference.html#header.boost.histogram.serialization_hpp
[5]: histogram/reference.html#header.boost.histogram.display_hpp
*/
#include <boost/histogram/accumulators.hpp>

View File

@ -17,6 +17,7 @@
[1]: histogram/reference.html#header.boost.histogram.accumulators.ostream_hpp
*/
#include <boost/histogram/accumulators/count.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/thread_safe.hpp>

View File

@ -0,0 +1,137 @@
// 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_ACCUMULATORS_NUMBER_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_NUMBER_HPP
#include <boost/core/nvp.hpp>
#include <boost/histogram/fwd.hpp> // for count<>
#include <type_traits> // for std::common_type
namespace boost {
namespace histogram {
namespace accumulators {
/**
Uses a C++ builtin arithmetic type to accumulate a count.
This wrapper class may be used as a base class by users who want to add custom metadata
to each bin of a histogram. Otherwise, arithmetic types should be used directly as
accumulators in storages for simplicity. In other words, prefer `dense_storage<double>`
over `dense_storage<count<double>>`, both are functionally equivalent.
When weighted data is accumulated and high precision is required, use
`accumulators::sum` instead. If a local variance estimate for the weight distribution
should be computed as well (generally needed for a detailed statistical analysis), use
`accumulators::weighted_sum`.
*/
template <class ValueType>
class count {
public:
using value_type = ValueType;
using const_reference = const value_type&;
count() = default;
/// Initialize count to value and allow implicit conversion
count(const_reference value) noexcept : value_(value) {}
/// Allow implicit conversion from other count
template <class T>
count(const count<T>& c) noexcept : count(c.value()) {}
/// Increment count by one
count& operator++() noexcept {
++value_;
return *this;
}
/// Increment count by value
count& operator+=(const_reference value) noexcept {
value_ += value;
return *this;
}
/// Add another count
count& operator+=(const count& s) noexcept {
value_ += s.value_;
return *this;
}
/// Scale by value
count& operator*=(const_reference value) noexcept {
value_ *= value;
return *this;
}
bool operator==(const count& rhs) const noexcept { return value_ == rhs.value_; }
bool operator!=(const count& rhs) const noexcept { return !operator==(rhs); }
/// Return count
const_reference value() const noexcept { return value_; }
// conversion to value_type must be explicit
explicit operator value_type() const noexcept { return value_; }
template <class Archive>
void serialize(Archive& ar, unsigned /* version */) {
ar& make_nvp("value", value_);
}
// begin: extra operators to make count behave like a regular number
count& operator*=(const count& rhs) noexcept {
value_ *= rhs.value_;
return *this;
}
count operator*(const count& rhs) const noexcept {
count x = *this;
x *= rhs;
return x;
}
count& operator/=(const count& rhs) noexcept {
value_ /= rhs.value_;
return *this;
}
count operator/(const count& rhs) const noexcept {
count x = *this;
x /= rhs;
return x;
}
bool operator<(const count& rhs) const noexcept { return value_ < rhs.value_; }
bool operator>(const count& rhs) const noexcept { return value_ > rhs.value_; }
bool operator<=(const count& rhs) const noexcept { return value_ <= rhs.value_; }
bool operator>=(const count& rhs) const noexcept { return value_ >= rhs.value_; }
// end: extra operators
private:
value_type value_{};
};
} // namespace accumulators
} // namespace histogram
} // namespace boost
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
struct common_type<boost::histogram::accumulators::count<T>,
boost::histogram::accumulators::count<U>> {
using type = boost::histogram::accumulators::count<common_type_t<T, U>>;
};
} // namespace std
#endif
#endif

View File

@ -52,32 +52,39 @@ std::basic_ostream<CharT, Traits>& handle_nonzero_width(
} // namespace detail
namespace accumulators {
template <class CharT, class Traits, class W>
template <class CharT, class Traits, class U>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const sum<W>& x) {
const count<U>& x) {
return os << x.value();
}
template <class CharT, class Traits, class U>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const sum<U>& x) {
if (os.width() == 0) return os << "sum(" << x.large() << " + " << x.small() << ")";
return detail::handle_nonzero_width(os, x);
}
template <class CharT, class Traits, class W>
template <class CharT, class Traits, class U>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const weighted_sum<W>& x) {
const weighted_sum<U>& x) {
if (os.width() == 0)
return os << "weighted_sum(" << x.value() << ", " << x.variance() << ")";
return detail::handle_nonzero_width(os, x);
}
template <class CharT, class Traits, class W>
template <class CharT, class Traits, class U>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const mean<W>& x) {
const mean<U>& x) {
if (os.width() == 0)
return os << "mean(" << x.count() << ", " << x.value() << ", " << x.variance() << ")";
return detail::handle_nonzero_width(os, x);
}
template <class CharT, class Traits, class W>
template <class CharT, class Traits, class U>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const weighted_mean<W>& x) {
const weighted_mean<U>& x) {
if (os.width() == 0)
return os << "weighted_mean(" << x.sum_of_weights() << ", " << x.value() << ", "
<< x.variance() << ")";

View File

@ -10,14 +10,14 @@
#include <boost/core/nvp.hpp>
#include <boost/histogram/fwd.hpp> // for sum<>
#include <cmath> // std::abs
#include <type_traits>
#include <type_traits> // std::is_floating_point, std::common_type
namespace boost {
namespace histogram {
namespace accumulators {
/**
Uses Neumaier algorithm to compute accurate sums.
Uses Neumaier algorithm to compute accurate sums of floats.
The algorithm is an improved Kahan algorithm
(https://en.wikipedia.org/wiki/Kahan_summation_algorithm). The algorithm uses memory for
@ -29,6 +29,9 @@ namespace accumulators {
*/
template <class ValueType>
class sum {
static_assert(std::is_floating_point<ValueType>::value,
"ValueType must be a floating point type");
public:
using value_type = ValueType;
using const_reference = const value_type&;

View File

@ -11,7 +11,6 @@
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/mp11/utility.hpp>
#include <numeric>
#include <type_traits>
namespace boost {

View File

@ -43,10 +43,15 @@ using empty_type = null_type;
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace transform {
struct id;
struct log;
struct sqrt;
struct pow;
} // namespace transform
template <class Value = double, class Transform = use_default,
@ -80,6 +85,10 @@ template <class T>
struct sample_type;
namespace accumulators {
template <class ValueType = double>
class count;
template <class ValueType = double>
class sum;
@ -99,6 +108,7 @@ template <class T>
struct is_thread_safe : std::false_type {};
template <class T>
struct is_thread_safe<thread_safe<T>> : std::true_type {};
} // namespace accumulators
struct unsafe_access;
@ -133,7 +143,8 @@ using weighted_profile_storage = dense_storage<accumulators::weighted_mean<>>;
template <class Axes, class Storage = default_storage>
class BOOST_ATTRIBUTE_NODISCARD histogram;
#endif
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
} // namespace histogram
} // namespace boost

View File

@ -41,7 +41,12 @@ boost_test(TYPE compile-fail SOURCES histogram_fail4.cpp)
set(BOOST_TEST_LINK_LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES accumulators_test.cpp)
boost_test(TYPE run SOURCES accumulators_count_test.cpp)
boost_test(TYPE run SOURCES accumulators_mean_test.cpp)
boost_test(TYPE run SOURCES accumulators_sum_test.cpp)
boost_test(TYPE run SOURCES accumulators_thread_safe_test.cpp)
boost_test(TYPE run SOURCES accumulators_weighted_mean_test.cpp)
boost_test(TYPE run SOURCES accumulators_weighted_sum_test.cpp)
boost_test(TYPE run SOURCES algorithm_project_test.cpp)
boost_test(TYPE run SOURCES algorithm_reduce_test.cpp)
boost_test(TYPE run SOURCES algorithm_sum_test.cpp)

View File

@ -38,10 +38,15 @@ alias odr :
;
alias cxx14 :
[ run accumulators_test.cpp : : :
[ run accumulators_count_test.cpp ]
[ run accumulators_mean_test.cpp ]
[ run accumulators_sum_test.cpp : : :
# make sure sum accumulator works even with -ffast-math and optimizations
<toolset>gcc:<cxxflags>"-O3 -ffast-math"
<toolset>clang:<cxxflags>"-O3 -ffast-math" ]
[ run accumulators_thread_safe_test.cpp ]
[ run accumulators_weighted_mean_test.cpp ]
[ run accumulators_weighted_sum_test.cpp ]
[ run algorithm_project_test.cpp ]
[ run algorithm_reduce_test.cpp ]
[ run algorithm_sum_test.cpp ]

View File

@ -0,0 +1,43 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/count.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
using c_t = accumulators::count<double>;
c_t c;
++c;
BOOST_TEST_EQ(c.value(), 1);
BOOST_TEST_EQ(str(c), "1"s);
BOOST_TEST_EQ(str(c, 2, false), " 1"s);
BOOST_TEST_EQ(str(c, 2, true), "1 "s);
c += 2;
BOOST_TEST_EQ(str(c), "3"s);
BOOST_TEST_EQ(c, 3);
BOOST_TEST_NE(c, 2);
c_t one(1), two(2), one_copy(1);
BOOST_TEST_LT(one, two);
BOOST_TEST_LE(one, two);
BOOST_TEST_LE(one, one_copy);
BOOST_TEST_GT(two, one);
BOOST_TEST_GE(two, one);
BOOST_TEST_GE(one, one_copy);
BOOST_TEST_EQ(c_t{} += c_t{}, c_t{});
return boost::report_errors();
}

View File

@ -0,0 +1,69 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/weight.hpp>
#include <sstream>
#include "is_close.hpp"
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
using m_t = accumulators::mean<double>;
m_t a;
BOOST_TEST_EQ(a.count(), 0);
BOOST_TEST_EQ(a, m_t{});
a(4);
a(7);
a(13);
a(16);
BOOST_TEST_EQ(a.count(), 4);
BOOST_TEST_EQ(a.value(), 10);
BOOST_TEST_EQ(a.variance(), 30);
BOOST_TEST_EQ(str(a), "mean(4, 10, 30)"s);
BOOST_TEST_EQ(str(a, 20, false), " mean(4, 10, 30)"s);
BOOST_TEST_EQ(str(a, 20, true), "mean(4, 10, 30) "s);
m_t b;
b(1e8 + 4);
b(1e8 + 7);
b(1e8 + 13);
b(1e8 + 16);
BOOST_TEST_EQ(b.count(), 4);
BOOST_TEST_EQ(b.value(), 1e8 + 10);
BOOST_TEST_EQ(b.variance(), 30);
auto c = a;
c += a; // same as feeding all samples twice
BOOST_TEST_EQ(c.count(), 8);
BOOST_TEST_EQ(c.value(), 10);
BOOST_TEST_IS_CLOSE(c.variance(), 25.714, 1e-3);
// also same as feeding all samples twice
m_t d;
d(weight(2), 4);
d(weight(2), 7);
d(weight(2), 13);
d(weight(2), 16);
BOOST_TEST_EQ(d, c);
BOOST_TEST_EQ(m_t() += m_t(), m_t());
BOOST_TEST_EQ(m_t(1, 2, 3) += m_t(), m_t(1, 2, 3));
BOOST_TEST_EQ(m_t() += m_t(1, 2, 3), m_t(1, 2, 3));
return boost::report_errors();
}

View File

@ -0,0 +1,70 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
double bad_sum = 0;
bad_sum += 1;
bad_sum += 1e100;
bad_sum += 1;
bad_sum += -1e100;
BOOST_TEST_EQ(bad_sum, 0); // instead of 2
using s_t = accumulators::sum<double>;
s_t sum;
++sum;
BOOST_TEST_EQ(sum, 1);
BOOST_TEST_EQ(sum.value(), 1);
BOOST_TEST_EQ(sum.large(), 1);
BOOST_TEST_EQ(sum.small(), 0);
BOOST_TEST_EQ(str(sum), "sum(1 + 0)"s);
BOOST_TEST_EQ(str(sum, 15, false), " sum(1 + 0)"s);
BOOST_TEST_EQ(str(sum, 15, true), "sum(1 + 0) "s);
sum += 1e100;
BOOST_TEST_EQ(sum, (s_t{1e100, 1}));
++sum;
BOOST_TEST_EQ(sum, (s_t{1e100, 2}));
sum += -1e100;
BOOST_TEST_EQ(sum, (s_t{0, 2}));
BOOST_TEST_EQ(sum, 2); // correct answer
BOOST_TEST_EQ(sum.value(), 2);
BOOST_TEST_EQ(sum.large(), 0);
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};
BOOST_TEST_LT(b, c);
BOOST_TEST_LE(b, c);
BOOST_TEST_LE(a, c);
BOOST_TEST_GT(a, b);
BOOST_TEST_GE(a, b);
BOOST_TEST_GE(a, c);
BOOST_TEST_EQ(s_t{} += s_t{}, s_t{});
return boost::report_errors();
}

View File

@ -1,243 +0,0 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/thread_safe.hpp>
#include <boost/histogram/accumulators/weighted_mean.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/weight.hpp>
#include <sstream>
#include "is_close.hpp"
#include "throw_exception.hpp"
using namespace boost::histogram;
using namespace std::literals;
template <class T>
auto str(const T& t, int w = 0, bool left = true) {
std::ostringstream os;
os.width(w);
if (left)
os << std::left;
else
os << std::right;
os << t;
return os.str();
}
int main() {
{
using w_t = accumulators::weighted_sum<double>;
w_t w;
BOOST_TEST_EQ(str(w), "weighted_sum(0, 0)"s);
BOOST_TEST_EQ(str(w, 20, false), " weighted_sum(0, 0)"s);
BOOST_TEST_EQ(str(w, 20, true), "weighted_sum(0, 0) "s);
BOOST_TEST_EQ(w, w_t{});
BOOST_TEST_EQ(w, w_t(0));
BOOST_TEST_NE(w, w_t(1));
w = w_t(1);
BOOST_TEST_EQ(w.value(), 1);
BOOST_TEST_EQ(w.variance(), 1);
BOOST_TEST_EQ(w, 1);
BOOST_TEST_NE(w, 2);
w += weight(2);
BOOST_TEST_EQ(w.value(), 3);
BOOST_TEST_EQ(w.variance(), 5);
BOOST_TEST_EQ(w, w_t(3, 5));
BOOST_TEST_NE(w, w_t(3));
w += w_t(1, 2);
BOOST_TEST_EQ(w.value(), 4);
BOOST_TEST_EQ(w.variance(), 7);
// consistency: a weighted counter increased by weight 1 multiplied
// by 2 must be the same as a weighted counter increased by weight 2
w_t u(0);
++u;
u *= 2;
BOOST_TEST_EQ(u, w_t(2, 4));
w_t v(0);
v += weight(2);
BOOST_TEST_EQ(u, v);
// conversion to RealType
w_t y(1, 2);
BOOST_TEST_NE(y, 1);
BOOST_TEST_EQ(static_cast<double>(y), 1);
BOOST_TEST_EQ(w_t() += w_t(), w_t());
}
{
using m_t = accumulators::mean<double>;
m_t a;
BOOST_TEST_EQ(a.count(), 0);
BOOST_TEST_EQ(a, m_t{});
a(4);
a(7);
a(13);
a(16);
BOOST_TEST_EQ(a.count(), 4);
BOOST_TEST_EQ(a.value(), 10);
BOOST_TEST_EQ(a.variance(), 30);
BOOST_TEST_EQ(str(a), "mean(4, 10, 30)"s);
BOOST_TEST_EQ(str(a, 20, false), " mean(4, 10, 30)"s);
BOOST_TEST_EQ(str(a, 20, true), "mean(4, 10, 30) "s);
m_t b;
b(1e8 + 4);
b(1e8 + 7);
b(1e8 + 13);
b(1e8 + 16);
BOOST_TEST_EQ(b.count(), 4);
BOOST_TEST_EQ(b.value(), 1e8 + 10);
BOOST_TEST_EQ(b.variance(), 30);
auto c = a;
c += a; // same as feeding all samples twice
BOOST_TEST_EQ(c.count(), 8);
BOOST_TEST_EQ(c.value(), 10);
BOOST_TEST_IS_CLOSE(c.variance(), 25.714, 1e-3);
// also same as feeding all samples twice
m_t d;
d(weight(2), 4);
d(weight(2), 7);
d(weight(2), 13);
d(weight(2), 16);
BOOST_TEST_EQ(d, c);
BOOST_TEST_EQ(m_t() += m_t(), m_t());
BOOST_TEST_EQ(m_t(1, 2, 3) += m_t(), m_t(1, 2, 3));
BOOST_TEST_EQ(m_t() += m_t(1, 2, 3), m_t(1, 2, 3));
}
{
using m_t = accumulators::weighted_mean<double>;
m_t a;
BOOST_TEST_EQ(a.sum_of_weights(), 0);
BOOST_TEST_EQ(a, m_t{});
a(weight(0.5), 1);
a(weight(1.0), 2);
a(weight(0.5), 3);
BOOST_TEST_EQ(a.sum_of_weights(), 2);
BOOST_TEST_EQ(a.sum_of_weights_squared(), 1.5);
BOOST_TEST_EQ(a.value(), 2);
BOOST_TEST_IS_CLOSE(a.variance(), 0.8, 1e-3);
BOOST_TEST_EQ(str(a), "weighted_mean(2, 2, 0.8)"s);
BOOST_TEST_EQ(str(a, 25, false), " weighted_mean(2, 2, 0.8)"s);
BOOST_TEST_EQ(str(a, 25, true), "weighted_mean(2, 2, 0.8) "s);
auto b = a;
b += a; // same as feeding all samples twice
BOOST_TEST_EQ(b.sum_of_weights(), 4);
BOOST_TEST_EQ(b.value(), 2);
BOOST_TEST_IS_CLOSE(b.variance(), 0.615, 1e-3);
BOOST_TEST_EQ(m_t() += m_t(), m_t());
BOOST_TEST_EQ(m_t(1, 2, 3, 4) += m_t(), m_t(1, 2, 3, 4));
BOOST_TEST_EQ(m_t() += m_t(1, 2, 3, 4), m_t(1, 2, 3, 4));
}
{
double bad_sum = 0;
bad_sum += 1;
bad_sum += 1e100;
bad_sum += 1;
bad_sum += -1e100;
BOOST_TEST_EQ(bad_sum, 0); // instead of 2
using s_t = accumulators::sum<double>;
s_t sum;
++sum;
BOOST_TEST_EQ(sum, 1);
BOOST_TEST_EQ(sum.value(), 1);
BOOST_TEST_EQ(sum.large(), 1);
BOOST_TEST_EQ(sum.small(), 0);
BOOST_TEST_EQ(str(sum), "sum(1 + 0)"s);
BOOST_TEST_EQ(str(sum, 15, false), " sum(1 + 0)"s);
BOOST_TEST_EQ(str(sum, 15, true), "sum(1 + 0) "s);
sum += 1e100;
BOOST_TEST_EQ(sum, (s_t{1e100, 1}));
++sum;
BOOST_TEST_EQ(sum, (s_t{1e100, 2}));
sum += -1e100;
BOOST_TEST_EQ(sum, (s_t{0, 2}));
BOOST_TEST_EQ(sum, 2); // correct answer
BOOST_TEST_EQ(sum.value(), 2);
BOOST_TEST_EQ(sum.large(), 0);
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};
BOOST_TEST_LT(b, c);
BOOST_TEST_LE(b, c);
BOOST_TEST_LE(a, c);
BOOST_TEST_GT(a, b);
BOOST_TEST_GE(a, b);
BOOST_TEST_GE(a, c);
BOOST_TEST_EQ(s_t{} += s_t{}, s_t{});
}
{
using s_t = accumulators::weighted_sum<accumulators::sum<double>>;
s_t w;
++w;
w += weight(1e100);
++w;
w += weight(-1e100);
BOOST_TEST_EQ(w.value(), 2);
BOOST_TEST_EQ(w.variance(), 2e200);
BOOST_TEST_EQ(s_t() += s_t(), s_t());
}
{
using ts_t = accumulators::thread_safe<int>;
ts_t i;
++i;
i += 1000;
BOOST_TEST_EQ(i, 1001);
BOOST_TEST_EQ(str(i), "1001"s);
BOOST_TEST_EQ(ts_t() += ts_t(), ts_t());
}
return boost::report_errors();
}

View File

@ -0,0 +1,30 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/thread_safe.hpp>
#include <sstream>
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
using ts_t = accumulators::thread_safe<int>;
ts_t i;
++i;
i += 1000;
BOOST_TEST_EQ(i, 1001);
BOOST_TEST_EQ(str(i), "1001"s);
BOOST_TEST_EQ(ts_t{} += ts_t{}, ts_t{});
return boost::report_errors();
}

View File

@ -0,0 +1,50 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/weighted_mean.hpp>
#include <boost/histogram/weight.hpp>
#include <sstream>
#include "is_close.hpp"
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
using m_t = accumulators::weighted_mean<double>;
m_t a;
BOOST_TEST_EQ(a.sum_of_weights(), 0);
BOOST_TEST_EQ(a, m_t{});
a(weight(0.5), 1);
a(weight(1.0), 2);
a(weight(0.5), 3);
BOOST_TEST_EQ(a.sum_of_weights(), 2);
BOOST_TEST_EQ(a.sum_of_weights_squared(), 1.5);
BOOST_TEST_EQ(a.value(), 2);
BOOST_TEST_IS_CLOSE(a.variance(), 0.8, 1e-3);
BOOST_TEST_EQ(str(a), "weighted_mean(2, 2, 0.8)"s);
BOOST_TEST_EQ(str(a, 25, false), " weighted_mean(2, 2, 0.8)"s);
BOOST_TEST_EQ(str(a, 25, true), "weighted_mean(2, 2, 0.8) "s);
auto b = a;
b += a; // same as feeding all samples twice
BOOST_TEST_EQ(b.sum_of_weights(), 4);
BOOST_TEST_EQ(b.value(), 2);
BOOST_TEST_IS_CLOSE(b.variance(), 0.615, 1e-3);
BOOST_TEST_EQ(m_t() += m_t(), m_t());
BOOST_TEST_EQ(m_t(1, 2, 3, 4) += m_t(), m_t(1, 2, 3, 4));
BOOST_TEST_EQ(m_t() += m_t(1, 2, 3, 4), m_t(1, 2, 3, 4));
return boost::report_errors();
}

View File

@ -0,0 +1,82 @@
// Copyright 2015-2018 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/weight.hpp>
#include <sstream>
#include "throw_exception.hpp"
#include "utility_str.hpp"
using namespace boost::histogram;
using namespace std::literals;
int main() {
{
using w_t = accumulators::weighted_sum<double>;
w_t w;
BOOST_TEST_EQ(str(w), "weighted_sum(0, 0)"s);
BOOST_TEST_EQ(str(w, 20, false), " weighted_sum(0, 0)"s);
BOOST_TEST_EQ(str(w, 20, true), "weighted_sum(0, 0) "s);
BOOST_TEST_EQ(w, w_t{});
BOOST_TEST_EQ(w, w_t(0));
BOOST_TEST_NE(w, w_t(1));
w = w_t(1);
BOOST_TEST_EQ(w.value(), 1);
BOOST_TEST_EQ(w.variance(), 1);
BOOST_TEST_EQ(w, 1);
BOOST_TEST_NE(w, 2);
w += weight(2);
BOOST_TEST_EQ(w.value(), 3);
BOOST_TEST_EQ(w.variance(), 5);
BOOST_TEST_EQ(w, w_t(3, 5));
BOOST_TEST_NE(w, w_t(3));
w += w_t(1, 2);
BOOST_TEST_EQ(w.value(), 4);
BOOST_TEST_EQ(w.variance(), 7);
// consistency: a weighted counter increased by weight 1 multiplied
// by 2 must be the same as a weighted counter increased by weight 2
w_t u(0);
++u;
u *= 2;
BOOST_TEST_EQ(u, w_t(2, 4));
w_t v(0);
v += weight(2);
BOOST_TEST_EQ(u, v);
// conversion to RealType
w_t y(1, 2);
BOOST_TEST_NE(y, 1);
BOOST_TEST_EQ(static_cast<double>(y), 1);
BOOST_TEST_EQ(w_t() += w_t(), w_t());
}
// sum nested in weighted_sum
{
using s_t = accumulators::weighted_sum<accumulators::sum<double>>;
s_t w;
++w;
w += weight(1e100);
++w;
w += weight(-1e100);
BOOST_TEST_EQ(w.value(), 2);
BOOST_TEST_EQ(w.variance(), 2e200);
BOOST_TEST_EQ(s_t() += s_t(), s_t());
}
return boost::report_errors();
}

View File

@ -8,12 +8,18 @@
#define BOOST_HISTOGRAM_TEST_UTILITY_STR_HPP
#include <sstream>
#include <string>
template <class T>
std::string str(const T& t) {
auto str(const T& t, int w = 0, bool left = true) {
std::ostringstream os;
auto saved = os.width();
os.width(w);
if (left)
os << std::left;
else
os << std::right;
os << t;
os.width(saved);
return os.str();
}