123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- #ifndef BOOST_HISTOGRAM_HISTOGRAM_HPP
- #define BOOST_HISTOGRAM_HISTOGRAM_HPP
- #include <boost/core/make_span.hpp>
- #include <boost/histogram/detail/accumulator_traits.hpp>
- #include <boost/histogram/detail/argument_traits.hpp>
- #include <boost/histogram/detail/axes.hpp>
- #include <boost/histogram/detail/common_type.hpp>
- #include <boost/histogram/detail/fill.hpp>
- #include <boost/histogram/detail/fill_n.hpp>
- #include <boost/histogram/detail/index_translator.hpp>
- #include <boost/histogram/detail/mutex_base.hpp>
- #include <boost/histogram/detail/nonmember_container_access.hpp>
- #include <boost/histogram/detail/static_if.hpp>
- #include <boost/histogram/fwd.hpp>
- #include <boost/histogram/indexed.hpp>
- #include <boost/histogram/multi_index.hpp>
- #include <boost/histogram/sample.hpp>
- #include <boost/histogram/storage_adaptor.hpp>
- #include <boost/histogram/unsafe_access.hpp>
- #include <boost/histogram/weight.hpp>
- #include <boost/mp11/integral.hpp>
- #include <boost/mp11/list.hpp>
- #include <boost/mp11/tuple.hpp>
- #include <boost/throw_exception.hpp>
- #include <mutex>
- #include <stdexcept>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include <vector>
- namespace boost {
- namespace histogram {
- template <class Axes, class Storage>
- class histogram : detail::mutex_base<Axes, Storage> {
- static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
- "Storage may not be a reference or const or volatile");
- static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
- public:
- using axes_type = Axes;
- using storage_type = Storage;
- using value_type = typename storage_type::value_type;
-
- using iterator = typename storage_type::iterator;
- using const_iterator = typename storage_type::const_iterator;
- using multi_index_type = multi_index<detail::relaxed_tuple_size_t<axes_type>::value>;
- private:
- using mutex_base = typename detail::mutex_base<axes_type, storage_type>;
- public:
- histogram() = default;
- template <class A, class S>
- explicit histogram(histogram<A, S>&& rhs)
- : storage_(std::move(unsafe_access::storage(rhs)))
- , offset_(unsafe_access::offset(rhs)) {
- detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
- detail::throw_if_axes_is_too_large(axes_);
- }
- template <class A, class S>
- explicit histogram(const histogram<A, S>& rhs)
- : storage_(unsafe_access::storage(rhs)), offset_(unsafe_access::offset(rhs)) {
- detail::axes_assign(axes_, unsafe_access::axes(rhs));
- detail::throw_if_axes_is_too_large(axes_);
- }
- template <class A, class S>
- histogram& operator=(histogram<A, S>&& rhs) {
- detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
- detail::throw_if_axes_is_too_large(axes_);
- storage_ = std::move(unsafe_access::storage(rhs));
- offset_ = unsafe_access::offset(rhs);
- return *this;
- }
- template <class A, class S>
- histogram& operator=(const histogram<A, S>& rhs) {
- detail::axes_assign(axes_, unsafe_access::axes(rhs));
- detail::throw_if_axes_is_too_large(axes_);
- storage_ = unsafe_access::storage(rhs);
- offset_ = unsafe_access::offset(rhs);
- return *this;
- }
- template <class A, class = detail::requires_axes<A>>
- histogram(A&& a, Storage s)
- : axes_(std::forward<A>(a))
- , storage_(std::move(s))
- , offset_(detail::offset(axes_)) {
- detail::throw_if_axes_is_too_large(axes_);
- storage_.reset(detail::bincount(axes_));
- }
- explicit histogram(Axes axes) : histogram(axes, storage_type()) {}
- template <class... As, class = detail::requires_axes<std::tuple<std::decay_t<As>...>>>
- explicit histogram(As&&... as)
- : histogram(std::tuple<std::decay_t<As>...>(std::forward<As>(as)...),
- storage_type()) {}
-
- constexpr unsigned rank() const noexcept { return detail::axes_rank(axes_); }
-
- std::size_t size() const noexcept { return storage_.size(); }
-
- void reset() { storage_.reset(size()); }
-
-
- template <unsigned N = 0>
- decltype(auto) axis(std::integral_constant<unsigned, N> = {}) const {
- assert(N < rank());
- return detail::axis_get<N>(axes_);
- }
-
-
- decltype(auto) axis(unsigned i) const {
- assert(i < rank());
- return detail::axis_get(axes_, i);
- }
- /// Apply unary functor/function to each axis.
- template <class Unary>
- auto for_each_axis(Unary&& unary) const {
- return detail::for_each_axis(axes_, std::forward<Unary>(unary));
- }
-
- template <class T0, class... Ts,
- class = std::enable_if_t<(detail::is_tuple<T0>::value == false ||
- sizeof...(Ts) > 0)>>
- iterator operator()(const T0& arg0, const Ts&... args) {
- return operator()(std::forward_as_tuple(arg0, args...));
- }
-
- template <class... Ts>
- iterator operator()(const std::tuple<Ts...>& args) {
- using arg_traits = detail::argument_traits<std::decay_t<Ts>...>;
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr bool weight_valid =
- arg_traits::wpos::value == -1 || acc_traits::weight_support;
- static_assert(weight_valid, "error: accumulator does not support weights");
- detail::sample_args_passed_vs_expected<typename arg_traits::sargs,
- typename acc_traits::args>();
- constexpr bool sample_valid =
- std::is_convertible<typename arg_traits::sargs, typename acc_traits::args>::value;
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- return detail::fill(mp11::mp_bool<(weight_valid && sample_valid)>{}, arg_traits{},
- offset_, storage_, axes_, args);
- }
-
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args) {
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr unsigned n_sample_args_expected =
- std::tuple_size<typename acc_traits::args>::value;
- static_assert(n_sample_args_expected == 0,
- "sample argument is missing but required by accumulator");
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- detail::fill_n(mp11::mp_bool<(n_sample_args_expected == 0)>{}, offset_, storage_,
- axes_, make_span(args));
- }
-
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const weight_type<T>& weights) {
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr bool weight_valid = acc_traits::weight_support;
- static_assert(weight_valid, "error: accumulator does not support weights");
- detail::sample_args_passed_vs_expected<std::tuple<>, typename acc_traits::args>();
- constexpr bool sample_valid =
- std::is_convertible<std::tuple<>, typename acc_traits::args>::value;
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_, storage_,
- axes_, make_span(args), weight(detail::to_ptr_size(weights.value)));
- }
-
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const weight_type<T>& weights, const Iterable& args) {
- fill(args, weights);
- }
-
- template <class Iterable, class... Ts, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const sample_type<std::tuple<Ts...>>& samples) {
- using acc_traits = detail::accumulator_traits<value_type>;
- using sample_args_passed =
- std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
- detail::sample_args_passed_vs_expected<sample_args_passed,
- typename acc_traits::args>();
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- mp11::tuple_apply(
- [&](const auto&... sargs) {
- constexpr bool sample_valid =
- std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
- detail::fill_n(mp11::mp_bool<(sample_valid)>{}, offset_, storage_, axes_,
- make_span(args), detail::to_ptr_size(sargs)...);
- },
- samples.value);
- }
-
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const sample_type<T>& samples, const Iterable& args) {
- fill(args, samples);
- }
- template <class Iterable, class T, class... Ts,
- class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const weight_type<T>& weights,
- const sample_type<std::tuple<Ts...>>& samples) {
- using acc_traits = detail::accumulator_traits<value_type>;
- using sample_args_passed =
- std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
- detail::sample_args_passed_vs_expected<sample_args_passed,
- typename acc_traits::args>();
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- mp11::tuple_apply(
- [&](const auto&... sargs) {
- constexpr bool weight_valid = acc_traits::weight_support;
- static_assert(weight_valid, "error: accumulator does not support weights");
- constexpr bool sample_valid =
- std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
- detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_,
- storage_, axes_, make_span(args),
- weight(detail::to_ptr_size(weights.value)),
- detail::to_ptr_size(sargs)...);
- },
- samples.value);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const sample_type<T>& samples, const weight_type<U>& weights,
- const Iterable& args) {
- fill(args, weights, samples);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const weight_type<T>& weights, const sample_type<U>& samples,
- const Iterable& args) {
- fill(args, weights, samples);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const sample_type<T>& samples,
- const weight_type<U>& weights) {
- fill(args, weights, samples);
- }
-
- template <class... Is>
- decltype(auto) at(axis::index_type i, Is... is) {
- return at(multi_index_type{i, static_cast<axis::index_type>(is)...});
- }
-
- template <class... Is>
- decltype(auto) at(axis::index_type i, Is... is) const {
- return at(multi_index_type{i, static_cast<axis::index_type>(is)...});
- }
-
- decltype(auto) at(const multi_index_type& is) {
- if (rank() != is.size())
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::linearize_indices(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- assert(idx < storage_.size());
- return storage_[idx];
- }
- /// Access cell value at integral indices stored in iterable (read-only).
- decltype(auto) at(const multi_index_type& is) const {
- if (rank() != is.size())
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::linearize_indices(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- assert(idx < storage_.size());
- return storage_[idx];
- }
- /// Access value at index (for rank = 1).
- decltype(auto) operator[](axis::index_type i) {
- const axis::index_type shift =
- axis::traits::options(axis()) & axis::option::underflow ? 1 : 0;
- return storage_[static_cast<std::size_t>(i + shift)];
- }
-
- decltype(auto) operator[](axis::index_type i) const {
- const axis::index_type shift =
- axis::traits::options(axis()) & axis::option::underflow ? 1 : 0;
- return storage_[static_cast<std::size_t>(i + shift)];
- }
-
- decltype(auto) operator[](const multi_index_type& is) {
- return storage_[detail::linearize_indices(axes_, is)];
- }
-
- decltype(auto) operator[](const multi_index_type& is) const {
- return storage_[detail::linearize_indices(axes_, is)];
- }
-
- template <class A, class S>
- bool operator==(const histogram<A, S>& rhs) const noexcept {
-
- return offset_ == unsafe_access::offset(rhs) &&
- detail::axes_equal(axes_, unsafe_access::axes(rhs)) &&
- storage_ == unsafe_access::storage(rhs);
- }
-
- template <class A, class S>
- bool operator!=(const histogram<A, S>& rhs) const noexcept {
- return !operator==(rhs);
- }
-
- template <class A, class S>
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- std::enable_if_t<
- detail::has_operator_radd<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- #endif
- operator+=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x += *rit++;
- return *this;
- }
-
- template <class S>
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- std::enable_if_t<detail::has_operator_radd<
- value_type, typename histogram<axes_type, S>::value_type>::value,
- histogram&>
- #endif
- operator+=(const histogram<axes_type, S>& rhs) {
- const auto& raxes = unsafe_access::axes(rhs);
- if (detail::axes_equal(axes_, unsafe_access::axes(rhs))) {
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x += *rit++;
- return *this;
- }
- if (rank() != detail::axes_rank(raxes))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes have different length"));
- auto h = histogram<axes_type, storage_type>(
- detail::axes_transform(axes_, raxes, detail::axis_merger{}),
- detail::make_default(storage_));
- const auto& axes = unsafe_access::axes(h);
- const auto tr1 = detail::make_index_translator(axes, axes_);
- for (auto&& x : indexed(*this, coverage::all)) h[tr1(x.indices())] += *x;
- const auto tr2 = detail::make_index_translator(axes, raxes);
- for (auto&& x : indexed(rhs, coverage::all)) h[tr2(x.indices())] += *x;
- *this = std::move(h);
- return *this;
- }
-
- template <class A, class S>
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- std::enable_if_t<
- detail::has_operator_rsub<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- #endif
- operator-=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x -= *rit++;
- return *this;
- }
-
- template <class A, class S>
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- std::enable_if_t<
- detail::has_operator_rmul<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- #endif
- operator*=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x *= *rit++;
- return *this;
- }
-
- template <class A, class S>
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- std::enable_if_t<
- detail::has_operator_rdiv<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- #endif
- operator/=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x /= *rit++;
- return *this;
- }
-
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- template <class V = value_type>
- std::enable_if_t<(detail::has_operator_rmul<V, double>::value), histogram&>
- #endif
- operator*=(const double x) {
-
-
- detail::static_if<detail::has_operator_rmul<storage_type, double>>(
- [x](auto& s) { s *= x; },
- [x](auto& s) {
- for (auto&& si : s) si *= x;
- },
- storage_);
- return *this;
- }
-
- #ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- histogram&
- #else
- template <class H = histogram>
- std::enable_if_t<(detail::has_operator_rmul<H, double>::value), histogram&>
- #endif
- operator/=(const double x) {
- return operator*=(1.0 / x);
- }
-
- iterator begin() noexcept { return storage_.begin(); }
-
- iterator end() noexcept { return storage_.end(); }
-
- const_iterator begin() const noexcept { return storage_.begin(); }
-
- const_iterator end() const noexcept { return storage_.end(); }
-
- const_iterator cbegin() const noexcept { return begin(); }
-
- const_iterator cend() const noexcept { return end(); }
- template <class Archive>
- void serialize(Archive& ar, unsigned ) {
- detail::axes_serialize(ar, axes_);
- ar& make_nvp("storage", storage_);
- if (Archive::is_loading::value) {
- offset_ = detail::offset(axes_);
- detail::throw_if_axes_is_too_large(axes_);
- }
- }
- private:
- axes_type axes_;
- storage_type storage_;
- std::size_t offset_ = 0;
- friend struct unsafe_access;
- };
- template <class A1, class S1, class A2, class S2>
- auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r += b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r *= b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r -= b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r /= b;
- }
- template <class A, class S>
- auto operator*(const histogram<A, S>& h, double x) {
- auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h);
- return r *= x;
- }
- template <class A, class S>
- auto operator*(double x, const histogram<A, S>& h) {
- return h * x;
- }
- template <class A, class S>
- auto operator/(const histogram<A, S>& h, double x) {
- return h * (1.0 / x);
- }
- #if __cpp_deduction_guides >= 201606
- template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>>
- histogram(Axes...) -> histogram<std::tuple<std::decay_t<Axes>...>>;
- template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>>
- histogram(std::tuple<Axes...>, S)
- -> histogram<std::tuple<Axes...>, std::conditional_t<detail::is_adaptible<S>::value,
- storage_adaptor<S>, S>>;
- template <class Iterable, class = detail::requires_iterable<Iterable>,
- class = detail::requires_any_axis<typename Iterable::value_type>>
- histogram(Iterable) -> histogram<std::vector<typename Iterable::value_type>>;
- template <class Iterable, class S, class = detail::requires_iterable<Iterable>,
- class = detail::requires_any_axis<typename Iterable::value_type>,
- class = detail::requires_storage_or_adaptible<S>>
- histogram(Iterable, S) -> histogram<
- std::vector<typename Iterable::value_type>,
- std::conditional_t<detail::is_adaptible<S>::value, storage_adaptor<S>, S>>;
- #endif
- }
- }
- #endif
|