algorithm_reduce_test.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 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. #include <boost/core/lightweight_test.hpp>
  7. #include <boost/histogram/algorithm/reduce.hpp>
  8. #include <boost/histogram/algorithm/sum.hpp>
  9. #include <boost/histogram/axis/category.hpp>
  10. #include <boost/histogram/axis/integer.hpp>
  11. #include <boost/histogram/axis/ostream.hpp>
  12. #include <boost/histogram/axis/regular.hpp>
  13. #include <boost/histogram/axis/variable.hpp>
  14. #include <boost/histogram/ostream.hpp>
  15. #include <boost/histogram/unsafe_access.hpp>
  16. #include <vector>
  17. #include "throw_exception.hpp"
  18. #include "utility_histogram.hpp"
  19. using namespace boost::histogram;
  20. using namespace boost::histogram::algorithm;
  21. template <typename Tag>
  22. void run_tests() {
  23. // reduce:
  24. // - does not work with arguments not convertible to double
  25. // - does not work with category axis, which is not ordered
  26. using R = axis::regular<double, axis::transform::id, axis::null_type>;
  27. using ID = axis::integer<double, axis::null_type>;
  28. using V = axis::variable<double, axis::empty_type>;
  29. using CI = axis::category<int, axis::empty_type>;
  30. // various failures
  31. {
  32. auto h = make(Tag(), R(4, 1, 5), R(3, -1, 2));
  33. // not allowed: invalid axis index
  34. BOOST_TEST_THROWS((void)reduce(h, slice(10, 2, 3)), std::invalid_argument);
  35. // not allowed: repeated indices
  36. BOOST_TEST_THROWS((void)reduce(h, slice(1, 0, 2), slice(1, 1, 3)),
  37. std::invalid_argument);
  38. BOOST_TEST_THROWS((void)reduce(h, rebin(0, 2), rebin(0, 2)), std::invalid_argument);
  39. BOOST_TEST_THROWS((void)reduce(h, shrink(1, 0, 2), shrink(1, 0, 2)),
  40. std::invalid_argument);
  41. // not allowed: slice with begin >= end
  42. BOOST_TEST_THROWS((void)reduce(h, slice(0, 1, 1)), std::invalid_argument);
  43. BOOST_TEST_THROWS((void)reduce(h, slice(0, 2, 1)), std::invalid_argument);
  44. // not allowed: shrink with lower == upper
  45. BOOST_TEST_THROWS((void)reduce(h, shrink(0, 0, 0)), std::invalid_argument);
  46. // not allowed: shrink axis to zero size
  47. BOOST_TEST_THROWS((void)reduce(h, shrink(0, 10, 11)), std::invalid_argument);
  48. // not allowed: rebin with zero merge
  49. BOOST_TEST_THROWS((void)reduce(h, rebin(0, 0)), std::invalid_argument);
  50. }
  51. // shrink behavior when value on edge and not on edge is inclusive:
  52. // - lower edge of shrink: pick bin which contains edge, lower <= x < upper
  53. // - upper edge of shrink: pick bin which contains edge + 1, lower < x <= upper
  54. {
  55. auto h = make(Tag(), ID(0, 3));
  56. const auto& ax = h.axis();
  57. BOOST_TEST_EQ(ax.value(0), 0);
  58. BOOST_TEST_EQ(ax.value(1), 1);
  59. BOOST_TEST_EQ(ax.value(2), 2);
  60. BOOST_TEST_EQ(ax.value(3), 3);
  61. BOOST_TEST_EQ(ax.index(-1), -1);
  62. BOOST_TEST_EQ(ax.index(0), 0);
  63. BOOST_TEST_EQ(ax.index(1), 1);
  64. BOOST_TEST_EQ(ax.index(2), 2);
  65. BOOST_TEST_EQ(ax.index(3), 3);
  66. BOOST_TEST_EQ(reduce(h, shrink(-1, 5)).axis(), ID(0, 3));
  67. BOOST_TEST_EQ(reduce(h, shrink(0, 3)).axis(), ID(0, 3));
  68. BOOST_TEST_EQ(reduce(h, shrink(1, 3)).axis(), ID(1, 3));
  69. BOOST_TEST_EQ(reduce(h, shrink(1.001, 3)).axis(), ID(1, 3));
  70. BOOST_TEST_EQ(reduce(h, shrink(1.999, 3)).axis(), ID(1, 3));
  71. BOOST_TEST_EQ(reduce(h, shrink(2, 3)).axis(), ID(2, 3));
  72. BOOST_TEST_EQ(reduce(h, shrink(0, 2.999)).axis(), ID(0, 3));
  73. BOOST_TEST_EQ(reduce(h, shrink(0, 2.001)).axis(), ID(0, 3));
  74. BOOST_TEST_EQ(reduce(h, shrink(0, 2)).axis(), ID(0, 2));
  75. BOOST_TEST_EQ(reduce(h, shrink(0, 1.999)).axis(), ID(0, 2));
  76. }
  77. {
  78. auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, -1, 2));
  79. /*
  80. matrix layout:
  81. x ->
  82. y 1 0 1 0
  83. | 1 1 0 0
  84. v 0 2 1 3
  85. */
  86. h.at(0, 0) = 1;
  87. h.at(0, 1) = 1;
  88. h.at(1, 1) = 1;
  89. h.at(1, 2) = 2;
  90. h.at(2, 0) = 1;
  91. h.at(2, 2) = 1;
  92. h.at(3, 2) = 3;
  93. // should do nothing, index order does not matter
  94. auto hr = reduce(h, shrink(1, -1, 2), rebin(0, 1));
  95. BOOST_TEST_EQ(hr.rank(), 2);
  96. BOOST_TEST_EQ(sum(hr), 10);
  97. BOOST_TEST_EQ(hr.axis(0), R(4, 1, 5));
  98. BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2));
  99. BOOST_TEST_EQ(hr, h);
  100. hr = reduce(h, slice(1, 0, 4), slice(0, 0, 4));
  101. BOOST_TEST_EQ(hr, h);
  102. hr = reduce(h, shrink(0, 2, 4));
  103. BOOST_TEST_EQ(hr.rank(), 2);
  104. BOOST_TEST_EQ(sum(hr), 10);
  105. BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4));
  106. BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2));
  107. BOOST_TEST_EQ(hr.at(-1, 0), 1); // underflow
  108. BOOST_TEST_EQ(hr.at(0, 0), 0);
  109. BOOST_TEST_EQ(hr.at(1, 0), 1);
  110. BOOST_TEST_EQ(hr.at(2, 0), 0); // overflow
  111. BOOST_TEST_EQ(hr.at(-1, 1), 1);
  112. BOOST_TEST_EQ(hr.at(0, 1), 1);
  113. BOOST_TEST_EQ(hr.at(1, 1), 0);
  114. BOOST_TEST_EQ(hr.at(2, 1), 0);
  115. BOOST_TEST_EQ(hr.at(-1, 2), 0);
  116. BOOST_TEST_EQ(hr.at(0, 2), 2);
  117. BOOST_TEST_EQ(hr.at(1, 2), 1);
  118. BOOST_TEST_EQ(hr.at(2, 2), 3);
  119. /*
  120. matrix layout:
  121. x ->
  122. y 1 0 1 0
  123. | 1 1 0 0
  124. v 0 2 1 3
  125. */
  126. hr = reduce(h, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3));
  127. BOOST_TEST_EQ(hr.rank(), 2);
  128. BOOST_TEST_EQ(sum(hr), 10);
  129. BOOST_TEST_EQ(hr.axis(0).size(), 1);
  130. BOOST_TEST_EQ(hr.axis(1).size(), 1);
  131. BOOST_TEST_EQ(hr.axis(0).bin(0).lower(), 2);
  132. BOOST_TEST_EQ(hr.axis(0).bin(0).upper(), 4);
  133. BOOST_TEST_EQ(hr.axis(1).bin(0).lower(), -1);
  134. BOOST_TEST_EQ(hr.axis(1).bin(0).upper(), 2);
  135. BOOST_TEST_EQ(hr.at(-1, 0), 2); // underflow
  136. BOOST_TEST_EQ(hr.at(0, 0), 5);
  137. BOOST_TEST_EQ(hr.at(1, 0), 3); // overflow
  138. // test overload that accepts iterable and test option fusion
  139. std::vector<reduce_option> opts{{shrink(0, 2, 5), rebin(0, 2), rebin(1, 3)}};
  140. auto hr2 = reduce(h, opts);
  141. BOOST_TEST_EQ(hr2, hr);
  142. opts = {rebin(0, 2), slice(0, 1, 4), rebin(1, 3)};
  143. auto hr3 = reduce(h, opts);
  144. BOOST_TEST_EQ(hr3, hr);
  145. }
  146. // mixed axis types
  147. {
  148. R r(5, 0.0, 1.0);
  149. V v{{1., 2., 3.}};
  150. CI c{{1, 2, 3}};
  151. auto h = make(Tag(), r, v, c);
  152. auto hr = algorithm::reduce(h, shrink(0, 0.2, 0.7));
  153. BOOST_TEST_EQ(hr.axis(0).size(), 3);
  154. BOOST_TEST_EQ(hr.axis(0).bin(0).lower(), 0.2);
  155. BOOST_TEST_EQ(hr.axis(0).bin(2).upper(), 0.8);
  156. BOOST_TEST_EQ(hr.axis(1).size(), 2);
  157. BOOST_TEST_EQ(hr.axis(1).bin(0).lower(), 1);
  158. BOOST_TEST_EQ(hr.axis(1).bin(1).upper(), 3);
  159. BOOST_TEST_THROWS((void)algorithm::reduce(h, rebin(2, 2)), std::invalid_argument);
  160. }
  161. // reduce on integer axis, rebin must fail
  162. {
  163. auto h = make(Tag(), axis::integer<>(1, 4));
  164. BOOST_TEST_THROWS((void)reduce(h, rebin(2)), std::invalid_argument);
  165. auto hr = reduce(h, shrink(2, 3));
  166. BOOST_TEST_EQ(hr.axis().size(), 1);
  167. BOOST_TEST_EQ(hr.axis().bin(0), 2);
  168. BOOST_TEST_EQ(hr.axis().bin(1), 3);
  169. }
  170. // reduce on circular axis, shrink must fail, also rebin with remainder
  171. {
  172. auto h = make(Tag(), axis::circular<>(4, 1, 4));
  173. BOOST_TEST_THROWS((void)reduce(h, shrink(0, 2)), std::invalid_argument);
  174. BOOST_TEST_THROWS((void)reduce(h, rebin(3)), std::invalid_argument);
  175. auto hr = reduce(h, rebin(2));
  176. BOOST_TEST_EQ(hr.axis().size(), 2);
  177. BOOST_TEST_EQ(hr.axis().bin(0).lower(), 1);
  178. BOOST_TEST_EQ(hr.axis().bin(1).upper(), 4);
  179. }
  180. // reduce on variable axis
  181. {
  182. auto h = make(Tag(), V({0, 1, 2, 3, 4, 5, 6}));
  183. auto hr = reduce(h, shrink_and_rebin(1, 5, 2));
  184. BOOST_TEST_EQ(hr.axis().size(), 2);
  185. BOOST_TEST_EQ(hr.axis().value(0), 1);
  186. BOOST_TEST_EQ(hr.axis().value(1), 3);
  187. BOOST_TEST_EQ(hr.axis().value(2), 5);
  188. }
  189. // reduce on axis with inverted range
  190. {
  191. auto h = make(Tag(), R(4, 2, -2));
  192. const auto& ax = h.axis();
  193. BOOST_TEST_EQ(ax.index(-0.999), 2);
  194. BOOST_TEST_EQ(ax.index(-1.0), 3);
  195. BOOST_TEST_EQ(ax.index(-1.5), 3);
  196. BOOST_TEST_EQ(reduce(h, shrink(3, -3)).axis(), R(4, 2, -2));
  197. BOOST_TEST_EQ(reduce(h, shrink(2, -2)).axis(), R(4, 2, -2));
  198. BOOST_TEST_EQ(reduce(h, shrink(1.999, -2)).axis(), R(4, 2, -2));
  199. BOOST_TEST_EQ(reduce(h, shrink(1.001, -2)).axis(), R(4, 2, -2));
  200. BOOST_TEST_EQ(reduce(h, shrink(1, -2)).axis(), R(3, 1, -2));
  201. BOOST_TEST_EQ(reduce(h, shrink(2, -1.999)).axis(), R(4, 2, -2));
  202. BOOST_TEST_EQ(reduce(h, shrink(2, -1.001)).axis(), R(4, 2, -2));
  203. BOOST_TEST_EQ(reduce(h, shrink(2, -1)).axis(), R(3, 2, -1));
  204. }
  205. }
  206. int main() {
  207. run_tests<static_tag>();
  208. run_tests<dynamic_tag>();
  209. return boost::report_errors();
  210. }