variable.hpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2015-2018 Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
  7. #define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
  8. #include <algorithm>
  9. #include <boost/assert.hpp>
  10. #include <boost/histogram/axis/interval_view.hpp>
  11. #include <boost/histogram/axis/iterator.hpp>
  12. #include <boost/histogram/axis/option.hpp>
  13. #include <boost/histogram/detail/compressed_pair.hpp>
  14. #include <boost/histogram/detail/meta.hpp>
  15. #include <boost/histogram/fwd.hpp>
  16. #include <boost/throw_exception.hpp>
  17. #include <cmath>
  18. #include <limits>
  19. #include <memory>
  20. #include <stdexcept>
  21. #include <type_traits>
  22. #include <utility>
  23. #include <vector>
  24. namespace boost {
  25. namespace histogram {
  26. namespace axis {
  27. /**
  28. Axis for non-equidistant bins on the real line.
  29. Binning is a O(log(N)) operation. If speed matters and the problem domain
  30. allows it, prefer a regular axis, possibly with a transform.
  31. @tparam Value input value type, must be floating point.
  32. @tparam MetaData type to store meta data.
  33. @tparam Options see boost::histogram::axis::option (all values allowed).
  34. @tparam Allocator allocator to use for dynamic memory management.
  35. */
  36. template <class Value, class MetaData, class Options, class Allocator>
  37. class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>> {
  38. static_assert(std::is_floating_point<Value>::value,
  39. "variable axis requires floating point type");
  40. using value_type = Value;
  41. using metadata_type = detail::replace_default<MetaData, std::string>;
  42. using options_type =
  43. detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
  44. using allocator_type = Allocator;
  45. using vec_type = std::vector<Value, allocator_type>;
  46. public:
  47. explicit variable(allocator_type alloc = {}) : vec_meta_(std::move(alloc)) {}
  48. /** Construct from iterator range of bin edges.
  49. *
  50. * \param begin begin of edge sequence.
  51. * \param end end of edge sequence.
  52. * \param meta description of the axis.
  53. * \param alloc allocator instance to use.
  54. */
  55. template <class It, class = detail::requires_iterator<It>>
  56. variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
  57. : vec_meta_(vec_type(std::move(alloc)), std::move(meta)) {
  58. if (std::distance(begin, end) <= 1)
  59. BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
  60. auto& v = vec_meta_.first();
  61. v.reserve(std::distance(begin, end));
  62. v.emplace_back(*begin++);
  63. while (begin != end) {
  64. if (*begin <= v.back())
  65. BOOST_THROW_EXCEPTION(
  66. std::invalid_argument("input sequence must be strictly ascending"));
  67. v.emplace_back(*begin++);
  68. }
  69. }
  70. /** Construct variable axis from iterable range of bin edges.
  71. *
  72. * \param iterable iterable range of bin edges.
  73. * \param meta description of the axis.
  74. * \param alloc allocator instance to use.
  75. */
  76. template <class U, class = detail::requires_iterable<U>>
  77. variable(const U& iterable, metadata_type meta = {}, allocator_type alloc = {})
  78. : variable(std::begin(iterable), std::end(iterable), std::move(meta),
  79. std::move(alloc)) {}
  80. /** Construct variable axis from initializer list of bin edges.
  81. *
  82. * @param list `std::initializer_list` of bin edges.
  83. * @param meta description of the axis.
  84. * @param alloc allocator instance to use.
  85. */
  86. template <class U>
  87. variable(std::initializer_list<U> list, metadata_type meta = {},
  88. allocator_type alloc = {})
  89. : variable(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
  90. /// Constructor used by algorithm::reduce to shrink and rebin (not for users).
  91. variable(const variable& src, index_type begin, index_type end, unsigned merge)
  92. : vec_meta_(vec_type(src.get_allocator()), src.metadata()) {
  93. BOOST_ASSERT((end - begin) % merge == 0);
  94. if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
  95. BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
  96. auto& vec = vec_meta_.first();
  97. vec.reserve((end - begin) / merge);
  98. const auto beg = src.vec_meta_.first().begin();
  99. for (index_type i = begin; i <= end; i += merge) vec.emplace_back(*(beg + i));
  100. }
  101. /// Return index for value argument.
  102. index_type index(value_type x) const noexcept {
  103. const auto& v = vec_meta_.first();
  104. if (options_type::test(option::circular)) {
  105. const auto a = v[0];
  106. const auto b = v[size()];
  107. x -= std::floor((x - a) / (b - a)) * (b - a);
  108. }
  109. return static_cast<index_type>(std::upper_bound(v.begin(), v.end(), x) - v.begin() -
  110. 1);
  111. }
  112. auto update(value_type x) noexcept {
  113. const auto i = index(x);
  114. if (std::isfinite(x)) {
  115. auto& vec = vec_meta_.first();
  116. if (0 <= i) {
  117. if (i < size()) return std::make_pair(i, 0);
  118. const auto d = value(size()) - value(size() - 0.5);
  119. x = std::nextafter(x, std::numeric_limits<value_type>::max());
  120. x = std::max(x, vec.back() + d);
  121. vec.push_back(x);
  122. return std::make_pair(i, -1);
  123. }
  124. const auto d = value(0.5) - value(0);
  125. x = std::min(x, value(0) - d);
  126. vec.insert(vec.begin(), x);
  127. return std::make_pair(0, -i);
  128. }
  129. return std::make_pair(x < 0 ? -1 : size(), 0);
  130. }
  131. /// Return value for fractional index argument.
  132. value_type value(real_index_type i) const noexcept {
  133. const auto& v = vec_meta_.first();
  134. if (options_type::test(option::circular)) {
  135. auto shift = std::floor(i / size());
  136. i -= shift * size();
  137. double z;
  138. const auto k = static_cast<index_type>(std::modf(i, &z));
  139. const auto a = v[0];
  140. const auto b = v[size()];
  141. return (1.0 - z) * v[k] + z * v[k + 1] + shift * (b - a);
  142. }
  143. if (i < 0) return detail::lowest<value_type>();
  144. if (i == size()) return v.back();
  145. if (i > size()) return detail::highest<value_type>();
  146. const auto k = static_cast<index_type>(i); // precond: i >= 0
  147. const real_index_type z = i - k;
  148. return (1.0 - z) * v[k] + z * v[k + 1];
  149. }
  150. /// Return bin for index argument.
  151. auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }
  152. /// Returns the number of bins, without over- or underflow.
  153. index_type size() const noexcept {
  154. return static_cast<index_type>(vec_meta_.first().size()) - 1;
  155. }
  156. /// Returns the options.
  157. static constexpr unsigned options() noexcept { return options_type::value; }
  158. /// Returns reference to metadata.
  159. metadata_type& metadata() noexcept { return vec_meta_.second(); }
  160. /// Returns reference to const metadata.
  161. const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
  162. template <class V, class M, class O, class A>
  163. bool operator==(const variable<V, M, O, A>& o) const noexcept {
  164. const auto& a = vec_meta_.first();
  165. const auto& b = o.vec_meta_.first();
  166. return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
  167. detail::relaxed_equal(metadata(), o.metadata());
  168. }
  169. template <class V, class M, class O, class A>
  170. bool operator!=(const variable<V, M, O, A>& o) const noexcept {
  171. return !operator==(o);
  172. }
  173. /// Return allocator instance.
  174. auto get_allocator() const { return vec_meta_.first().get_allocator(); }
  175. template <class Archive>
  176. void serialize(Archive&, unsigned);
  177. private:
  178. detail::compressed_pair<vec_type, metadata_type> vec_meta_;
  179. template <class V, class M, class O, class A>
  180. friend class variable;
  181. };
  182. #if __cpp_deduction_guides >= 201606
  183. template <class U, class T = detail::convert_integer<U, double>>
  184. variable(std::initializer_list<U>)->variable<T>;
  185. template <class U, class T = detail::convert_integer<U, double>>
  186. variable(std::initializer_list<U>, const char*)->variable<T>;
  187. template <class U, class M, class T = detail::convert_integer<U, double>>
  188. variable(std::initializer_list<U>, M)->variable<T, M>;
  189. template <
  190. class Iterable,
  191. class T = detail::convert_integer<
  192. detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
  193. variable(Iterable)->variable<T>;
  194. template <
  195. class Iterable,
  196. class T = detail::convert_integer<
  197. detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
  198. variable(Iterable, const char*)->variable<T>;
  199. template <
  200. class Iterable, class M,
  201. class T = detail::convert_integer<
  202. detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
  203. variable(Iterable, M)->variable<T, M>;
  204. #endif
  205. } // namespace axis
  206. } // namespace histogram
  207. } // namespace boost
  208. #endif