axes.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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_DETAIL_AXES_HPP
  7. #define BOOST_HISTOGRAM_DETAIL_AXES_HPP
  8. #include <array>
  9. #include <boost/core/nvp.hpp>
  10. #include <boost/histogram/axis/traits.hpp>
  11. #include <boost/histogram/axis/variant.hpp>
  12. #include <boost/histogram/detail/make_default.hpp>
  13. #include <boost/histogram/detail/nonmember_container_access.hpp>
  14. #include <boost/histogram/detail/optional_index.hpp>
  15. #include <boost/histogram/detail/priority.hpp>
  16. #include <boost/histogram/detail/relaxed_tuple_size.hpp>
  17. #include <boost/histogram/detail/static_if.hpp>
  18. #include <boost/histogram/detail/sub_array.hpp>
  19. #include <boost/histogram/detail/try_cast.hpp>
  20. #include <boost/histogram/fwd.hpp>
  21. #include <boost/mp11/algorithm.hpp>
  22. #include <boost/mp11/integer_sequence.hpp>
  23. #include <boost/mp11/list.hpp>
  24. #include <boost/mp11/tuple.hpp>
  25. #include <boost/mp11/utility.hpp>
  26. #include <boost/throw_exception.hpp>
  27. #include <cassert>
  28. #include <initializer_list>
  29. #include <iterator>
  30. #include <stdexcept>
  31. #include <string>
  32. #include <tuple>
  33. #include <type_traits>
  34. #include <vector>
  35. namespace boost {
  36. namespace histogram {
  37. namespace detail {
  38. template <class T, class Unary>
  39. void for_each_axis_impl(dynamic_size, T& t, Unary& p) {
  40. for (auto& a : t) axis::visit(p, a);
  41. }
  42. template <class N, class T, class Unary>
  43. void for_each_axis_impl(N, T& t, Unary& p) {
  44. mp11::tuple_for_each(t, p);
  45. }
  46. // also matches const T and const Unary
  47. template <class T, class Unary>
  48. void for_each_axis(T&& t, Unary&& p) {
  49. for_each_axis_impl(relaxed_tuple_size(t), t, p);
  50. }
  51. // merge if a and b are discrete and growing
  52. struct axis_merger {
  53. template <class T, class U>
  54. T operator()(const T& a, const U& u) {
  55. const T* bp = ptr_cast<T>(&u);
  56. if (!bp) BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
  57. using O = axis::traits::get_options<T>;
  58. constexpr bool discrete_and_growing =
  59. axis::traits::is_continuous<T>::value == false && O::test(axis::option::growth);
  60. return impl(mp11::mp_bool<discrete_and_growing>{}, a, *bp);
  61. }
  62. template <class T>
  63. T impl(std::false_type, const T& a, const T& b) {
  64. if (!relaxed_equal{}(a, b))
  65. BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
  66. return a;
  67. }
  68. template <class T>
  69. T impl(std::true_type, const T& a, const T& b) {
  70. if (relaxed_equal{}(axis::traits::metadata(a), axis::traits::metadata(b))) {
  71. auto r = a;
  72. if (axis::traits::is_ordered<T>::value) {
  73. r.update(b.value(0));
  74. r.update(b.value(b.size() - 1));
  75. } else
  76. for (auto&& v : b) r.update(v);
  77. return r;
  78. }
  79. return impl(std::false_type{}, a, b);
  80. }
  81. };
  82. // create empty dynamic axis which can store any axes types from the argument
  83. template <class T>
  84. auto make_empty_dynamic_axes(const T& axes) {
  85. return make_default(axes);
  86. }
  87. template <class... Ts>
  88. auto make_empty_dynamic_axes(const std::tuple<Ts...>&) {
  89. using namespace ::boost::mp11;
  90. using L = mp_unique<axis::variant<Ts...>>;
  91. // return std::vector<axis::variant<Axis0, Axis1, ...>> or std::vector<Axis0>
  92. return std::vector<mp_if_c<(mp_size<L>::value == 1), mp_first<L>, L>>{};
  93. }
  94. template <class T, class Functor, std::size_t... Is>
  95. auto axes_transform_impl(const T& t, Functor&& f, mp11::index_sequence<Is...>) {
  96. return std::make_tuple(f(Is, std::get<Is>(t))...);
  97. }
  98. // warning: sequential order of functor execution is platform-dependent!
  99. template <class... Ts, class Functor>
  100. auto axes_transform(const std::tuple<Ts...>& old_axes, Functor&& f) {
  101. return axes_transform_impl(old_axes, std::forward<Functor>(f),
  102. mp11::make_index_sequence<sizeof...(Ts)>{});
  103. }
  104. // changing axes type is not supported
  105. template <class T, class Functor>
  106. T axes_transform(const T& old_axes, Functor&& f) {
  107. T axes = make_default(old_axes);
  108. axes.reserve(old_axes.size());
  109. for_each_axis(old_axes, [&](const auto& a) { axes.emplace_back(f(axes.size(), a)); });
  110. return axes;
  111. }
  112. template <class... Ts, class Binary, std::size_t... Is>
  113. std::tuple<Ts...> axes_transform_impl(const std::tuple<Ts...>& lhs,
  114. const std::tuple<Ts...>& rhs, Binary&& bin,
  115. mp11::index_sequence<Is...>) {
  116. return std::make_tuple(bin(std::get<Is>(lhs), std::get<Is>(rhs))...);
  117. }
  118. template <class... Ts, class Binary>
  119. std::tuple<Ts...> axes_transform(const std::tuple<Ts...>& lhs,
  120. const std::tuple<Ts...>& rhs, Binary&& bin) {
  121. return axes_transform_impl(lhs, rhs, bin, mp11::make_index_sequence<sizeof...(Ts)>{});
  122. }
  123. template <class T, class Binary>
  124. T axes_transform(const T& lhs, const T& rhs, Binary&& bin) {
  125. T ax = make_default(lhs);
  126. ax.reserve(lhs.size());
  127. using std::begin;
  128. auto ir = begin(rhs);
  129. for (auto&& li : lhs) {
  130. axis::visit(
  131. [&](const auto& li) {
  132. axis::visit([&](const auto& ri) { ax.emplace_back(bin(li, ri)); }, *ir);
  133. },
  134. li);
  135. ++ir;
  136. }
  137. return ax;
  138. }
  139. template <class T>
  140. unsigned axes_rank(const T& axes) {
  141. using std::begin;
  142. using std::end;
  143. return static_cast<unsigned>(std::distance(begin(axes), end(axes)));
  144. }
  145. template <class... Ts>
  146. constexpr unsigned axes_rank(const std::tuple<Ts...>&) {
  147. return static_cast<unsigned>(sizeof...(Ts));
  148. }
  149. template <class T>
  150. void throw_if_axes_is_too_large(const T& axes) {
  151. if (axes_rank(axes) > BOOST_HISTOGRAM_DETAIL_AXES_LIMIT)
  152. BOOST_THROW_EXCEPTION(
  153. std::invalid_argument("length of axis vector exceeds internal buffers, "
  154. "recompile with "
  155. "-DBOOST_HISTOGRAM_DETAIL_AXES_LIMIT=<new max size> "
  156. "to increase internal buffers"));
  157. }
  158. // tuple is never too large because internal buffers adapt to size of tuple
  159. template <class... Ts>
  160. void throw_if_axes_is_too_large(const std::tuple<Ts...>&) {}
  161. template <unsigned N, class... Ts>
  162. decltype(auto) axis_get(std::tuple<Ts...>& axes) {
  163. return std::get<N>(axes);
  164. }
  165. template <unsigned N, class... Ts>
  166. decltype(auto) axis_get(const std::tuple<Ts...>& axes) {
  167. return std::get<N>(axes);
  168. }
  169. template <unsigned N, class T>
  170. decltype(auto) axis_get(T& axes) {
  171. return axes[N];
  172. }
  173. template <unsigned N, class T>
  174. decltype(auto) axis_get(const T& axes) {
  175. return axes[N];
  176. }
  177. template <class... Ts>
  178. auto axis_get(std::tuple<Ts...>& axes, const unsigned i) {
  179. constexpr auto S = sizeof...(Ts);
  180. using V = mp11::mp_unique<axis::variant<Ts*...>>;
  181. return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
  182. }
  183. template <class... Ts>
  184. auto axis_get(const std::tuple<Ts...>& axes, const unsigned i) {
  185. constexpr auto S = sizeof...(Ts);
  186. using V = mp11::mp_unique<axis::variant<const Ts*...>>;
  187. return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
  188. }
  189. template <class T>
  190. decltype(auto) axis_get(T& axes, const unsigned i) {
  191. return axes[i];
  192. }
  193. template <class T>
  194. decltype(auto) axis_get(const T& axes, const unsigned i) {
  195. return axes[i];
  196. }
  197. template <class T, class U, std::size_t... Is>
  198. bool axes_equal_impl(const T& t, const U& u, mp11::index_sequence<Is...>) noexcept {
  199. bool result = true;
  200. // operator folding emulation
  201. (void)std::initializer_list<bool>{
  202. (result &= relaxed_equal{}(std::get<Is>(t), std::get<Is>(u)))...};
  203. return result;
  204. }
  205. template <class... Ts, class... Us>
  206. bool axes_equal_impl(const std::tuple<Ts...>& t, const std::tuple<Us...>& u) noexcept {
  207. return axes_equal_impl(
  208. t, u, mp11::make_index_sequence<std::min(sizeof...(Ts), sizeof...(Us))>{});
  209. }
  210. template <class... Ts, class U>
  211. bool axes_equal_impl(const std::tuple<Ts...>& t, const U& u) noexcept {
  212. using std::begin;
  213. auto iu = begin(u);
  214. bool result = true;
  215. mp11::tuple_for_each(t, [&](const auto& ti) {
  216. axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
  217. ++iu;
  218. });
  219. return result;
  220. }
  221. template <class T, class... Us>
  222. bool axes_equal_impl(const T& t, const std::tuple<Us...>& u) noexcept {
  223. return axes_equal_impl(u, t);
  224. }
  225. template <class T, class U>
  226. bool axes_equal_impl(const T& t, const U& u) noexcept {
  227. using std::begin;
  228. auto iu = begin(u);
  229. bool result = true;
  230. for (auto&& ti : t) {
  231. axis::visit(
  232. [&](const auto& ti) {
  233. axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
  234. },
  235. ti);
  236. ++iu;
  237. }
  238. return result;
  239. }
  240. template <class T, class U>
  241. bool axes_equal(const T& t, const U& u) noexcept {
  242. return axes_rank(t) == axes_rank(u) && axes_equal_impl(t, u);
  243. }
  244. // enable_if_t needed by msvc :(
  245. template <class... Ts, class... Us>
  246. std::enable_if_t<!(std::is_same<std::tuple<Ts...>, std::tuple<Us...>>::value)>
  247. axes_assign(std::tuple<Ts...>&, const std::tuple<Us...>&) {
  248. BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, types do not match"));
  249. }
  250. template <class... Ts>
  251. void axes_assign(std::tuple<Ts...>& t, const std::tuple<Ts...>& u) {
  252. t = u;
  253. }
  254. template <class... Ts, class U>
  255. void axes_assign(std::tuple<Ts...>& t, const U& u) {
  256. if (sizeof...(Ts) == detail::size(u)) {
  257. using std::begin;
  258. auto iu = begin(u);
  259. mp11::tuple_for_each(t, [&](auto& ti) {
  260. using T = std::decay_t<decltype(ti)>;
  261. ti = axis::get<T>(*iu);
  262. ++iu;
  263. });
  264. return;
  265. }
  266. BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, sizes do not match"));
  267. }
  268. template <class T, class... Us>
  269. void axes_assign(T& t, const std::tuple<Us...>& u) {
  270. // resize instead of reserve, because t may not be empty and we want exact capacity
  271. t.resize(sizeof...(Us));
  272. using std::begin;
  273. auto it = begin(t);
  274. mp11::tuple_for_each(u, [&](const auto& ui) { *it++ = ui; });
  275. }
  276. template <class T, class U>
  277. void axes_assign(T& t, const U& u) {
  278. t.assign(u.begin(), u.end());
  279. }
  280. template <class Archive, class T>
  281. void axes_serialize(Archive& ar, T& axes) {
  282. ar& make_nvp("axes", axes);
  283. }
  284. template <class Archive, class... Ts>
  285. void axes_serialize(Archive& ar, std::tuple<Ts...>& axes) {
  286. // needed to keep serialization format backward compatible
  287. struct proxy {
  288. std::tuple<Ts...>& t;
  289. void serialize(Archive& ar, unsigned /* version */) {
  290. mp11::tuple_for_each(t, [&ar](auto& x) { ar& make_nvp("item", x); });
  291. }
  292. };
  293. proxy p{axes};
  294. ar& make_nvp("axes", p);
  295. }
  296. // total number of bins including *flow bins
  297. template <class T>
  298. std::size_t bincount(const T& axes) {
  299. std::size_t n = 1;
  300. for_each_axis(axes, [&n](const auto& a) {
  301. const auto old = n;
  302. const auto s = axis::traits::extent(a);
  303. n *= s;
  304. if (s > 0 && n < old) BOOST_THROW_EXCEPTION(std::overflow_error("bincount overflow"));
  305. });
  306. return n;
  307. }
  308. // initial offset for the linear index
  309. template <class T>
  310. std::size_t offset(const T& axes) {
  311. std::size_t n = 0;
  312. auto stride = static_cast<std::size_t>(1);
  313. for_each_axis(axes, [&](const auto& a) {
  314. if (axis::traits::options(a) & axis::option::growth)
  315. n = invalid_index;
  316. else if (n != invalid_index && axis::traits::options(a) & axis::option::underflow)
  317. n += stride;
  318. stride *= axis::traits::extent(a);
  319. });
  320. return n;
  321. }
  322. // make default-constructed buffer (no initialization for POD types)
  323. template <class T, class A>
  324. auto make_stack_buffer(const A& a) {
  325. return sub_array<T, buffer_size<A>::value>(axes_rank(a));
  326. }
  327. // make buffer with elements initialized to v
  328. template <class T, class A>
  329. auto make_stack_buffer(const A& a, const T& t) {
  330. return sub_array<T, buffer_size<A>::value>(axes_rank(a), t);
  331. }
  332. template <class T>
  333. using has_underflow =
  334. decltype(axis::traits::get_options<T>::test(axis::option::underflow));
  335. template <class T>
  336. using is_growing = decltype(axis::traits::get_options<T>::test(axis::option::growth));
  337. template <class T>
  338. using is_not_inclusive = mp11::mp_not<axis::traits::is_inclusive<T>>;
  339. // for vector<T>
  340. template <class T>
  341. struct axis_types_impl {
  342. using type = mp11::mp_list<std::decay_t<T>>;
  343. };
  344. // for vector<variant<Ts...>>
  345. template <class... Ts>
  346. struct axis_types_impl<axis::variant<Ts...>> {
  347. using type = mp11::mp_list<std::decay_t<Ts>...>;
  348. };
  349. // for tuple<Ts...>
  350. template <class... Ts>
  351. struct axis_types_impl<std::tuple<Ts...>> {
  352. using type = mp11::mp_list<std::decay_t<Ts>...>;
  353. };
  354. template <class T>
  355. using axis_types =
  356. typename axis_types_impl<mp11::mp_if<is_vector_like<T>, mp11::mp_first<T>, T>>::type;
  357. template <template <class> class Trait, class Axes>
  358. using has_special_axis = mp11::mp_any_of<axis_types<Axes>, Trait>;
  359. template <class Axes>
  360. using has_growing_axis = mp11::mp_any_of<axis_types<Axes>, is_growing>;
  361. template <class Axes>
  362. using has_non_inclusive_axis = mp11::mp_any_of<axis_types<Axes>, is_not_inclusive>;
  363. template <class T>
  364. constexpr std::size_t type_score() {
  365. return sizeof(T) *
  366. (std::is_integral<T>::value ? 1 : std::is_floating_point<T>::value ? 10 : 100);
  367. }
  368. // arbitrary ordering of types
  369. template <class T, class U>
  370. using type_less = mp11::mp_bool<(type_score<T>() < type_score<U>())>;
  371. template <class Axes>
  372. using value_types = mp11::mp_sort<
  373. mp11::mp_unique<mp11::mp_transform<axis::traits::value_type, axis_types<Axes>>>,
  374. type_less>;
  375. } // namespace detail
  376. } // namespace histogram
  377. } // namespace boost
  378. #endif