]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/histogram/test/algorithm_reduce_test.cpp
1 // Copyright 2018 Hans Dembinski
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)
7 #include <boost/core/lightweight_test.hpp>
8 #include <boost/histogram/algorithm/reduce.hpp>
9 #include <boost/histogram/algorithm/sum.hpp>
10 #include <boost/histogram/axis/category.hpp>
11 #include <boost/histogram/axis/integer.hpp>
12 #include <boost/histogram/axis/ostream.hpp>
13 #include <boost/histogram/axis/regular.hpp>
14 #include <boost/histogram/axis/variable.hpp>
15 #include <boost/histogram/ostream.hpp>
16 #include <boost/histogram/unsafe_access.hpp>
18 #include "throw_exception.hpp"
19 #include "utility_histogram.hpp"
21 using namespace boost::histogram
;
22 using namespace boost::histogram::algorithm
;
25 axis::index_type
index(int) const { return 0; }
26 axis::index_type
size() const { return 1; }
27 friend std::ostream
& operator<<(std::ostream
& os
, const unreducible
&) {
33 template <typename Tag
>
35 // limitations: shrink does not work with arguments not convertible to double
37 using R
= axis::regular
<double>;
38 using ID
= axis::integer
<double, axis::empty_type
>;
39 using V
= axis::variable
<double, axis::empty_type
>;
40 using CI
= axis::category
<int, axis::empty_type
>;
44 auto h
= make(Tag(), R(4, 1, 5), R(3, -1, 2));
46 // not allowed: invalid axis index
47 BOOST_TEST_THROWS((void)reduce(h
, slice(10, 2, 3)), std::invalid_argument
);
48 // two slice requests for same axis not allowed
49 BOOST_TEST_THROWS((void)reduce(h
, slice(1, 0, 2), slice(1, 1, 3)),
50 std::invalid_argument
);
51 // two rebin requests for same axis not allowed
52 BOOST_TEST_THROWS((void)reduce(h
, rebin(0, 2), rebin(0, 2)), std::invalid_argument
);
53 // rebin and slice_and_rebin with merge > 1 requests for same axis cannot be fused
54 BOOST_TEST_THROWS((void)reduce(h
, slice_and_rebin(0, 1, 3, 2), rebin(0, 2)),
55 std::invalid_argument
);
56 BOOST_TEST_THROWS((void)reduce(h
, shrink(1, 0, 2), crop(1, 0, 2)),
57 std::invalid_argument
);
58 // not allowed: slice with begin >= end
59 BOOST_TEST_THROWS((void)reduce(h
, slice(0, 1, 1)), std::invalid_argument
);
60 BOOST_TEST_THROWS((void)reduce(h
, slice(0, 2, 1)), std::invalid_argument
);
61 // not allowed: shrink with lower == upper
62 BOOST_TEST_THROWS((void)reduce(h
, shrink(0, 0, 0)), std::invalid_argument
);
63 // not allowed: crop with lower == upper
64 BOOST_TEST_THROWS((void)reduce(h
, crop(0, 0, 0)), std::invalid_argument
);
65 // not allowed: shrink axis to zero size
66 BOOST_TEST_THROWS((void)reduce(h
, shrink(0, 10, 11)), std::invalid_argument
);
67 // not allowed: rebin with zero merge
68 BOOST_TEST_THROWS((void)reduce(h
, rebin(0, 0)), std::invalid_argument
);
69 // not allowed: reducing unreducible axis
70 BOOST_TEST_THROWS((void)reduce(make(Tag(), unreducible
{}), slice(0, 1)),
71 std::invalid_argument
);
74 // shrink and crop behavior when value on edge and not on edge is inclusive:
75 // - lower edge of shrink: pick bin which contains edge, lower <= x < upper
76 // - upper edge of shrink: pick bin which contains edge + 1, lower < x <= upper
78 auto h
= make(Tag(), ID(0, 3));
79 const auto& ax
= h
.axis();
80 BOOST_TEST_EQ(ax
.value(0), 0);
81 BOOST_TEST_EQ(ax
.value(3), 3);
82 BOOST_TEST_EQ(ax
.index(-1), -1);
83 BOOST_TEST_EQ(ax
.index(3), 3);
85 BOOST_TEST_EQ(reduce(h
, shrink(-1, 5)).axis(), ID(0, 3));
86 BOOST_TEST_EQ(reduce(h
, shrink(0, 3)).axis(), ID(0, 3));
87 BOOST_TEST_EQ(reduce(h
, shrink(1, 3)).axis(), ID(1, 3));
88 BOOST_TEST_EQ(reduce(h
, shrink(1.001, 3)).axis(), ID(1, 3));
89 BOOST_TEST_EQ(reduce(h
, shrink(1.999, 3)).axis(), ID(1, 3));
90 BOOST_TEST_EQ(reduce(h
, shrink(2, 3)).axis(), ID(2, 3));
91 BOOST_TEST_EQ(reduce(h
, shrink(0, 2.999)).axis(), ID(0, 3));
92 BOOST_TEST_EQ(reduce(h
, shrink(0, 2.001)).axis(), ID(0, 3));
93 BOOST_TEST_EQ(reduce(h
, shrink(0, 2)).axis(), ID(0, 2));
94 BOOST_TEST_EQ(reduce(h
, shrink(0, 1.999)).axis(), ID(0, 2));
96 BOOST_TEST_EQ(reduce(h
, crop(-1, 5)).axis(), ID(0, 3));
97 BOOST_TEST_EQ(reduce(h
, crop(0, 3)).axis(), ID(0, 3));
98 BOOST_TEST_EQ(reduce(h
, crop(1, 3)).axis(), ID(1, 3));
99 BOOST_TEST_EQ(reduce(h
, crop(1.001, 3)).axis(), ID(1, 3));
100 BOOST_TEST_EQ(reduce(h
, crop(1.999, 3)).axis(), ID(1, 3));
101 BOOST_TEST_EQ(reduce(h
, crop(2, 3)).axis(), ID(2, 3));
102 BOOST_TEST_EQ(reduce(h
, crop(0, 2.999)).axis(), ID(0, 3));
103 BOOST_TEST_EQ(reduce(h
, crop(0, 2.001)).axis(), ID(0, 3));
104 BOOST_TEST_EQ(reduce(h
, crop(0, 2)).axis(), ID(0, 2));
105 BOOST_TEST_EQ(reduce(h
, crop(0, 1.999)).axis(), ID(0, 2));
109 auto h
= make_s(Tag(), std::vector
<int>(), R(4, 1, 5, "1"), R(3, -1, 2, "2"));
126 // should do nothing, index order does not matter
127 auto hr
= reduce(h
, shrink(1, -1, 2), rebin(0, 1));
128 BOOST_TEST_EQ(hr
.rank(), 2);
129 BOOST_TEST_EQ(sum(hr
), 10);
130 BOOST_TEST_EQ(hr
.axis(0), R(4, 1, 5, "1"));
131 BOOST_TEST_EQ(hr
.axis(1), R(3, -1, 2, "2"));
132 BOOST_TEST_EQ(hr
, h
);
135 hr
= reduce(h
, slice(1, 0, 4), slice(0, 0, 4));
136 BOOST_TEST_EQ(hr
, h
);
138 // shrinking along first axis
139 hr
= reduce(h
, shrink(0, 2, 4));
140 BOOST_TEST_EQ(hr
.rank(), 2);
141 BOOST_TEST_EQ(sum(hr
), 10);
142 BOOST_TEST_EQ(hr
.axis(0), R(2, 2, 4, "1"));
143 BOOST_TEST_EQ(hr
.axis(1), R(3, -1, 2, "2"));
144 BOOST_TEST_EQ(hr
.at(-1, 0), 1); // underflow
145 BOOST_TEST_EQ(hr
.at(0, 0), 0);
146 BOOST_TEST_EQ(hr
.at(1, 0), 1);
147 BOOST_TEST_EQ(hr
.at(2, 0), 0); // overflow
148 BOOST_TEST_EQ(hr
.at(-1, 1), 1);
149 BOOST_TEST_EQ(hr
.at(0, 1), 1);
150 BOOST_TEST_EQ(hr
.at(1, 1), 0);
151 BOOST_TEST_EQ(hr
.at(2, 1), 0);
152 BOOST_TEST_EQ(hr
.at(-1, 2), 0);
153 BOOST_TEST_EQ(hr
.at(0, 2), 2);
154 BOOST_TEST_EQ(hr
.at(1, 2), 1);
155 BOOST_TEST_EQ(hr
.at(2, 2), 3);
165 hr
= reduce(h
, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3));
166 BOOST_TEST_EQ(hr
.rank(), 2);
167 BOOST_TEST_EQ(sum(hr
), 10);
168 BOOST_TEST_EQ(hr
.axis(0), R(1, 2, 4, "1"));
169 BOOST_TEST_EQ(hr
.axis(1), R(1, -1, 2, "2"));
170 BOOST_TEST_EQ(hr
.at(-1, 0), 2); // underflow
171 BOOST_TEST_EQ(hr
.at(0, 0), 5);
172 BOOST_TEST_EQ(hr
.at(1, 0), 3); // overflow
174 // test overload that accepts iterable and test option fusion
175 std::vector
<reduce_command
> opts
{{shrink(0, 2, 5), rebin(0, 2), rebin(1, 3)}};
176 auto hr2
= reduce(h
, opts
);
177 BOOST_TEST_EQ(hr2
, hr
);
178 reduce_command opts2
[3] = {rebin(1, 3), rebin(0, 2), shrink(0, 2, 5)};
179 auto hr3
= reduce(h
, opts2
);
180 BOOST_TEST_EQ(hr3
, hr
);
182 // test positional args
183 auto hr4
= reduce(h
, shrink_and_rebin(2, 5, 2), rebin(3));
184 BOOST_TEST_EQ(hr4
, hr
);
189 auto h
= make_s(Tag(), std::vector
<int>(), R(4, 1, 5), R(3, 1, 4));
207 crop first and last column in x and y
213 auto hr
= reduce(h
, crop(2, 4), crop_and_rebin(2, 4, 2));
214 BOOST_TEST_EQ(hr
.rank(), 2);
215 BOOST_TEST_EQ(sum(hr
), 4);
216 BOOST_TEST_EQ(hr
.axis(0), R(2, 2, 4));
217 BOOST_TEST_EQ(hr
.axis(1), R(1, 2, 4));
218 BOOST_TEST_EQ(hr
.at(0, 0), 3);
219 BOOST_TEST_EQ(hr
.at(1, 0), 1);
221 // slice with crop mode
222 auto hr2
= reduce(h
, slice(1, 3, slice_mode::crop
),
223 slice_and_rebin(1, 3, 2, slice_mode::crop
));
224 BOOST_TEST_EQ(hr
, hr2
);
226 // explicit axis indices
227 auto hr3
= reduce(h
, crop_and_rebin(1, 2, 4, 2), crop(0, 2, 4));
228 BOOST_TEST_EQ(hr
, hr3
);
229 auto hr4
= reduce(h
, slice_and_rebin(1, 1, 3, 2, slice_mode::crop
),
230 slice(0, 1, 3, slice_mode::crop
));
231 BOOST_TEST_EQ(hr
, hr4
);
241 auto h
= make(Tag(), r
, v
, c
, u
);
242 auto hr
= algorithm::reduce(h
, shrink(0, 2, 4), slice(2, 1, 3));
243 BOOST_TEST_EQ(hr
.axis(0), (R
{2, 2, 4}));
244 BOOST_TEST_EQ(hr
.axis(1), (V
{{1., 2., 3.}}));
245 BOOST_TEST_EQ(hr
.axis(2), (CI
{{2, 3}}));
246 BOOST_TEST_EQ(hr
.axis(3), u
);
247 BOOST_TEST_THROWS((void)algorithm::reduce(h
, rebin(2, 2)), std::invalid_argument
);
250 // reduce on integer axis, rebin must fail
252 auto h
= make(Tag(), axis::integer
<>(1, 4));
253 BOOST_TEST_THROWS((void)reduce(h
, rebin(2)), std::invalid_argument
);
254 auto hr
= reduce(h
, shrink(2, 3));
255 BOOST_TEST_EQ(hr
.axis().size(), 1);
256 BOOST_TEST_EQ(hr
.axis().bin(0), 2);
257 BOOST_TEST_EQ(hr
.axis().bin(1), 3);
260 // reduce on circular axis, shrink must fail, also rebin with remainder
262 auto h
= make(Tag(), axis::circular
<>(4, 1, 4));
263 BOOST_TEST_THROWS((void)reduce(h
, shrink(0, 2)), std::invalid_argument
);
264 BOOST_TEST_THROWS((void)reduce(h
, rebin(3)), std::invalid_argument
);
265 auto hr
= reduce(h
, rebin(2));
266 BOOST_TEST_EQ(hr
.axis().size(), 2);
267 BOOST_TEST_EQ(hr
.axis().bin(0).lower(), 1);
268 BOOST_TEST_EQ(hr
.axis().bin(1).upper(), 4);
271 // reduce on variable axis
273 auto h
= make(Tag(), V({0, 1, 2, 3, 4, 5, 6}));
274 auto hr
= reduce(h
, shrink_and_rebin(1, 5, 2));
275 BOOST_TEST_EQ(hr
.axis().size(), 2);
276 BOOST_TEST_EQ(hr
.axis().value(0), 1);
277 BOOST_TEST_EQ(hr
.axis().value(1), 3);
278 BOOST_TEST_EQ(hr
.axis().value(2), 5);
281 // reduce on axis with inverted range
283 auto h
= make(Tag(), R(4, 2, -2));
284 const auto& ax
= h
.axis();
285 BOOST_TEST_EQ(ax
.index(-0.999), 2);
286 BOOST_TEST_EQ(ax
.index(-1.0), 3);
287 BOOST_TEST_EQ(ax
.index(-1.5), 3);
289 BOOST_TEST_EQ(reduce(h
, shrink(3, -3)).axis(), R(4, 2, -2));
290 BOOST_TEST_EQ(reduce(h
, shrink(2, -2)).axis(), R(4, 2, -2));
291 BOOST_TEST_EQ(reduce(h
, shrink(1.999, -2)).axis(), R(4, 2, -2));
292 BOOST_TEST_EQ(reduce(h
, shrink(1.001, -2)).axis(), R(4, 2, -2));
293 BOOST_TEST_EQ(reduce(h
, shrink(1, -2)).axis(), R(3, 1, -2));
294 BOOST_TEST_EQ(reduce(h
, shrink(2, -1.999)).axis(), R(4, 2, -2));
295 BOOST_TEST_EQ(reduce(h
, shrink(2, -1.001)).axis(), R(4, 2, -2));
296 BOOST_TEST_EQ(reduce(h
, shrink(2, -1)).axis(), R(3, 2, -1));
299 // reduce on histogram with axis without flow bins, see GitHub issue #257
301 auto h
= make(Tag(), axis::integer
<int, use_default
, axis::option::underflow_t
>(0, 3),
302 axis::integer
<int, use_default
, axis::option::overflow_t
>(0, 3));
304 std::fill(h
.begin(), h
.end(), 1);
323 auto hr
= reduce(h
, slice(0, 1, 2), slice(1, 1, 2));
324 BOOST_TEST_EQ(hr
.size(), 2 * 2);
325 BOOST_TEST_EQ(hr
.axis(0).size(), 1);
326 BOOST_TEST_EQ(hr
.axis(1).size(), 1);
327 BOOST_TEST_EQ(hr
.axis(0).bin(0), 1);
328 BOOST_TEST_EQ(hr
.axis(1).bin(0), 1);
330 BOOST_TEST_EQ(hr
.at(-1, 0), 2);
331 BOOST_TEST_EQ(hr
.at(0, 0), 1);
332 BOOST_TEST_EQ(hr
.at(-1, 1), 4);
333 BOOST_TEST_EQ(hr
.at(0, 1), 2);
336 // reduce on category axis: removed bins are added to overflow bin
338 auto h
= make(Tag(), CI
{{1, 2, 3}});
339 std::fill(h
.begin(), h
.end(), 1);
340 // original: [1: 1, 2: 1, 3: 1, overflow: 1]
341 auto hr
= reduce(h
, slice(1, 2));
342 // reduced: [2: 1, overflow: 3]
343 BOOST_TEST_EQ(hr
[0], 1);
344 BOOST_TEST_EQ(hr
[1], 3);
349 run_tests
<static_tag
>();
350 run_tests
<dynamic_tag
>();
352 return boost::report_errors();