Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / histogram / test / histogram_fill_test.cpp
1 // Copyright 2019 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 <array>
8 #include <boost/config.hpp>
9 #include <boost/core/ignore_unused.hpp>
10 #include <boost/core/lightweight_test.hpp>
11 #include <boost/histogram/accumulators.hpp>
12 #include <boost/histogram/accumulators/ostream.hpp>
13 #include <boost/histogram/algorithm/sum.hpp>
14 #include <boost/histogram/axis/category.hpp>
15 #include <boost/histogram/axis/integer.hpp>
16 #include <boost/histogram/axis/ostream.hpp>
17 #include <boost/histogram/histogram.hpp>
18 #include <boost/histogram/literals.hpp>
19 #include <boost/histogram/make_histogram.hpp>
20 #include <boost/histogram/ostream.hpp>
21 #include <boost/histogram/storage_adaptor.hpp>
22 #include <boost/variant2/variant.hpp>
23 #include <random>
24 #include <sstream>
25 #include <stdexcept>
26 #include <tuple>
27 #include <utility>
28 #include <vector>
29 #include "throw_exception.hpp"
30 #include "utility_histogram.hpp"
31
32 using namespace boost::histogram;
33 using namespace boost::histogram::algorithm;
34 using namespace boost::histogram::literals; // to get _c suffix
35 using boost::variant2::variant;
36
37 constexpr auto ndata = 1 << 16; // should be larger than index buffer in fill_n
38
39 using in = axis::integer<int, axis::null_type>;
40 using in0 = axis::integer<int, axis::null_type, axis::option::none_t>;
41 using ing = axis::integer<double, axis::null_type,
42                           decltype(axis::option::growth | axis::option::underflow |
43                                    axis::option::overflow)>;
44 using cs = axis::category<std::string, axis::null_type>;
45 using csg = axis::category<std::string, axis::null_type, axis::option::growth_t>;
46
47 struct axis2d {
48   auto size() const { return axis::index_type{2}; }
49
50   auto index(const std::tuple<double, double>& xy) const {
51     const auto x = std::get<0>(xy);
52     const auto y = std::get<1>(xy);
53     const auto r = std::sqrt(x * x + y * y);
54     return std::min(static_cast<axis::index_type>(r), size());
55   }
56
57   friend std::ostream& operator<<(std::ostream& os, const axis2d&) {
58     os << "axis2d()";
59     return os;
60   }
61 };
62
63 template <class Tag>
64 void run_tests(const std::vector<int>& x, const std::vector<int>& y,
65                const std::vector<double>& w) {
66
67   // 1D simple A
68   {
69     auto h = make(Tag(), in{1, 3});
70     auto h2 = h;
71     for (auto&& xi : x) h(xi);
72     // uses 1D specialization
73     h2.fill(x);
74     BOOST_TEST_EQ(h, h2);
75   }
76
77   // 1D simple B
78   {
79     auto h = make(Tag(), in{1, 3});
80     auto h2 = h;
81     for (auto&& xi : x) h(xi);
82     // uses generic form
83     const auto vx = {x};
84     h2.fill(vx);
85     BOOST_TEST_EQ(h, h2);
86   }
87
88   // 1D simple C
89   {
90     auto h = make(Tag(), in{1, 3});
91     auto h2 = h;
92     h(1);
93     for (auto&& xi : x) h(xi);
94     // uses variant
95     boost::variant2::variant<int, std::vector<int>, std::string> v[1];
96     v[0] = 1;
97     h2.fill(v);
98     v[0] = x;
99     h2.fill(v);
100     BOOST_TEST_EQ(h, h2);
101   }
102
103   // 1D bad arguments
104   {
105     auto h = make(Tag(), in{1, 3});
106
107     int bad1[2][4];
108     boost::ignore_unused(bad1);
109     BOOST_TEST_THROWS(h.fill(bad1), std::invalid_argument);
110
111     std::vector<std::array<int, 4>> bad2;
112     boost::ignore_unused(bad2);
113     BOOST_TEST_THROWS(h.fill(bad2), std::invalid_argument);
114   }
115
116   // 1D with category axis
117   {
118     auto h = make(Tag(), cs{"A", "B"});
119     auto h2 = h;
120
121     const auto s = {"A", "B", "C"};
122     for (auto&& si : s) h(si);
123     h2.fill(s);
124
125     variant<int, std::string, std::vector<std::string>> v[1];
126     h("B");
127     v[0] = "B";
128     h2.fill(v);
129
130     v[0] = std::vector<std::string>(s.begin(), s.end());
131     for (auto&& si : s) h(si);
132     h2.fill(v);
133
134     BOOST_TEST_EQ(h, h2);
135   }
136
137   // 1D weight
138   {
139     auto h = make(Tag(), in{1, 3});
140     auto h2 = h;
141
142     for (auto&& xi : x) h(weight(2), xi);
143     h2.fill(weight(2), x);
144
145     for (unsigned i = 0; i < ndata; ++i) h(weight(w[i]), x[i]);
146     h2.fill(weight(w), x);
147
148     BOOST_TEST_EQ(h, h2);
149
150     auto w2 = {1};
151     boost::ignore_unused(w2);
152     BOOST_TEST_THROWS(h2.fill(x, weight(w2)), std::invalid_argument);
153   }
154
155   // 2D simple
156   {
157     auto h = make(Tag(), in{1, 3}, in0{1, 5});
158     auto h2 = h;
159
160     for (int i = 0; i < ndata; ++i) h(x[i], y[i]);
161     const auto xy = {x, y};
162     h2.fill(xy);
163
164     BOOST_TEST_EQ(h, h2);
165
166     // wrong rank
167     BOOST_TEST_THROWS(h.fill(x), std::invalid_argument);
168
169     // not rectangular
170     std::array<std::vector<int>, 2> bad = {{std::vector<int>(1), std::vector<int>(2)}};
171     boost::ignore_unused(bad);
172     BOOST_TEST_THROWS(h2.fill(bad), std::invalid_argument);
173   }
174
175   // 2D variant and weight
176   {
177     auto h = make(Tag(), in{1, 3}, in0{1, 5});
178
179     using V = variant<int, std::vector<int>, std::string>;
180     V xy[2];
181
182     {
183       xy[0] = 3;
184       xy[1] = y;
185       auto h1 = h;
186       auto h2 = h;
187       for (auto&& vi : y) h1(3, vi);
188       h2.fill(xy);
189       BOOST_TEST_EQ(h1, h2);
190     }
191
192     {
193       xy[0] = x;
194       xy[1] = 3;
195       auto h1 = h;
196       auto h2 = h;
197       for (auto&& vi : x) h1(vi, 3);
198       h2.fill(xy);
199       BOOST_TEST_EQ(h1, h2);
200     }
201
202     {
203       xy[0] = 3;
204       xy[1] = y;
205       auto h1 = h;
206       auto h2 = h;
207       for (auto&& vi : y) h1(3, vi, weight(2));
208       h2.fill(xy, weight(2));
209       BOOST_TEST_EQ(h1, h2);
210     }
211
212     {
213       xy[0] = 3;
214       xy[1] = y;
215       auto h1 = h;
216       auto h2 = h;
217       for (unsigned i = 0; i < ndata; ++i) h1(3, y[i], weight(w[i]));
218       h2.fill(xy, weight(w));
219       BOOST_TEST_EQ(sum(h1), sum(h2));
220       BOOST_TEST_EQ(h1, h2);
221     }
222   }
223
224   // 1D growing
225   {
226     auto h = make(Tag(), ing());
227     auto h2 = h;
228     for (const auto& xi : x) h(xi);
229     h2.fill(x);
230     BOOST_TEST_EQ(h, h2);
231   }
232
233   // 2D growing A
234   {
235     auto h = make(Tag(), in(1, 3), ing());
236     auto h2 = h;
237     for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]);
238     const auto xy = {x, y};
239     h2.fill(xy);
240     BOOST_TEST_EQ(h, h2);
241   }
242
243   // 2D growing B
244   {
245     auto h = make(Tag(), ing(), ing());
246     auto h2 = h;
247     for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]);
248     const auto xy = {x, y};
249     h2.fill(xy);
250     BOOST_TEST_EQ(h, h2);
251   }
252
253   // 2D growing with weights A
254   {
255     auto h = make(Tag(), in(1, 3), ing());
256     auto h2 = h;
257     for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(w[i]));
258     const auto xy = {x, y};
259     h2.fill(xy, weight(w));
260     BOOST_TEST_EQ(h, h2);
261   }
262
263   // 2D growing with weights B
264   {
265     auto h = make(Tag(), ing(), ing());
266     auto h2 = h;
267     for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(2));
268     const auto xy = {x, y};
269     h2.fill(xy, weight(2));
270     BOOST_TEST_EQ(h, h2);
271   }
272
273   // 2D growing and variant
274   {
275     auto h = make(Tag(), csg{}, in{1, 2});
276     auto h2 = h;
277
278     h("foo", 1);
279     h("foo", 2);
280
281     using V = variant<std::string, std::vector<std::string>, int, std::vector<int>>;
282     const auto xy = {V("foo"), V(std::vector<int>{1, 2})};
283     h2.fill(xy);
284
285     BOOST_TEST_EQ(h, h2);
286
287     const auto bad = {V(std::vector<std::string>(1, "foo")), V(std::vector<int>{1, 2})};
288     boost::ignore_unused(bad);
289     BOOST_TEST_THROWS(h.fill(bad), std::invalid_argument);
290   }
291
292   // 1D profile with samples
293   {
294     auto h = make_s(Tag(), profile_storage(), in(1, 3));
295     auto h2 = h;
296
297     for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]));
298     for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(w[i]));
299     for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(2), weight(w[i]));
300     for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(2));
301
302     h2.fill(x, sample(w));
303     h2.fill(x, sample(w), weight(w));
304     h2.fill(x, sample(2), weight(w));
305     h2.fill(x, sample(w), weight(2));
306
307     BOOST_TEST_EQ(h, h2);
308   }
309
310   // 2D weighted profile with samples and weights
311   {
312     auto h = make_s(Tag(), weighted_profile_storage(), in(1, 3), in0(1, 3));
313     auto h2 = h;
314
315     for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(w[i]));
316     for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(2), weight(w[i]));
317     for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(2));
318
319     using V = variant<int, std::vector<int>>;
320     std::array<V, 2> xy;
321     xy[0] = x;
322     xy[1] = 3;
323     h2.fill(xy, sample(w), weight(w));
324     h2.fill(xy, sample(2), weight(w));
325     h2.fill(xy, sample(w), weight(2));
326
327     BOOST_TEST_EQ(h, h2);
328   }
329
330   // axis2d
331   {
332     auto h = make(Tag(), axis2d{});
333     auto h2 = h;
334
335     std::vector<std::tuple<double, double>> xy;
336     xy.reserve(ndata);
337     for (unsigned i = 0; i < ndata; ++i) xy.emplace_back(x[i], y[i]);
338
339     for (auto&& xyi : xy) h(xyi);
340     h2.fill(xy);
341
342     BOOST_TEST_EQ(h, h2);
343   }
344 }
345
346 int main() {
347   std::mt19937 gen(1);
348   std::normal_distribution<> id(0, 2);
349   std::vector<int> x(ndata), y(ndata);
350   auto generator = [&] { return static_cast<int>(id(gen)); };
351   std::generate(x.begin(), x.end(), generator);
352   std::generate(y.begin(), y.end(), generator);
353   std::vector<double> w(ndata);
354   // must be all positive
355   std::generate(w.begin(), w.end(), [&] { return 0.5 + std::abs(id(gen)); });
356
357   run_tests<static_tag>(x, y, w);
358   run_tests<dynamic_tag>(x, y, w);
359
360   return boost::report_errors();
361 }