• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
7 #include <boost/core/ignore_unused.hpp>
8 #include <boost/core/lightweight_test.hpp>
9 #include <boost/histogram/accumulators.hpp>
10 #include <boost/histogram/accumulators/ostream.hpp>
11 #include <boost/histogram/algorithm/sum.hpp>
12 #include <boost/histogram/axis.hpp>
13 #include <boost/histogram/axis/ostream.hpp>
14 #include <boost/histogram/histogram.hpp>
15 #include <boost/histogram/literals.hpp>
16 #include <boost/histogram/make_histogram.hpp>
17 #include <boost/histogram/ostream.hpp>
18 #include <sstream>
19 #include <stdexcept>
20 #include <tuple>
21 #include <utility>
22 #include <vector>
23 #include "is_close.hpp"
24 #include "std_ostream.hpp"
25 #include "throw_exception.hpp"
26 #include "utility_allocator.hpp"
27 #include "utility_axis.hpp"
28 #include "utility_histogram.hpp"
29 
30 using namespace boost::histogram;
31 using namespace boost::histogram::literals; // to get _c suffix
32 
33 template <class A, class S>
pass_histogram(boost::histogram::histogram<A,S> & h)34 void pass_histogram(boost::histogram::histogram<A, S>& h) {
35   BOOST_TEST_EQ(h.at(0), 0);
36   BOOST_TEST_EQ(h.at(1), 1);
37   BOOST_TEST_EQ(h.at(2), 0);
38   BOOST_TEST_EQ(h.axis(0_c), axis::integer<>(0, 3));
39 }
40 
41 template <class Tag>
run_tests()42 void run_tests() {
43   // init_1
44   {
45     auto h = make(Tag(), axis::regular<>{3, -1, 1});
46     BOOST_TEST_EQ(h.rank(), 1);
47     BOOST_TEST_EQ(h.size(), 5);
48     BOOST_TEST_EQ(h.axis(0_c).size(), 3);
49     BOOST_TEST_EQ(h.axis().size(), 3);
50     auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1});
51     BOOST_TEST_EQ(h2, h);
52   }
53 
54   // init_2
55   {
56     auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2},
57                   axis::circular<>{2, 0, 360}, axis::variable<>{-1, 0, 1},
58                   axis::category<>{{3, 1, 2}});
59     BOOST_TEST_EQ(h.rank(), 5);
60     BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4 * 4);
61     auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1},
62                      axis::integer<>{-1, 2}, axis::circular<>{2, 0, 360},
63                      axis::variable<>{-1, 0, 1}, axis::category<>{{3, 1, 2}});
64     BOOST_TEST_EQ(h2, h);
65   }
66 
67   // copy_ctor
68   {
69     auto h = make(Tag(), axis::integer<>{0, 2}, axis::integer<>{0, 3});
70     h(0, 0);
71     auto h2 = decltype(h)(h);
72     BOOST_TEST_EQ(h2, h);
73     auto h3 =
74         histogram<std::tuple<axis::integer<>, axis::integer<>>, dense_storage<double>>(h);
75     BOOST_TEST_EQ(h3, h);
76   }
77 
78   // copy_assign
79   {
80     auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 2));
81     h(0, 0);
82     auto h2 = decltype(h)();
83     BOOST_TEST_NE(h, h2);
84     h2 = h;
85     BOOST_TEST_EQ(h, h2);
86     auto h3 =
87         histogram<std::tuple<axis::integer<>, axis::integer<>>, dense_storage<double>>();
88     h3 = h;
89     BOOST_TEST_EQ(h, h3);
90   }
91 
92   // move
93   {
94     auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 2));
95     h(0, 0);
96     const auto href = h;
97     decltype(h) h2(std::move(h));
98     BOOST_TEST_EQ(algorithm::sum(h), 0);
99     BOOST_TEST_EQ(h.size(), 0);
100     BOOST_TEST_EQ(h2, href);
101     decltype(h) h3;
102     h3 = std::move(h2);
103     BOOST_TEST_EQ(algorithm::sum(h2), 0);
104     BOOST_TEST_EQ(h2.size(), 0);
105     BOOST_TEST_EQ(h3, href);
106   }
107 
108   // axis methods
109   {
110     auto a = make(Tag(), axis::integer<double>(1, 2, "foo"));
111     BOOST_TEST_EQ(a.axis().size(), 1);
112     BOOST_TEST_EQ(a.axis().bin(0).lower(), 1);
113     BOOST_TEST_EQ(a.axis().bin(0).upper(), 2);
114     BOOST_TEST_EQ(a.axis().metadata(), "foo");
115     unsafe_access::axis(a, 0).metadata() = "bar";
116     BOOST_TEST_EQ(a.axis().metadata(), "bar");
117 
118     auto b = make(Tag(), axis::integer<double>(1, 2, "foo"), axis::integer<>(1, 3));
119 
120     // check static access
121     BOOST_TEST_EQ(b.axis(0_c).size(), 1);
122     BOOST_TEST_EQ(b.axis(0_c).bin(0).lower(), 1);
123     BOOST_TEST_EQ(b.axis(0_c).bin(0).upper(), 2);
124     BOOST_TEST_EQ(b.axis(1_c).size(), 2);
125     BOOST_TEST_EQ(b.axis(1_c).bin(0), 1);
126     BOOST_TEST_EQ(b.axis(1_c).bin(1), 2);
127     unsafe_access::axis(b, 1_c).metadata() = "bar";
128     BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo");
129     BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar");
130 
131     // check dynamic access
132     BOOST_TEST_EQ(b.axis(0).size(), 1);
133     BOOST_TEST_EQ(b.axis(0).bin(0).lower(), 1);
134     BOOST_TEST_EQ(b.axis(0).bin(0).upper(), 2);
135     BOOST_TEST_EQ(b.axis(1).size(), 2);
136     BOOST_TEST_EQ(b.axis(1).bin(0), 1);
137     BOOST_TEST_EQ(b.axis(1).bin(1), 2);
138     BOOST_TEST_EQ(b.axis(0).metadata(), "foo");
139     BOOST_TEST_EQ(b.axis(1).metadata(), "bar");
140     unsafe_access::axis(b, 0).metadata() = "baz";
141     BOOST_TEST_EQ(b.axis(0).metadata(), "baz");
142 
143     auto c = make(Tag(), axis::category<>({1, 2}));
144     BOOST_TEST_EQ(c.axis().size(), 2);
145     unsafe_access::axis(c, 0).metadata() = "foo";
146     BOOST_TEST_EQ(c.axis().metadata(), "foo");
147     // need to cast here for this to work with Tag == dynamic_tag, too
148     const auto& ca = axis::get<axis::category<>>(c.axis());
149     BOOST_TEST_EQ(ca.bin(0), 1);
150     const auto& ca2 = axis::get<axis::category<>>(c.axis(0));
151     BOOST_TEST_EQ(&ca2, &ca);
152   }
153 
154   // equal_compare
155   {
156     auto a = make(Tag(), axis::integer<>(0, 2));
157     auto b = make(Tag(), axis::integer<>(0, 2), axis::integer<>(0, 3));
158     BOOST_TEST(a != b);
159     BOOST_TEST(b != a);
160     auto c = make(Tag(), axis::integer<>(0, 2));
161     BOOST_TEST(b != c);
162     BOOST_TEST(c != b);
163     BOOST_TEST(a == c);
164     BOOST_TEST(c == a);
165     auto d = make(Tag(), axis::regular<>(2, 0, 1));
166     BOOST_TEST(c != d);
167     BOOST_TEST(d != c);
168     c(0);
169     BOOST_TEST(a != c);
170     BOOST_TEST(c != a);
171     a(0);
172     BOOST_TEST(a == c);
173     BOOST_TEST(c == a);
174     a(0);
175     BOOST_TEST(a != c);
176     BOOST_TEST(c != a);
177   }
178 
179   // 1D
180   {
181     auto h = make(Tag(), axis::integer<int, axis::null_type>{0, 2});
182     h(0);
183     auto i = h(0);
184     BOOST_TEST(i == h.begin() + 1); // +1 because of underflow
185     i = h(-1);
186     BOOST_TEST(i == h.begin()); // underflow
187     i = h(10);
188     BOOST_TEST(i == h.end() - 1); // overflow
189 
190     BOOST_TEST_EQ(h.rank(), 1);
191     BOOST_TEST_EQ(h.axis().size(), 2);
192     BOOST_TEST_EQ(algorithm::sum(h), 4);
193 
194     BOOST_TEST_EQ(h.at(-1), 1);
195     BOOST_TEST_EQ(h.at(0), 2);
196     BOOST_TEST_EQ(h.at(1), 0);
197     BOOST_TEST_EQ(h.at(2), 1);
198   }
199 
200   // 1D no *flow
201   {
202     auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2));
203     h(0);
204     auto i = h(-0);
205     BOOST_TEST(i == h.begin());
206     i = h(-1);
207     BOOST_TEST(i == h.end());
208     i = h(10);
209     BOOST_TEST(i == h.end());
210 
211     BOOST_TEST_EQ(h.rank(), 1);
212     BOOST_TEST_EQ(h.axis().size(), 2);
213     BOOST_TEST_EQ(algorithm::sum(h), 2);
214 
215     BOOST_TEST_EQ(h.at(0), 2);
216     BOOST_TEST_EQ(h.at(1), 0);
217   }
218 
219   // 1D category axis
220   {
221     auto h = make(Tag(), axis::category<>({1, 2}));
222     h(1);
223     h(2);
224     h(4);
225     h(5);
226 
227     BOOST_TEST_EQ(h.rank(), 1);
228     BOOST_TEST_EQ(h.axis().size(), 2);
229     BOOST_TEST_EQ(algorithm::sum(h), 4);
230 
231     BOOST_TEST_EQ(h.at(0), 1);
232     BOOST_TEST_EQ(h.at(1), 1);
233     BOOST_TEST_EQ(h.at(2), 2); // overflow bin
234   }
235 
236   // 1D weight
237   {
238     auto h = make_s(Tag(), weight_storage(), axis::integer<>(0, 2));
239     h(-1);
240     h(0);
241     h(weight(0.5), 0);
242     h(1);
243     h(weight(2), 2);
244 
245     BOOST_TEST_EQ(algorithm::sum(h).value(), 5.5);
246     BOOST_TEST_EQ(algorithm::sum(h).variance(), 7.25);
247 
248     BOOST_TEST_EQ(h[-1].value(), 1);
249     BOOST_TEST_EQ(h[-1].variance(), 1);
250     BOOST_TEST_EQ(h[0].value(), 1.5);
251     BOOST_TEST_EQ(h[0].variance(), 1.25);
252     BOOST_TEST_EQ(h[1].value(), 1);
253     BOOST_TEST_EQ(h[1].variance(), 1);
254     BOOST_TEST_EQ(h[2].value(), 2);
255     BOOST_TEST_EQ(h[2].variance(), 4);
256   }
257 
258   // 1D profile
259   {
260     auto h = make_s(Tag(), profile_storage(), axis::integer<>(0, 2));
261 
262     h(0, sample(1));
263     h(0, sample(2));
264     h(0, sample(3));
265     h(sample(4), 1);
266     h(sample(5), 1);
267     h(sample(6), 1);
268 
269     BOOST_TEST_EQ(h[0].count(), 3);
270     BOOST_TEST_EQ(h[0].value(), 2);
271     BOOST_TEST_EQ(h[0].variance(), 1);
272     BOOST_TEST_EQ(h[1].count(), 3);
273     BOOST_TEST_EQ(h[1].value(), 5);
274     BOOST_TEST_EQ(h[1].variance(), 1);
275   }
276 
277   // 1D weighted profile
278   {
279     auto h = make_s(Tag(), weighted_profile_storage(), axis::integer<>(0, 2));
280 
281     h(0, sample(1));
282     h(sample(1), 0);
283 
284     h(0, weight(2), sample(3));
285     h(0, sample(5), weight(2));
286 
287     h(weight(2), 1, sample(1));
288     h(sample(2), 1, weight(2));
289 
290     h(weight(2), sample(3), 1);
291     h(sample(4), weight(2), 1);
292 
293     BOOST_TEST_EQ(h[0].sum_of_weights(), 6);
294     BOOST_TEST_EQ(h[0].value(), 3);
295     BOOST_TEST_EQ(h[1].sum_of_weights(), 8);
296     BOOST_TEST_EQ(h[1].value(), 2.5);
297   }
298 
299   // 2D
300   {
301     auto h = make(Tag(), axis::integer<>(-1, 1),
302                   axis::integer<int, axis::null_type, axis::option::none_t>(-1, 2));
303     h(-1, -1);
304     h(-1, 0);
305     h(-1, -10);
306     h(-10, 0);
307 
308     BOOST_TEST_EQ(h.rank(), 2);
309     BOOST_TEST_EQ(h.axis(0_c).size(), 2);
310     BOOST_TEST_EQ(h.axis(1_c).size(), 3);
311     BOOST_TEST_EQ(algorithm::sum(h), 3);
312 
313     BOOST_TEST_EQ(h.at(-1, 0), 0);
314     BOOST_TEST_EQ(h.at(-1, 1), 1);
315     BOOST_TEST_EQ(h.at(-1, 2), 0);
316 
317     BOOST_TEST_EQ(h.at(0, 0), 1);
318     BOOST_TEST_EQ(h.at(0, 1), 1);
319     BOOST_TEST_EQ(h.at(0, 2), 0);
320 
321     BOOST_TEST_EQ(h.at(1, 0), 0);
322     BOOST_TEST_EQ(h.at(1, 1), 0);
323     BOOST_TEST_EQ(h.at(1, 2), 0);
324 
325     BOOST_TEST_EQ(h.at(2, 0), 0);
326     BOOST_TEST_EQ(h.at(2, 1), 0);
327     BOOST_TEST_EQ(h.at(2, 2), 0);
328   }
329 
330   // 2D weight
331   {
332     auto h = make_s(Tag(), weight_storage(), axis::integer<>(-1, 1),
333                     axis::integer<int, axis::null_type, axis::option::none_t>(-1, 2));
334     h(-1, 0);              // -> 0, 1
335     h(weight(10), -1, -1); // -> 0, 0
336     h(weight(5), -1, -10); // is ignored
337     h(weight(7), -10, 0);  // -> -1, 1
338 
339     BOOST_TEST_EQ(algorithm::sum(h).value(), 18);
340     BOOST_TEST_EQ(algorithm::sum(h).variance(), 150);
341 
342     BOOST_TEST_EQ(h.at(-1, 0).value(), 0);
343     BOOST_TEST_EQ(h.at(-1, 1).value(), 7);
344     BOOST_TEST_EQ(h.at(-1, 2).value(), 0);
345 
346     BOOST_TEST_EQ(h.at(0, 0).value(), 10);
347     BOOST_TEST_EQ(h.at(0, 1).value(), 1);
348     BOOST_TEST_EQ(h.at(0, 2).value(), 0);
349 
350     BOOST_TEST_EQ(h.at(1, 0).value(), 0);
351     BOOST_TEST_EQ(h.at(1, 1).value(), 0);
352     BOOST_TEST_EQ(h.at(1, 2).value(), 0);
353 
354     BOOST_TEST_EQ(h.at(2, 0).value(), 0);
355     BOOST_TEST_EQ(h.at(2, 1).value(), 0);
356     BOOST_TEST_EQ(h.at(2, 2).value(), 0);
357 
358     BOOST_TEST_EQ(h.at(-1, 0).variance(), 0);
359     BOOST_TEST_EQ(h.at(-1, 1).variance(), 49);
360     BOOST_TEST_EQ(h.at(-1, 2).variance(), 0);
361 
362     BOOST_TEST_EQ(h.at(0, 0).variance(), 100);
363     BOOST_TEST_EQ(h.at(0, 1).variance(), 1);
364     BOOST_TEST_EQ(h.at(0, 2).variance(), 0);
365 
366     BOOST_TEST_EQ(h.at(1, 0).variance(), 0);
367     BOOST_TEST_EQ(h.at(1, 1).variance(), 0);
368     BOOST_TEST_EQ(h.at(1, 2).variance(), 0);
369 
370     BOOST_TEST_EQ(h.at(2, 0).variance(), 0);
371     BOOST_TEST_EQ(h.at(2, 1).variance(), 0);
372     BOOST_TEST_EQ(h.at(2, 2).variance(), 0);
373   }
374 
375   // 3D weight
376   {
377     auto h = make_s(Tag(), weight_storage(), axis::integer<>(0, 3), axis::integer<>(0, 4),
378                     axis::integer<>(0, 5));
379     for (auto i = 0; i < h.axis(0_c).size(); ++i)
380       for (auto j = 0; j < h.axis(1_c).size(); ++j)
381         for (auto k = 0; k < h.axis(2_c).size(); ++k) h(i, j, k, weight(i + j + k));
382 
383     for (auto i = 0; i < h.axis(0_c).size(); ++i) {
384       for (auto j = 0; j < h.axis(1_c).size(); ++j) {
385         for (auto k = 0; k < h.axis(2_c).size(); ++k) {
386           BOOST_TEST_EQ(h.at(i, j, k).value(), i + j + k);
387           BOOST_TEST_EQ(h.at(i, j, k).variance(), (i + j + k) * (i + j + k));
388         }
389       }
390     }
391   }
392 
393   // STL support
394   {
395     auto v = std::vector<int>{0, 1, 2};
396     auto h = std::for_each(v.begin(), v.end(), make(Tag(), axis::integer<>(0, 3)));
397     BOOST_TEST_EQ(h.at(0), 1);
398     BOOST_TEST_EQ(h.at(1), 1);
399     BOOST_TEST_EQ(h.at(2), 1);
400     BOOST_TEST_EQ(algorithm::sum(h), 3);
401 
402     auto a = std::vector<double>();
403     // walks over all bins, including underflow and overflow
404     std::partial_sum(h.begin(), h.end(), std::back_inserter(a));
405     BOOST_TEST_EQ(a.size(), 5);
406     BOOST_TEST_EQ(a[0], 0);
407     BOOST_TEST_EQ(a[1], 1);
408     BOOST_TEST_EQ(a[2], 2);
409     BOOST_TEST_EQ(a[3], 3);
410     BOOST_TEST_EQ(a[4], 3);
411   }
412 
413   // histogram reset
414   {
415     auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2));
416     h(0);
417     h(1);
418     BOOST_TEST_EQ(h.at(0), 1);
419     BOOST_TEST_EQ(h.at(1), 1);
420     BOOST_TEST_EQ(algorithm::sum(h), 2);
421     h.reset();
422     BOOST_TEST_EQ(h.at(0), 0);
423     BOOST_TEST_EQ(h.at(1), 0);
424     BOOST_TEST_EQ(algorithm::sum(h), 0);
425   }
426 
427   // using containers for input and output
428   {
429     auto h = make(Tag(), axis::integer<>(0, 2), axis::integer<double>(2, 4));
430     // tuple in
431     h(std::make_tuple(0, 2.0));
432     h(std::make_tuple(1, 3.0));
433 
434     auto i00 = std::make_tuple(0, 0);
435     auto i11 = std::make_tuple(1, 1);
436 
437     // tuple out
438     BOOST_TEST_EQ(h.at(i00), 1);
439     BOOST_TEST_EQ(h[i00], 1);
440     BOOST_TEST_EQ(h[i11], 1);
441 
442     // iterable out
443     int j11[] = {1, 1};
444     BOOST_TEST_EQ(h.at(j11), 1);
445     BOOST_TEST_EQ(h[j11], 1);
446     int j111[] = {1, 1, 1};
447     (void)j111;
448     BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument);
449     int j13[] = {1, 3};
450     (void)j13;
451     BOOST_TEST_THROWS((void)h.at(j13), std::out_of_range);
452 
453     // tuple with weight
454     h(std::make_tuple(weight(2), 0, 2.0));
455     h(std::make_tuple(1, 3.0, weight(2)));
456 
457     BOOST_TEST_EQ(h.at(i00), 3);
458     BOOST_TEST_EQ(h[i00], 3);
459 
460     // test special case of 1-dimensional histogram, which should unpack
461     // 1-dimensional tuple normally, but forward larger tuples to the axis
462     auto h1 = make(Tag(), axis::integer<>(0, 2));
463     h1(std::make_tuple(0));                      // as if one had passed 0 directly
464     BOOST_TEST_EQ(h1.at(std::make_tuple(0)), 1); // as if one had passed 0 directly
465   }
466 
467   // bad bin access
468   {
469     auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 1));
470     BOOST_TEST_THROWS(h.at(0, 2), std::out_of_range);
471     BOOST_TEST_THROWS(h.at(std::make_tuple(2, 0)), std::out_of_range);
472   }
473 
474   // pass histogram to function
475   {
476     auto h = make(Tag(), axis::integer<>(0, 3));
477     h(1);
478     pass_histogram(h);
479   }
480 
481   // allocator support
482   {
483     tracing_allocator_db db;
484     {
485       tracing_allocator<char> a(db);
486       auto h = make_s(Tag(), std::vector<int, tracing_allocator<int>>(a),
487                       axis::integer<>(0, 1000));
488       h(0);
489     }
490 
491     // int allocation for std::vector
492     BOOST_TEST_EQ(db.at<int>().first, 0);
493     BOOST_TEST_EQ(db.at<int>().second, 1002);
494 
495     if (Tag()) { // axis::variant allocation, only for dynamic histogram
496       using T = axis::variant<axis::integer<>>;
497       BOOST_TEST_EQ(db.at<T>().first, 0);
498       // may be zero if vector uses small-vector-optimisation
499       BOOST_TEST_LE(db.at<T>().second, 1);
500     }
501   }
502 }
503 
main()504 int main() {
505   run_tests<static_tag>();
506   run_tests<dynamic_tag>();
507 
508   return boost::report_errors();
509 }
510