123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- // 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)
- #ifndef BOOST_HISTOGRAM_AXIS_REGULAR_HPP
- #define BOOST_HISTOGRAM_AXIS_REGULAR_HPP
- #include <boost/core/nvp.hpp>
- #include <boost/histogram/axis/interval_view.hpp>
- #include <boost/histogram/axis/iterator.hpp>
- #include <boost/histogram/axis/metadata_base.hpp>
- #include <boost/histogram/axis/option.hpp>
- #include <boost/histogram/detail/convert_integer.hpp>
- #include <boost/histogram/detail/relaxed_equal.hpp>
- #include <boost/histogram/detail/replace_type.hpp>
- #include <boost/histogram/fwd.hpp>
- #include <boost/mp11/utility.hpp>
- #include <boost/throw_exception.hpp>
- #include <cassert>
- #include <cmath>
- #include <limits>
- #include <stdexcept>
- #include <string>
- #include <type_traits>
- #include <utility>
- namespace boost {
- namespace histogram {
- namespace detail {
- template <class T>
- using get_scale_type_helper = typename T::value_type;
- template <class T>
- using get_scale_type = mp11::mp_eval_or<T, detail::get_scale_type_helper, T>;
- struct one_unit {};
- template <class T>
- T operator*(T&& t, const one_unit&) {
- return std::forward<T>(t);
- }
- template <class T>
- T operator/(T&& t, const one_unit&) {
- return std::forward<T>(t);
- }
- template <class T>
- using get_unit_type_helper = typename T::unit_type;
- template <class T>
- using get_unit_type = mp11::mp_eval_or<one_unit, detail::get_unit_type_helper, T>;
- template <class T, class R = get_scale_type<T>>
- R get_scale(const T& t) {
- return t / get_unit_type<T>();
- }
- } // namespace detail
- namespace axis {
- namespace transform {
- /// Identity transform for equidistant bins.
- struct id {
- /// Pass-through.
- template <class T>
- static T forward(T&& x) noexcept {
- return std::forward<T>(x);
- }
- /// Pass-through.
- template <class T>
- static T inverse(T&& x) noexcept {
- return std::forward<T>(x);
- }
- template <class Archive>
- void serialize(Archive&, unsigned /* version */) {}
- };
- /// Log transform for equidistant bins in log-space.
- struct log {
- /// Returns log(x) of external value x.
- template <class T>
- static T forward(T x) {
- return std::log(x);
- }
- /// Returns exp(x) for internal value x.
- template <class T>
- static T inverse(T x) {
- return std::exp(x);
- }
- template <class Archive>
- void serialize(Archive&, unsigned /* version */) {}
- };
- /// Sqrt transform for equidistant bins in sqrt-space.
- struct sqrt {
- /// Returns sqrt(x) of external value x.
- template <class T>
- static T forward(T x) {
- return std::sqrt(x);
- }
- /// Returns x^2 of internal value x.
- template <class T>
- static T inverse(T x) {
- return x * x;
- }
- template <class Archive>
- void serialize(Archive&, unsigned /* version */) {}
- };
- /// Pow transform for equidistant bins in pow-space.
- struct pow {
- double power = 1; /**< power index */
- /// Make transform with index p.
- explicit pow(double p) : power(p) {}
- pow() = default;
- /// Returns pow(x, power) of external value x.
- template <class T>
- auto forward(T x) const {
- return std::pow(x, power);
- }
- /// Returns pow(x, 1/power) of external value x.
- template <class T>
- auto inverse(T x) const {
- return std::pow(x, 1.0 / power);
- }
- bool operator==(const pow& o) const noexcept { return power == o.power; }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("power", power);
- }
- };
- } // namespace transform
- #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- // Type envelope to mark value as step size
- template <class T>
- struct step_type {
- T value;
- };
- #endif
- /**
- Helper function to mark argument as step size.
- */
- template <class T>
- step_type<T> step(T t) {
- return step_type<T>{t};
- }
- /**
- Axis for equidistant intervals on the real line.
- The most common binning strategy. Very fast. Binning is a O(1) operation.
- @tparam Value input value type, must be floating point.
- @tparam Transform builtin or user-defined transform type.
- @tparam MetaData type to store meta data.
- @tparam Options see boost::histogram::axis::option (all values allowed).
- */
- template <class Value, class Transform, class MetaData, class Options>
- class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
- protected detail::replace_default<Transform, transform::id>,
- public metadata_base_t<MetaData> {
- // these must be private, so that they are not automatically inherited
- using value_type = Value;
- using transform_type = detail::replace_default<Transform, transform::id>;
- using metadata_base = metadata_base_t<MetaData>;
- using metadata_type = typename metadata_base::metadata_type;
- using options_type =
- detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
- static_assert(std::is_nothrow_move_constructible<transform_type>::value,
- "transform must be no-throw move constructible");
- static_assert(std::is_nothrow_move_assignable<transform_type>::value,
- "transform must be no-throw move assignable");
- using unit_type = detail::get_unit_type<value_type>;
- using internal_value_type = detail::get_scale_type<value_type>;
- static_assert(std::is_floating_point<internal_value_type>::value,
- "regular axis requires floating point type");
- static_assert(
- (!options_type::test(option::circular) && !options_type::test(option::growth)) ||
- (options_type::test(option::circular) ^ options_type::test(option::growth)),
- "circular and growth options are mutually exclusive");
- public:
- constexpr regular() = default;
- /** Construct n bins over real transformed range [start, stop).
- *
- * @param trans transform instance to use.
- * @param n number of bins.
- * @param start low edge of first bin.
- * @param stop high edge of last bin.
- * @param meta description of the axis (optional).
- */
- regular(transform_type trans, unsigned n, value_type start, value_type stop,
- metadata_type meta = {})
- : transform_type(std::move(trans))
- , metadata_base(std::move(meta))
- , size_(static_cast<index_type>(n))
- , min_(this->forward(detail::get_scale(start)))
- , delta_(this->forward(detail::get_scale(stop)) - min_) {
- if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
- if (!std::isfinite(min_) || !std::isfinite(delta_))
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("forward transform of start or stop invalid"));
- if (delta_ == 0)
- BOOST_THROW_EXCEPTION(std::invalid_argument("range of axis is zero"));
- }
- /** Construct n bins over real range [start, stop).
- *
- * @param n number of bins.
- * @param start low edge of first bin.
- * @param stop high edge of last bin.
- * @param meta description of the axis (optional).
- */
- regular(unsigned n, value_type start, value_type stop, metadata_type meta = {})
- : regular({}, n, start, stop, std::move(meta)) {}
- /** Construct bins with the given step size over real transformed range
- * [start, stop).
- *
- * @param trans transform instance to use.
- * @param step width of a single bin.
- * @param start low edge of first bin.
- * @param stop upper limit of high edge of last bin (see below).
- * @param meta description of the axis (optional).
- *
- * The axis computes the number of bins as n = abs(stop - start) / step,
- * rounded down. This means that stop is an upper limit to the actual value
- * (start + n * step).
- */
- template <class T>
- regular(transform_type trans, step_type<T> step, value_type start, value_type stop,
- metadata_type meta = {})
- : regular(trans, static_cast<index_type>(std::abs(stop - start) / step.value),
- start,
- start + static_cast<index_type>(std::abs(stop - start) / step.value) *
- step.value,
- std::move(meta)) {}
- /** Construct bins with the given step size over real range [start, stop).
- *
- * @param step width of a single bin.
- * @param start low edge of first bin.
- * @param stop upper limit of high edge of last bin (see below).
- * @param meta description of the axis (optional).
- *
- * The axis computes the number of bins as n = abs(stop - start) / step,
- * rounded down. This means that stop is an upper limit to the actual value
- * (start + n * step).
- */
- template <class T>
- regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {})
- : regular({}, step, start, stop, std::move(meta)) {}
- /// Constructor used by algorithm::reduce to shrink and rebin (not for users).
- regular(const regular& src, index_type begin, index_type end, unsigned merge)
- : regular(src.transform(), (end - begin) / merge, src.value(begin), src.value(end),
- src.metadata()) {
- assert((end - begin) % merge == 0);
- if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
- BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
- }
- /// Return instance of the transform type.
- const transform_type& transform() const noexcept { return *this; }
- /// Return index for value argument.
- index_type index(value_type x) const noexcept {
- // Runs in hot loop, please measure impact of changes
- auto z = (this->forward(x / unit_type{}) - min_) / delta_;
- if (options_type::test(option::circular)) {
- if (std::isfinite(z)) {
- z -= std::floor(z);
- return static_cast<index_type>(z * size());
- }
- } else {
- if (z < 1) {
- if (z >= 0)
- return static_cast<index_type>(z * size());
- else
- return -1;
- }
- }
- return size(); // also returned if x is NaN
- }
- /// Returns index and shift (if axis has grown) for the passed argument.
- std::pair<index_type, index_type> update(value_type x) noexcept {
- assert(options_type::test(option::growth));
- const auto z = (this->forward(x / unit_type{}) - min_) / delta_;
- if (z < 1) { // don't use i here!
- if (z >= 0) {
- const auto i = static_cast<axis::index_type>(z * size());
- return {i, 0};
- }
- if (z != -std::numeric_limits<internal_value_type>::infinity()) {
- const auto stop = min_ + delta_;
- const auto i = static_cast<axis::index_type>(std::floor(z * size()));
- min_ += i * (delta_ / size());
- delta_ = stop - min_;
- size_ -= i;
- return {0, -i};
- }
- // z is -infinity
- return {-1, 0};
- }
- // z either beyond range, infinite, or NaN
- if (z < std::numeric_limits<internal_value_type>::infinity()) {
- const auto i = static_cast<axis::index_type>(z * size());
- const auto n = i - size() + 1;
- delta_ /= size();
- delta_ *= size() + n;
- size_ += n;
- return {i, -n};
- }
- // z either infinite or NaN
- return {size(), 0};
- }
- /// Return value for fractional index argument.
- value_type value(real_index_type i) const noexcept {
- auto z = i / size();
- if (!options_type::test(option::circular) && z < 0.0)
- z = -std::numeric_limits<internal_value_type>::infinity() * delta_;
- else if (options_type::test(option::circular) || z <= 1.0)
- z = (1.0 - z) * min_ + z * (min_ + delta_);
- else {
- z = std::numeric_limits<internal_value_type>::infinity() * delta_;
- }
- return static_cast<value_type>(this->inverse(z) * unit_type());
- }
- /// Return bin for index argument.
- decltype(auto) bin(index_type idx) const noexcept {
- return interval_view<regular>(*this, idx);
- }
- /// Returns the number of bins, without over- or underflow.
- index_type size() const noexcept { return size_; }
- /// Returns the options.
- static constexpr unsigned options() noexcept { return options_type::value; }
- template <class V, class T, class M, class O>
- bool operator==(const regular<V, T, M, O>& o) const noexcept {
- return detail::relaxed_equal{}(transform(), o.transform()) && size() == o.size() &&
- min_ == o.min_ && delta_ == o.delta_ &&
- detail::relaxed_equal{}(this->metadata(), o.metadata());
- }
- template <class V, class T, class M, class O>
- bool operator!=(const regular<V, T, M, O>& o) const noexcept {
- return !operator==(o);
- }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("transform", static_cast<transform_type&>(*this));
- ar& make_nvp("size", size_);
- ar& make_nvp("meta", this->metadata());
- ar& make_nvp("min", min_);
- ar& make_nvp("delta", delta_);
- }
- private:
- index_type size_{0};
- internal_value_type min_{0}, delta_{1};
- template <class V, class T, class M, class O>
- friend class regular;
- };
- #if __cpp_deduction_guides >= 201606
- template <class T>
- regular(unsigned, T, T)
- ->regular<detail::convert_integer<T, double>, transform::id, null_type>;
- template <class T, class M>
- regular(unsigned, T, T, M)
- ->regular<detail::convert_integer<T, double>, transform::id,
- detail::replace_cstring<std::decay_t<M>>>;
- template <class Tr, class T, class = detail::requires_transform<Tr, T>>
- regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr, null_type>;
- template <class Tr, class T, class M>
- regular(Tr, unsigned, T, T, M)
- ->regular<detail::convert_integer<T, double>, Tr,
- detail::replace_cstring<std::decay_t<M>>>;
- #endif
- /// Regular axis with circular option already set.
- template <class Value = double, class MetaData = use_default, class Options = use_default>
- #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- using circular = regular<Value, transform::id, MetaData,
- decltype(detail::replace_default<Options, option::overflow_t>{} |
- option::circular)>;
- #else
- class circular;
- #endif
- } // namespace axis
- } // namespace histogram
- } // namespace boost
- #endif
|