1 /***************************************************************************
2 * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht          *
3 * Copyright (c) QuantStack                                                 *
4 *                                                                          *
5 * Distributed under the terms of the BSD 3-Clause License.                 *
6 *                                                                          *
7 * The full license is in the file LICENSE, distributed with this software. *
8 ****************************************************************************/
9 
10 #include "test_common_macros.hpp"
11 #include "test_common_macros.hpp"
12 #if (defined(__GNUC__) && !defined(__clang__))
13 #pragma GCC diagnostic push
14 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
15 #pragma GCC diagnostic ignored "-Wconversion"
16 #pragma GCC diagnostic ignored "-Wfloat-conversion"
17 #include "xtensor/xmath.hpp"
18 #pragma GCC diagnostic pop
19 #else
20 #include "xtensor/xmath.hpp"
21 #endif
22 #include "xtensor/xutils.hpp"
23 #include "xtensor/xfixed.hpp"
24 #include "xtensor/xbuilder.hpp"
25 #include "xtensor/xreducer.hpp"
26 #include "xtensor/xview.hpp"
27 #include "xtensor/xmanipulation.hpp"
28 #include "xtensor/xarray.hpp"
29 #include "xtensor/xtensor.hpp"
30 #include "xtensor/xrandom.hpp"
31 #include "xtensor/xoptional.hpp"
32 #include "xtensor/xoptional_assembly.hpp"
33 #include "xtensor/xio.hpp"
34 
35 namespace xt
36 {
37 
38 #define CHECK_RESULT_TYPE(EXPRESSION, EXPECTED_TYPE)                             \
39 {                                                                                \
40     using result_type = typename std::decay_t<decltype(EXPRESSION)>::value_type; \
41     EXPECT_TRUE((std::is_same<result_type, EXPECTED_TYPE>::value));              \
42 }
43 
44 #define CHECK_TAG_TYPE(EXPRESSION, EXPECTED_TYPE)                                    \
45 {                                                                                    \
46     using result_type = typename std::decay_t<decltype(EXPRESSION)>::expression_tag; \
47     EXPECT_TRUE((std::is_same<result_type, EXPECTED_TYPE>::value));                  \
48 }
49 
50 #define CHECK_TYPE(VALUE, EXPECTED_TYPE)                                         \
51 {                                                                                \
52     using result_type = typename std::decay_t<decltype(VALUE)>;                  \
53     EXPECT_TRUE((std::is_same<result_type, EXPECTED_TYPE>::value));              \
54 }
55 
56     struct xreducer_features
57     {
58         using axes_type = std::array<std::size_t, 2>;
59         using shape_type = xarray<double>::shape_type;
60         using xarray_type = xarray<double>;
61         using func = xreducer_functors<std::plus<double>>;
62 
63         axes_type m_axes;
64 
65         xarray_type m_a;
66 
67         xreducer<func, const xarray_type&, axes_type, xt::reducer_options<double, std::tuple<xt::evaluation_strategy::lazy_type>>> m_red;
68 
69         xreducer_features();
70     };
71 
xreducer_features()72     xreducer_features::xreducer_features()
73         : m_axes({1, 3}), m_a(ones<double>({3, 2, 4, 6, 5})),
74           m_red(func(), m_a, m_axes, xt::evaluation_strategy::lazy)
75     {
76         for (std::size_t i = 0; i < 2; ++i)
77         {
78             for (std::size_t j = 0; j < 6; ++j)
79             {
80                 m_a(1, i, 1, j, 1) = 2;
81             }
82         }
83     }
84 
85    struct xreducer_opt_features
86     {
87         using axes_type = std::array<std::size_t, 2>;
88         using shape_type = typename xarray_optional<double>::shape_type;
89 
90         using xarray_of_optional_type = xarray<xtl::xoptional<double>>;
91         using xarray_optional_type = xarray_optional<double>;
92         using optional_assembly_type = xoptional_assembly<xarray<double>, xarray<bool>>;
93 
94         axes_type m_axes;
95 
96         xarray_of_optional_type m_array_of_optional, m_simple_array_of_optional;
97 
98         xarray_optional_type m_array_optional, m_simple_array_optional;
99 
100         optional_assembly_type m_optional_assembly, m_simple_optional_assembly;
101 
xreducer_opt_featuresxt::xreducer_opt_features102         xreducer_opt_features():
103             m_axes({1, 3}),
104             m_array_of_optional(ones<xtl::xoptional<double>>({3, 2, 4, 6, 5})),
105             m_array_optional(ones<xtl::xoptional<double>>({3, 2, 4, 6, 5})),
106             m_optional_assembly(ones<xtl::xoptional<double>>({3, 2, 4, 6, 5}))
107         {
108             for (std::size_t i = 0; i < 2; ++i)
109             {
110                 for (std::size_t j = 0; j < 6; ++j)
111                 {
112                     m_array_of_optional(1, i, 1, j, 1) = 2;
113                     m_array_optional(1, i, 1, j, 1) = 2;
114                     m_optional_assembly(1, i, 1, j, 1) = 2;
115                 }
116             }
117             m_array_of_optional(0, 0, 0, 0, 0) = xtl::missing<double>();
118             m_array_optional(0, 0, 0, 0, 0) = xtl::missing<double>();
119             m_optional_assembly(0, 0, 0, 0, 0) = xtl::missing<double>();
120 
121             m_simple_array_of_optional = xarray_of_optional_type({{1, 2, 0}, {4, 8, xtl::missing<double>()}});
122             m_simple_array_optional = xarray_optional_type({{1, 2, 0}, {4, 8, xtl::missing<double>()}});
123             m_simple_optional_assembly = optional_assembly_type({{1, 2, 0}, {4, 8, xtl::missing<double>()}});
124         }
125     };
126 
127 #define TEST_EXPRESSION_TAG(INPUT, EXPECTED)   \
128     auto res = xt::sum(INPUT, feats.m_axes);   \
129     CHECK_TAG_TYPE(res, EXPECTED);
130 
TEST(xreducer,expression_tag)131     TEST(xreducer, expression_tag)
132     {
133         xreducer_features feats;
134         TEST_EXPRESSION_TAG(feats.m_a, xtensor_expression_tag);
135     }
136 
TEST(xreducer_array_of_optional,expression_tag)137     TEST(xreducer_array_of_optional, expression_tag)
138     {
139         xreducer_opt_features feats;
140         TEST_EXPRESSION_TAG(feats.m_array_of_optional, xtensor_expression_tag);
141     }
142 
TEST(xreducer_array_optional,expression_tag)143     TEST(xreducer_array_optional, expression_tag)
144     {
145         xreducer_opt_features feats;
146         TEST_EXPRESSION_TAG(feats.m_array_optional, xoptional_expression_tag);
147     }
148 
TEST(xreducer_optional_assembly,expression_tag)149     TEST(xreducer_optional_assembly, expression_tag)
150     {
151         xreducer_opt_features feats;
152         TEST_EXPRESSION_TAG(feats.m_optional_assembly, xoptional_expression_tag);
153     }
154 #undef TEST_EXPRESSION_TAG
155 
156 #define TEST_VALUE_HAS_VALUE(INPUT, V_TYPE, OPTIONAL)                                      \
157     using result_type = std::conditional_t<OPTIONAL, xtl::xoptional<double>, double>;      \
158                                                                                            \
159     auto res = xt::sum(INPUT, feats.m_axes);                                               \
160     CHECK_RESULT_TYPE(res, result_type);                                                   \
161     CHECK_TYPE(xt::value(res)(1, 1, 1), V_TYPE);                                           \
162     EXPECT_EQ(!OPTIONAL, xt::has_value(res)(0, 0, 0));                                     \
163     EXPECT_TRUE(xt::has_value(res)(1, 1, 1));
164 
TEST(xreducer,value_has_value)165     TEST(xreducer, value_has_value)
166     {
167         xreducer_features feats;
168         TEST_VALUE_HAS_VALUE(feats.m_a, double, false);
169     }
170 /*
171     TEST(xreducer_array_of_optional, value_has_value)
172     {
173         xreducer_opt_features feats;
174         TEST_VALUE_HAS_VALUE(feats.m_array_of_optional, xtl::xoptional<double>, true);  // TODO: fail, the mask is not reflecting the missing values
175     }
176 */
TEST(xreducer_array_optional,value_has_value)177     TEST(xreducer_array_optional, value_has_value)
178     {
179        xreducer_opt_features feats;
180        TEST_VALUE_HAS_VALUE(feats.m_array_optional, double, true);
181     }
182 
TEST(xreducer_optional_assembly,value_has_value)183     TEST(xreducer_optional_assembly, value_has_value)
184     {
185         xreducer_opt_features feats;
186         TEST_VALUE_HAS_VALUE(feats.m_optional_assembly, double, true);
187     }
188 
189 #undef TEST_VALUE_HAS_VALUE
190 
TEST(xreducer,functor_type)191     TEST(xreducer, functor_type)
192     {
193         auto sum = [](auto const& left, auto const& right) { return left + right; };
194         auto sum_functor = xt::make_xreducer_functor(sum);
195         xt::xarray<int> a = {{1, 2, 3}, {4, 5, 6}};
196         xt::xarray<int> a_sums = xt::reduce(std::move(sum_functor), a, {1});
197         xt::xarray<int> a_sums2 = xt::reduce(sum_functor, a, {1});
198         xt::xarray<int> expect = {6, 15};
199         EXPECT_EQ(a_sums, expect);
200         EXPECT_EQ(a_sums2, expect);
201 
202         xt::xarray<int> a_sums3 = xt::reduce(sum, a, {1});
203         EXPECT_EQ(a_sums3, expect);
204 
205     }
206 
TEST(xreducer,errors)207     TEST(xreducer, errors)
208     {
209         xt::xarray<int> a = {{1, 2, 3}, {4, 5, 6}};
210         XT_EXPECT_THROW(xt::sum(a, {1, 0}), std::runtime_error);
211         XT_EXPECT_THROW(xt::sum(a, {0, 2}), std::runtime_error);
212         XT_EXPECT_THROW(xt::sum(a, {1, 0}, evaluation_strategy::immediate), std::runtime_error);
213         XT_EXPECT_THROW(xt::sum(a, {0, 2}, evaluation_strategy::immediate), std::runtime_error);
214     }
215 
TEST(xreducer,shape)216     TEST(xreducer, shape)
217     {
218         xreducer_features features;
219         xreducer_features::shape_type s = {3, 4, 5};
220         EXPECT_EQ(s, features.m_red.shape());
221         EXPECT_EQ(features.m_red.layout(), layout_type::dynamic);
222     }
223 
TEST(xreducer,access)224     TEST(xreducer, access)
225     {
226         xreducer_features features;
227         EXPECT_EQ(12, features.m_red(0, 0, 0));
228         EXPECT_EQ(24, features.m_red(1, 1, 1));
229         EXPECT_EQ(features.m_red(0, 1), features.m_red(0, 0, 1));
230         EXPECT_EQ(features.m_red(1, 2, 1, 0, 1), features.m_red(1, 0, 1));
231 
232         EXPECT_EQ(12, features.m_red(2, 0, 0, 0));
233         EXPECT_EQ(12, features.m_red());
234     }
235 
TEST(xreducer,unchecked)236     TEST(xreducer, unchecked)
237     {
238         xreducer_features features;
239         EXPECT_EQ(12, features.m_red.unchecked(0, 0, 0));
240         EXPECT_EQ(24, features.m_red.unchecked(1, 1, 1));
241     }
242 
TEST(xreducer,indexed_access)243     TEST(xreducer, indexed_access)
244     {
245         xreducer_features features;
246         EXPECT_EQ(12, (features.m_red[{0, 0, 0}]));
247         EXPECT_EQ(24, (features.m_red[{1, 1, 1}]));
248     }
249 
TEST(xreducer,at)250     TEST(xreducer, at)
251     {
252         xreducer_features features;
253         EXPECT_EQ(12, features.m_red.at(0, 0, 0));
254         EXPECT_EQ(24, features.m_red.at(1, 1, 1));
255         XT_EXPECT_ANY_THROW(features.m_red.at(10, 10, 10));
256         XT_EXPECT_ANY_THROW(features.m_red.at(0, 0, 0, 0));
257     }
258 
TEST(xreducer,iterator)259     TEST(xreducer, iterator)
260     {
261         xreducer_features features;
262         auto iter = features.m_red.cbegin();
263         auto iter_end = features.m_red.cend();
264         const xreducer_features::shape_type& s = features.m_red.shape();
265         std::size_t nb_iter = 1;
266         nb_iter = std::accumulate(s.cbegin(), s.cend(), nb_iter, std::multiplies<std::size_t>());
267         std::advance(iter, static_cast<std::ptrdiff_t>(nb_iter));
268         EXPECT_EQ(iter_end, iter);
269     }
270 
TEST(xreducer,assign)271     TEST(xreducer, assign)
272     {
273         xreducer_features features;
274         xarray<double> res = features.m_red;
275         xarray<double> expected = 12 * ones<double>({3, 4, 5});
276         expected(1, 1, 1) = 24;
277         EXPECT_EQ(expected, res);
278 
279         xarray_optional<double> opt_expected = 12 * ones<double>({3, 4, 5});
280         opt_expected(1, 1, 1) = 24;
281 
282 // TODO: fix the reducer assignment issue in multithreaded env and enable
283 // these tests again.
284 #if !defined(XTENSOR_USE_TBB) && !defined(XTENSOR_USE_OPENMP)
285         xreducer_opt_features::xarray_of_optional_type opt_res1 = res;
286         CHECK_RESULT_TYPE(opt_res1, xtl::xoptional<double>);
287         CHECK_TYPE(xt::value(opt_res1)(1, 1, 1), xtl::xoptional<double>);
288         EXPECT_EQ(xt::has_value(opt_res1), xt::full_like(res, true));
289         EXPECT_EQ(opt_expected, opt_res1);
290 
291         xreducer_opt_features::xarray_optional_type opt_res2 = res;
292         CHECK_RESULT_TYPE(opt_res2, xtl::xoptional<double>);
293         CHECK_TYPE(xt::value(opt_res2)(1, 1, 1), double);
294         EXPECT_EQ(xt::has_value(opt_res2), xt::full_like(res, true));
295         EXPECT_EQ(opt_expected, opt_res2);
296 
297         xreducer_opt_features::optional_assembly_type opt_res3 = res;
298         CHECK_RESULT_TYPE(opt_res3, xtl::xoptional<double>);
299         CHECK_TYPE(xt::value(opt_res3)(1, 1, 1), double);
300         EXPECT_EQ(xt::has_value(opt_res3), xt::full_like(res, true));
301         EXPECT_EQ(opt_expected, opt_res3);
302 #endif
303     }
304 
305 #define TEST_OPT_ASSIGNMENT(INPUT)                               \
306     auto res = xt::sum(INPUT, feats.m_axes);                     \
307                                                                  \
308     xreducer_opt_features::xarray_of_optional_type res1 = res;   \
309     CHECK_RESULT_TYPE(res1, xtl::xoptional<double>);             \
310     CHECK_TYPE(xt::value(res1)(1, 1, 1), xtl::xoptional<double>);\
311     EXPECT_EQ(res1(1, 1, 1), 24.);                               \
312     /* EXPECT_FALSE(xt::has_value(res1)(0, 0, 0)); */            \
313     EXPECT_TRUE(xt::has_value(res1)(1, 1, 1));                   \
314                                                                  \
315     xreducer_opt_features::xarray_optional_type res2 = res;      \
316     CHECK_RESULT_TYPE(res2, xtl::xoptional<double>);             \
317     CHECK_TYPE(xt::value(res2)(1, 1, 1), double);                \
318     EXPECT_EQ(res2(1, 1, 1), 24.);                               \
319     EXPECT_FALSE(xt::has_value(res2)(0, 0, 0));                  \
320     EXPECT_TRUE(xt::has_value(res2)(1, 1, 1));                   \
321                                                                  \
322     xreducer_opt_features::optional_assembly_type res3 = res;    \
323     CHECK_RESULT_TYPE(res3, xtl::xoptional<double>);             \
324     CHECK_TYPE(xt::value(res3)(1, 1, 1), double);                \
325     EXPECT_EQ(res3(1, 1, 1), 24.);                               \
326     EXPECT_FALSE(xt::has_value(res3)(0, 0, 0));                  \
327     EXPECT_TRUE(xt::has_value(res3)(1, 1, 1));
328 
TEST(xreducer_array_of_optional,assign)329     TEST(xreducer_array_of_optional, assign)
330     {
331         xreducer_opt_features feats;
332         TEST_OPT_ASSIGNMENT(feats.m_array_of_optional)
333     }
334 
TEST(xreducer_array_optional,assign)335     TEST(xreducer_array_optional, assign)
336     {
337         xreducer_opt_features feats;
338         TEST_OPT_ASSIGNMENT(feats.m_array_optional)
339     }
340 
TEST(xreducer_optional_assembly,assign)341     TEST(xreducer_optional_assembly, assign)
342     {
343         xreducer_opt_features feats;
344         TEST_OPT_ASSIGNMENT(feats.m_optional_assembly)
345     }
346 #undef TEST_OPT_ASSIGNMENT
347 
TEST(xreducer,sum)348     TEST(xreducer, sum)
349     {
350         xreducer_features features;
351         xarray<double> res = sum(features.m_a, features.m_axes);
352         xarray<double> expected = 12 * ones<double>({3, 4, 5});
353         expected(1, 1, 1) = 24;
354         EXPECT_EQ(expected, res);
355     }
356 
357 #define TEST_OPT_SUM(INPUT)                            \
358     auto res = xt::sum(INPUT, feats.m_axes);           \
359     EXPECT_EQ(res.dimension(), std::size_t(3));        \
360     EXPECT_EQ(res(0, 0, 0), xtl::missing<double>());   \
361     EXPECT_EQ(res(1, 1, 1), 24.);
362 
TEST(xreducer_array_of_optional,sum)363     TEST(xreducer_array_of_optional, sum)
364     {
365         xreducer_opt_features feats;
366         TEST_OPT_SUM(feats.m_array_of_optional);
367     }
368 
TEST(xreducer_array_optional,sum)369     TEST(xreducer_array_optional, sum)
370     {
371         xreducer_opt_features feats;
372         TEST_OPT_SUM(feats.m_array_optional);
373     }
374 
TEST(xreducer_optional_assembly,sum)375     TEST(xreducer_optional_assembly, sum)
376     {
377         xreducer_opt_features feats;
378         TEST_OPT_SUM(feats.m_optional_assembly);
379     }
380 #undef TEST_OPT_SUM
381 
TEST(xreducer,sum_tensor)382     TEST(xreducer, sum_tensor)
383     {
384         xtensor<double, 2> m = {{1, 2}, {3, 4}};
385         xarray<double> res = xt::sum(m, {0});
386         EXPECT_EQ(res.dimension(), std::size_t(1));
387         EXPECT_EQ(res(0), 4.0);
388         EXPECT_EQ(res(1), 6.0);
389     }
390 
TEST(xreducer,single_axis_sugar)391     TEST(xreducer, single_axis_sugar)
392     {
393         xarray<double> m = {{1, 0}, {3, 4}};
394 
395         xarray<std::size_t> res1 = xt::count_nonzero(m, {1});
396         xarray<std::size_t> res2 = xt::count_nonzero(m, 1);
397         EXPECT_EQ(res1, res2);
398 
399         xarray<double> res3 = xt::sum(m, {1});
400         xarray<double> res4 = xt::sum(m, 1);
401         EXPECT_EQ(res3, res4);
402     }
403 
404 #define TEST_OPT_SINGLE_AXIS(INPUT)                                    \
405     auto res = xt::sum(INPUT, feats.m_axes);                           \
406     xarray_optional<std::size_t> res1 = xt::count_nonzero(INPUT, {1}); \
407     xarray_optional<std::size_t> res2 = xt::count_nonzero(INPUT, 1);   \
408     EXPECT_EQ(res1, res2);                                             \
409                                                                        \
410     xarray_optional<double> res3 = xt::sum(INPUT, {1});                \
411     xarray_optional<double> res4 = xt::sum(INPUT, 1);                  \
412     EXPECT_EQ(res3, res4);
413 
TEST(xreducer_array_of_optional,single_axis_sugar)414     TEST(xreducer_array_of_optional, single_axis_sugar)
415     {
416         xreducer_opt_features feats;
417         TEST_OPT_SINGLE_AXIS(feats.m_array_of_optional);
418     }
419 
TEST(xreducer_array_optional,single_axis_sugar)420     TEST(xreducer_array_optional, single_axis_sugar)
421     {
422         xreducer_opt_features feats;
423         TEST_OPT_SINGLE_AXIS(feats.m_array_optional);
424     }
425 
TEST(xreducer_optional_assembly,single_axis_sugar)426     TEST(xreducer_optional_assembly, single_axis_sugar)
427     {
428         xreducer_opt_features feats;
429         TEST_OPT_SINGLE_AXIS(feats.m_optional_assembly);
430     }
431 #undef TEST_OPT_SINGLE_AXIS
432 
TEST(xreducer,sum2)433     TEST(xreducer, sum2)
434     {
435         xarray<double> u = ones<double>({2, 4});
436         xarray<double> expectedu0 = 2 * ones<double>({4});
437         xarray<double> resu0 = sum(u, {0});
438         EXPECT_EQ(expectedu0, resu0);
439         xarray<double> expectedu1 = 4 * ones<double>({2});
440         xarray<double> resu1 = sum(u, {std::size_t(1)});
441         xarray<double> resm1 = sum(u, {-1});
442 
443         std::array<std::size_t, 1> a_us = {1};
444         std::array<std::ptrdiff_t, 1> a_ss = {-1};
445         xarray<double> res_refu1 = sum(u, a_us);
446         xarray<double> res_refm1 = sum(u, a_ss);
447 
448         EXPECT_EQ(expectedu1, resm1);
449         EXPECT_EQ(expectedu1, resu1);
450         EXPECT_EQ(expectedu1, res_refm1);
451         EXPECT_EQ(expectedu1, res_refu1);
452 
453         xarray<double> v = ones<double>({4, 2});
454         xarray<double> expectedv0 = 4 * ones<double>({2});
455         xarray<double> resv0 = sum(v, {0});
456         EXPECT_EQ(expectedv0, resv0);
457         xarray<double> expectedv1 = 2 * ones<double>({4});
458         xarray<double> resv1 = sum(v, {1});
459         EXPECT_EQ(expectedv1, resv1);
460 
461         // check that there is no overflow
462         xarray<uint8_t> c = ones<uint8_t>({1000});
463         EXPECT_EQ(1000u, sum(c)());
464     }
465 
TEST(xreducer,sum_all)466     TEST(xreducer, sum_all)
467     {
468         xreducer_features features;
469         auto res = sum(features.m_a);
470         double expected = 732;
471         EXPECT_EQ(res(), expected);
472     }
473 
474 #define TEST_OPT_SUM_ALL(INPUT)               \
475     auto res = xt::sum(INPUT);                \
476     EXPECT_EQ(res(), xtl::missing<double>());
477 
TEST(xreducer_array_of_optional,sum_all)478     TEST(xreducer_array_of_optional, sum_all)
479     {
480         xreducer_opt_features feats;
481         TEST_OPT_SUM_ALL(feats.m_array_of_optional);
482     }
483 
TEST(xreducer_array_optional,sum_all)484     TEST(xreducer_array_optional, sum_all)
485     {
486         xreducer_opt_features feats;
487         TEST_OPT_SUM_ALL(feats.m_array_optional);
488     }
489 
TEST(xreducer_optional_assembly,sum_all)490     TEST(xreducer_optional_assembly, sum_all)
491     {
492         xreducer_opt_features feats;
493         TEST_OPT_SUM_ALL(feats.m_optional_assembly);
494     }
495 #undef TEST_OPT_SUM_ALL
496 
TEST(xreducer,prod)497     TEST(xreducer, prod)
498     {
499         // check that there is no overflow
500         xarray<uint8_t> c = 2 * ones<uint8_t>({34});
501         EXPECT_EQ(1ULL << 34, prod<long long>(c)());
502     }
503 
504 #define TEST_OPT_PROD(INPUT)                     \
505     auto res1 = xt::prod(INPUT);                 \
506     EXPECT_EQ(res1(), xtl::missing<double>());   \
507                                                  \
508     auto res2 = xt::prod(INPUT, feats.m_axes);   \
509     EXPECT_EQ(res2(), xtl::missing<double>());   \
510     EXPECT_EQ(res2(1, 1, 1), 4096.);
511 
TEST(xreducer_array_of_optional,prod)512     TEST(xreducer_array_of_optional, prod)
513     {
514         xreducer_opt_features feats;
515         TEST_OPT_PROD(feats.m_array_of_optional);
516     }
517 
TEST(xreducer_array_optional,prod)518     TEST(xreducer_array_optional, prod)
519     {
520         xreducer_opt_features feats;
521         TEST_OPT_PROD(feats.m_array_optional);
522     }
523 
TEST(xreducer_optional_assembly,prod)524     TEST(xreducer_optional_assembly, prod)
525     {
526         xreducer_opt_features feats;
527         TEST_OPT_PROD(feats.m_optional_assembly);
528     }
529 #undef TEST_OPT_PROD
530 
TEST(xreducer,mean)531     TEST(xreducer, mean)
532     {
533         xtensor<double, 2> input
534             {{-1.0, 0.0}, {1.0, 0.0}};
535         auto mean_all = mean(input);
536         auto mean0 = mean(input, {0});
537         auto mean1 = mean(input, {1});
538 
539         xtensor<double, 0> expect_all = 0.0;
540         xtensor<double, 1> expect0 = {0.0, 0.0};
541         xtensor<double, 1> expect1 = {-0.5, 0.5};
542 
543         EXPECT_EQ(mean_all(), expect_all());
544         EXPECT_TRUE(all(equal(mean0, expect0)));
545         EXPECT_TRUE(all(equal(mean1, expect1)));
546 
547 #ifndef SKIP_ON_WERROR
548         // may loose precision because uint8_t is casted to long for intermediate
549         // computations and then divided by a double for mean
550         xarray<uint8_t> c = {1, 2};
551         EXPECT_EQ(mean(c)(), 1.5);
552 #endif
553 
554         const auto rvalue_xarray = [] () { return xtensor<double, 1>({1, 2}); };
555         EXPECT_EQ(mean(rvalue_xarray(), {0})(), 1.5);
556     }
557 
558 #define TEST_OPT_MEAN(INPUT)                      \
559     auto res1 = xt::mean(INPUT);                  \
560     EXPECT_EQ(res1(), xtl::missing<double>());    \
561                                                   \
562     auto res2 = xt::mean(INPUT, feats.m_axes);    \
563     EXPECT_EQ(res2(), xtl::missing<double>());    \
564     EXPECT_EQ(res2(1, 1, 1), 2.);
565 
TEST(xreducer_array_of_optional,mean)566     TEST(xreducer_array_of_optional, mean)
567     {
568         xreducer_opt_features feats;
569         TEST_OPT_MEAN(feats.m_array_of_optional);
570     }
571 
TEST(xreducer_array_optional,mean)572     TEST(xreducer_array_optional, mean)
573     {
574         xreducer_opt_features feats;
575         TEST_OPT_MEAN(feats.m_array_optional);
576     }
577 
TEST(xreducer_optional_assembly,mean)578     TEST(xreducer_optional_assembly, mean)
579     {
580         xreducer_opt_features feats;
581         TEST_OPT_MEAN(feats.m_optional_assembly);
582     }
583 #undef TEST_OPT_MEAN
584 
TEST(xreducer,average)585     TEST(xreducer, average)
586     {
587         xt::xtensor<float, 2> a = {{ 3, 4, 2, 1}, { 1, 1, 3, 2}};
588         xt::xarray<double> all_weights = {{1, 2, 3, 4}, { 5, 6, 7, 8}};
589         auto avg_all = xt::average(a, all_weights);
590         auto avg_all_2 = xt::average(a, all_weights, {0ul, 1ul});
591 
592         auto avg0 = xt::average(a, xt::xarray<double>{3, 9}, {0});
593         auto avg1 = xt::average(a, xt::xarray<double>{1,2,3,4}, {1});
594         auto avg_m1 = xt::average(a, xt::xarray<double>{1,2,3,4}, {-1});
595         auto avg_d1 = xt::average(a, xt::xarray<double>{1,2,3,4}, {-1}, evaluation_strategy::immediate);
596 
597         xtensor<double, 0> expect_all = 1.9166666666666667;
598         xtensor<double, 1> expect0 = {1.5, 1.75, 2.75, 1.75};
599         xtensor<double, 1> expect1 = {2.1, 2.0};
600 
601         EXPECT_TRUE(allclose(avg_all, expect_all));
602         EXPECT_TRUE(allclose(avg_all_2, expect_all));
603         EXPECT_TRUE(all(equal(avg0, expect0)));
604         EXPECT_TRUE(all(equal(avg1, expect1)));
605         EXPECT_TRUE(all(equal(avg_m1, expect1)));
606         EXPECT_TRUE(all(equal(avg_d1, expect1)));
607     }
608 
609 #define TEST_OPT_AVERAGE(INPUT)                                            \
610     xt::xarray<double> all_weights = {{3, 3, 3},                           \
611                                       {1, 1, 1}};                          \
612     auto res1 = xt::average(INPUT, all_weights);                           \
613     EXPECT_EQ(res1(), xtl::missing<double>());                             \
614                                                                            \
615     xtensor_optional<double, 1> expect = {1., 2., xtl::missing<double>()}; \
616     xt::xarray<double> weights = {2, 0};                                   \
617     auto res2 = xt::average(INPUT, weights, {0});                          \
618     EXPECT_TRUE(all(equal(res2, expect)));
619 
TEST(xreducer_array_of_optional,average)620     TEST(xreducer_array_of_optional, average)
621     {
622         xreducer_opt_features feats;
623         TEST_OPT_AVERAGE(feats.m_simple_array_of_optional);
624     }
625 
TEST(xreducer_array_optional,average)626     TEST(xreducer_array_optional, average)
627     {
628         xreducer_opt_features feats;
629         TEST_OPT_AVERAGE(feats.m_simple_array_optional);
630     }
631 
TEST(xreducer_optional_assembly,average)632     TEST(xreducer_optional_assembly, average)
633     {
634         xreducer_opt_features feats;
635         TEST_OPT_AVERAGE(feats.m_simple_optional_assembly);
636     }
637 #undef TEST_OPT_AVERAGE
638 
TEST(xreducer,count_nonzero)639     TEST(xreducer, count_nonzero)
640     {
641         xarray<double> m = {{1, 0}, {3, 4}};
642 
643         xarray<std::size_t> res0 = xt::count_nonzero(m, {0});
644         xtensor<std::size_t, 1> expect0 = {2, 1};
645         EXPECT_TRUE(all(equal(res0, expect0)));
646 
647         xarray<std::size_t> res1 = xt::count_nonzero(m, {1});
648         xtensor<std::size_t, 1> expect1 = {1, 2};
649         EXPECT_TRUE(all(equal(res1, expect1)));
650     }
651 
652 #define TEST_OPT_COUNT_NONZEROS(INPUT)             \
653     auto res0 = xt::count_nonzero(INPUT, {0});     \
654     xtensor_optional<std::size_t, 1> expect0 =     \
655             {2, 2, xtl::missing<std::size_t>()};   \
656     EXPECT_TRUE(all(equal(res0, expect0)));        \
657                                                    \
658     auto res1 = xt::count_nonzero(INPUT, {1});     \
659     xtensor_optional<std::size_t, 1> expect1 =     \
660             {2, xtl::missing<std::size_t>()};      \
661     EXPECT_TRUE(all(equal(res1, expect1)));
662 
663 /*  TODO: fix policy, missing values should lead to a missing value result
664     TEST(xreducer_array_of_optional, count_nonzero)
665     {
666         xreducer_opt_features feats;
667         TEST_OPT_COUNT_NONZEROS(feats.m_simple_array_of_optional);
668     }
669 
670     TEST(xreducer_array_optional, count_nonzero)
671     {
672         xreducer_opt_features feats;
673         TEST_OPT_COUNT_NONZEROS(feats.m_simple_array_optional);
674     }
675 
676     TEST(xreducer_optional_assembly, count_nonzero)
677     {
678         xreducer_opt_features feats;
679         TEST_OPT_COUNT_NONZEROS(feats.m_simple_optional_assembly);
680     }
681 */
682 #undef TEST_OPT_COUNT_NONZEROS
683 
TEST(xreducer,minmax)684     TEST(xreducer, minmax)
685     {
686         using A = std::array<double, 2>;
687 
688         xtensor<double, 2> input
689             {{-1.0, 0.0}, {1.0, 0.0}};
690         EXPECT_EQ(minmax(input)(), (A{-1.0, 1.0}));
691     }
692 
TEST(xreducer,immediate)693     TEST(xreducer, immediate)
694     {
695         xarray<double> a = xt::arange(27);
696         a.resize({3, 3, 3});
697 
698         xarray<double> a_lz = sum(a);
699         auto a_gd = sum(a, evaluation_strategy::immediate);
700         EXPECT_EQ(a_lz, a_gd);
701 
702         a_lz = sum(a, {1});
703         a_gd = sum(a, {1}, evaluation_strategy::immediate);
704         EXPECT_EQ(a_lz, a_gd);
705 
706         a_lz = sum(a, {0, 2});
707         a_gd = sum(a, {0, 2}, evaluation_strategy::immediate);
708         EXPECT_EQ(a_lz, a_gd);
709 
710         a_lz = sum(a, {1, 2});
711         a_gd = sum(a, {1, 2}, evaluation_strategy::immediate);
712         EXPECT_EQ(a_lz, a_gd);
713 
714         a = xt::arange(4 * 3 * 6 * 2 * 7);
715         a.resize({4, 3, 6, 2, 7});
716 
717         a_lz = sum(a);
718         a_gd = sum(a, evaluation_strategy::immediate);
719         EXPECT_EQ(a_lz, a_gd);
720 
721         a_lz = sum(a, {1});
722         a_gd = sum(a, {1}, evaluation_strategy::immediate);
723         EXPECT_EQ(a_lz, a_gd);
724 
725         a_lz = sum(a, {0, 2});
726         a_gd = sum(a, {0, 2}, evaluation_strategy::immediate);
727         EXPECT_EQ(a_lz, a_gd);
728 
729         a_lz = sum(a, {1, 2});
730         a_gd = sum(a, {1, 2}, evaluation_strategy::immediate);
731         EXPECT_EQ(a_lz, a_gd);
732 
733         a_lz = sum(a, {1, 3, 4});
734         a_gd = sum(a, {1, 3, 4}, evaluation_strategy::immediate);
735         EXPECT_EQ(a_lz, a_gd);
736 
737         a_lz = sum(a, {0, 1, 4});
738         a_gd = sum(a, {0, 1, 4}, evaluation_strategy::immediate);
739         EXPECT_EQ(a_lz, a_gd);
740 
741         a_lz = sum(a, {0, 1, 3});
742         a_gd = sum(a, {0, 1, 3}, evaluation_strategy::immediate);
743         EXPECT_EQ(a_lz, a_gd);
744 
745         a_lz = sum(a, {0, 2, 3});
746         a_gd = sum(a, {0, 2, 3}, evaluation_strategy::immediate);
747         EXPECT_EQ(a_lz, a_gd);
748 
749         a_lz = sum(a, {1, 2, 3});
750         a_gd = sum(a, {1, 2, 3}, evaluation_strategy::immediate);
751         EXPECT_EQ(a_lz, a_gd);
752 
753         xtensor<short, 3, layout_type::column_major> ct = xt::random::randint<short>({1, 5, 3});
754         EXPECT_EQ(sum(ct, {0, 2}), sum(ct, {0, 2}, evaluation_strategy::immediate));
755 
756         xtensor<short, 5, layout_type::column_major> ct2 = xt::random::randint<short>({1, 5, 1, 2, 3});
757         EXPECT_EQ(sum(ct2, {0, 1, 2}), sum(ct2, {0, 1, 2}, evaluation_strategy::immediate));
758         EXPECT_EQ(sum(ct2, {2, 3}), sum(ct2, {2, 3}, evaluation_strategy::immediate));
759         EXPECT_EQ(sum(ct2, {1, 3}), sum(ct2, {1, 3}, evaluation_strategy::immediate));
760     }
761 
TEST(xreducer,chaining_reducers)762     TEST(xreducer, chaining_reducers)
763     {
764         xt::xarray<double> a = {{ 1., 2. },
765                                 { 3., 4. }};
766 
767         auto b = a - xt::sum(a, { 0 });
768         auto c = xt::sum(b, { 0 });
769         EXPECT_EQ(c(0), -4.);
770         EXPECT_EQ(c(1), -6.);
771     }
772 
TEST(xreducer,immediate_shape)773     TEST(xreducer, immediate_shape)
774     {
775         xtensor<double, 2> c = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
776         auto xa = xt::sum(c, {0}, evaluation_strategy::immediate);
777         auto is_arr = [](const auto& c)
778         {
779             bool istrue = detail::is_array<std::decay_t<decltype(c)>>::value;
780             return istrue;
781         };
782 
783         EXPECT_TRUE(is_arr(xa.shape()));
784 
785         xtensor<double, 3> a;
786         a.resize({3, 3, 3});
787         std::iota(a.storage().begin(), a.storage().end(), 0);
788 
789         xarray<double> a_lz = sum(a);
790         auto a_gd = sum(a, evaluation_strategy::immediate);
791         EXPECT_EQ(a_lz, a_gd);
792         EXPECT_TRUE(is_arr(a_gd.shape()));
793 
794         a_lz = sum(a, {1});
795         auto a_gd_1 = sum(a, {1}, evaluation_strategy::immediate);
796         EXPECT_EQ(a_lz, a_gd_1);
797 
798         a_lz = sum(a, {0, 2});
799         auto a_gd_2 = sum(a, {0, 2}, evaluation_strategy::immediate);
800         EXPECT_EQ(a_lz, a_gd_2);
801 
802         EXPECT_TRUE(is_arr(a_gd_1.shape()));
803         EXPECT_TRUE(is_arr(a_gd_2.shape()));
804 
805         a_lz = sum(a, {1, 2});
806         a_gd_2 = sum(a, {1, 2}, evaluation_strategy::immediate);
807         EXPECT_EQ(a_lz, a_gd_2);
808     }
809 
TEST(xreducer,xfixed_reduction)810     TEST(xreducer, xfixed_reduction)
811     {
812         xtensor_fixed<double, xshape<3, 3, 3>> a;
813         std::iota(a.storage().begin(), a.storage().end(), 0);
814 
815         xtensor<double, 3> b;
816         b.resize({3, 3, 3});
817         std::iota(b.storage().begin(), b.storage().end(), 0);
818 
819         auto is_arr = [](const auto& c)
820         {
821             bool istrue = detail::is_array<std::decay_t<decltype(c)>>::value;
822             return istrue;
823         };
824 
825         xarray<double> a_lz = sum(a);
826         auto a_gd = sum(a, evaluation_strategy::immediate);
827         EXPECT_EQ(a_lz, a_gd);
828 
829         // EXPECT_TRUE(is_fixed(a_gd.shape())); // this actually evaluates to const_array
830 
831         a_lz = sum(a, xt::dynamic_shape<std::size_t>{1});
832         auto a_gd_1 = sum(a, xt::dynamic_shape<std::size_t>{1}, evaluation_strategy::immediate);
833         auto b_gd_1 = sum(b, {1}, evaluation_strategy::immediate);
834         EXPECT_EQ(a_lz, a_gd_1);
835         EXPECT_EQ(a_lz, b_gd_1);
836 
837         a_lz = sum(a, {0, 2});
838         auto a_gd_2 = sum(a, {0, 2}, evaluation_strategy::immediate);
839         auto b_gd_2 = sum(b, {0, 2}, evaluation_strategy::immediate);
840         EXPECT_EQ(a_lz, a_gd_2);
841         EXPECT_EQ(b_gd_2, a_gd_2);
842         EXPECT_EQ(a_gd_2.dimension(), std::size_t(1));
843 
844         // EXPECT_TRUE(is_arr(a_gd_1.shape()));
845         EXPECT_TRUE(is_arr(a_gd_2.shape()));
846 
847         a_lz = sum(a, {1, 2});
848         a_gd_2 = sum(a, {1, 2}, evaluation_strategy::immediate);
849         EXPECT_EQ(a_lz, a_gd_2);
850 
851         auto a_lx_3 = sum(a, {1, 2});
852         auto a_lz_3 = sum(a, xshape<1, 2>());
853         auto a_gd_3 = sum(a, xshape<1, 2>(), evaluation_strategy::immediate);
854         xarray<double> xevd = a_lz_3;
855         EXPECT_EQ(a_lz_3, a_gd_2);
856         EXPECT_TRUE(a_gd_3 == a_gd_2);
857         bool truth = std::is_same<decltype(a_gd_3), xtensor_fixed<double, xshape<3>>>::value;
858         EXPECT_TRUE(truth);
859 
860         xtensor<short, 3> ct = xt::random::randint<short>({1, 5, 3});
861         xtensor_fixed<short, xshape<1, 5, 3>> c = ct;
862         auto b_fx_1 = sum(c, xshape<0, 2>(), evaluation_strategy::immediate);
863         auto b_fx_2 = sum(c, xshape<0, 1>(), evaluation_strategy::immediate);
864         auto b_fx_3 = sum(c, xshape<0, 1, 2>(), evaluation_strategy::immediate);
865 
866         EXPECT_EQ(sum(ct, {0, 2}, evaluation_strategy::immediate), sum(c, {0, 2}));
867         EXPECT_TRUE(b_fx_1 == sum(c, {0, 2}));
868         EXPECT_TRUE(b_fx_2 == sum(c, {0, 1}));
869         EXPECT_EQ(b_fx_3, sum(c, {0, 1, 2}));
870 
871         truth = std::is_same<std::decay_t<decltype(b_fx_1)>, xtensor_fixed<int, xshape<5>>>::value;
872         EXPECT_TRUE(truth);
873         truth = std::is_same<std::decay_t<decltype(b_fx_3)>, xtensor_fixed<int, xshape<>>>::value;
874         EXPECT_TRUE(truth);
875 
876         truth = std::is_same<xshape<1, 3>, typename fixed_xreducer_shape_type<xshape<1, 5, 3>, xshape<1>>::type>();
877         EXPECT_TRUE(truth);
878         truth = std::is_same<xshape<5>, typename fixed_xreducer_shape_type<xshape<1, 5, 3>, xshape<0, 2>>::type>();
879         EXPECT_TRUE(truth);
880         truth = std::is_same<xshape<>, typename fixed_xreducer_shape_type<xshape<1, 5, 3>, xshape<0, 1, 2>>::type>();
881         EXPECT_TRUE(truth);
882         truth = std::is_same<xshape<1, 5>, typename fixed_xreducer_shape_type<xshape<1, 5>, xshape<2>>::type>();
883         EXPECT_TRUE(truth);
884     }
885 
TEST(xreducer,view_steppers)886     TEST(xreducer, view_steppers)
887     {
888         xt::xtensor<double, 2> X({10, 20});
889         xt::xtensor<double, 2> Y(X.shape());
890 
891         X = xt::random::randn<double>(X.shape());
892 
893         xt::xtensor<double, 2> vx0 = xt::view(xt::sum(X, {1}), xt::all(), xt::newaxis());
894         xt::xtensor<double, 2> vx1 = xt::expand_dims(xt::sum(X, {1}), 1);
895 
896         EXPECT_EQ(vx0, vx1);
897     }
898 
TEST(xreducer,wrong_number_of_indices)899     TEST(xreducer, wrong_number_of_indices)
900     {
901         xt::xtensor<double, 4> a = xt::random::rand<double>({5, 5, 5, 5});
902         double e = xt::sum(a)();
903         double s1 = xt::sum(a)(0);
904         EXPECT_EQ(s1, e);
905         double s2 = xt::sum(a)(0, 1, 2, 3, 4, 5, 0);
906         EXPECT_EQ(s2, e);
907 
908         auto red = xt::sum(a, {0});
909         EXPECT_EQ(red(2), red(0, 0, 2));
910         EXPECT_EQ(red(1, 2), red(0, 1, 2));
911         EXPECT_EQ(red(1, 2), red(1, 1, 1, 1, 1, 0, 1, 2));
912     }
913 
TEST(xreducer,normalize_axes)914     TEST(xreducer, normalize_axes)
915     {
916         xt::xtensor<double, 4> x{};
917         std::vector<std::size_t> sva {0, 1, 2, 3};
918         std::vector<std::ptrdiff_t> svb = {-4, 1, -2, -1};
919         std::initializer_list<std::ptrdiff_t> initlist = {-4, 1, -2, -1};
920         std::array<std::size_t, 4> saa = {0, 1, 2, 3};
921         std::array<std::ptrdiff_t, 4> sab = {-4, 1, -2, -1};
922 
923         auto resa = forward_normalize<std::vector<std::size_t>>(x, svb);
924         EXPECT_EQ(forward_normalize<std::vector<std::size_t>>(x, svb), sva);
925         EXPECT_EQ(forward_normalize<std::vector<std::size_t>>(x, initlist), sva);
926         auto resaa = forward_normalize<std::array<std::size_t, 4>>(x, saa);
927         EXPECT_EQ(resaa, saa);
928         auto resab = forward_normalize<std::array<std::size_t, 4>>(x, sab);
929         EXPECT_EQ(resab, saa);
930     }
931 
TEST(xreducer,input_0d)932     TEST(xreducer, input_0d)
933     {
934         xt::xarray<double> a;
935         EXPECT_EQ(0., xt::amin(a)[0]);
936 
937         using A = std::array<double, 2>;
938         xt::xarray<double> b(1.2);
939         EXPECT_EQ(b.dimension(), 0u);
940         EXPECT_EQ(minmax(b)(), (A{1.2, 1.2}));
941     }
942 
943     template <std::size_t... I, std::size_t... J>
operator ==(fixed_shape<I...>,fixed_shape<J...>)944     bool operator==(fixed_shape<I...>, fixed_shape<J...>)
945     {
946         std::array<std::size_t, sizeof...(I)> ix = {I...};
947         std::array<std::size_t, sizeof...(J)> jx = {J...};
948         return sizeof...(J) == sizeof...(I) && std::equal(ix.begin(), ix.end(), jx.begin());
949     }
950 
TEST(xreducer,keep_dims)951     TEST(xreducer, keep_dims)
952     {
953         xt::xtensor<double, 4> a = xt::reshape_view(xt::arange<double>(5 * 5 * 5 * 5), {5, 5, 5, 5});
954 
955         auto res = xt::sum(a, {0, 1}, xt::keep_dims | xt::evaluation_strategy::immediate);
956         EXPECT_EQ(res.shape(), (std::array<std::size_t, 4>{1, 1, 5, 5}));
957         auto res2 = xt::sum(a, {0, 1}, xt::keep_dims);
958         EXPECT_EQ(res2.shape(), (std::array<std::size_t, 4>{1, 1, 5, 5}));
959 
960         xt::xarray<double> b = a;
961         auto res3 = xt::sum(b, {0, 1}, xt::keep_dims | xt::evaluation_strategy::immediate);
962         EXPECT_EQ(res3.shape(), (xt::dynamic_shape<std::size_t>{1, 1, 5, 5}));
963         auto res4 = xt::sum(b, {0, 1}, xt::keep_dims | xt::evaluation_strategy::lazy);
964         EXPECT_EQ(res4.shape(), (xt::dynamic_shape<std::size_t>{1, 1, 5, 5}));
965 
966         xt::xarray<double> resx3 = xt::sum(a, {0, 1});
967         auto exp1 = xt::sum(a, {0, 1});
968 
969         EXPECT_EQ(res, res2);
970         EXPECT_EQ(res, res3);
971         EXPECT_EQ(res, res4);
972 
973         EXPECT_TRUE(xt::allclose(res, res2));
974         EXPECT_TRUE(xt::allclose(res, res3));
975         EXPECT_TRUE(xt::allclose(res, res4));
976 
977         auto res5 = xt::sum(a, xt::keep_dims);
978         EXPECT_EQ(res5.shape(), (xt::static_shape<size_t, 4>{1, 1, 1, 1}));
979         auto res6 = xt::sum(a);
980         EXPECT_EQ(res6.shape(), (xt::static_shape<size_t, 0>{}));
981         xt::xtensor_fixed<double, xshape<5, 5, 5, 5>> c = a;
982         auto res7 = xt::sum(c);
983         EXPECT_EQ(res7.shape(), (xt::xshape<>{}));
984         auto res8 = xt::sum(c, xt::keep_dims);
985         EXPECT_EQ(res8.shape(), (xt::xshape<1, 1, 1, 1>{}));
986     }
987 
TEST(xreducer,initial_value)988     TEST(xreducer, initial_value)
989     {
990         xt::xtensor<double, 4> a = xt::reshape_view(xt::arange<double>(5 * 5 * 5 * 5), {5, 5, 5, 5});
991 
992         auto res = xt::sum(a, {0, 2}, xt::keep_dims | xt::evaluation_strategy::immediate | initial(5));
993         auto reso = xt::sum(a, {0, 2}, xt::keep_dims | xt::evaluation_strategy::immediate);
994         EXPECT_EQ(res, reso + 5);
995 
996         xt::xarray<double> res2 = xt::sum(a, {0, 2}, xt::keep_dims | initial(5));
997         auto reso2 = xt::sum(a, {0, 2}, xt::keep_dims);
998         EXPECT_EQ(res2, reso2 + 5);
999 
1000         auto re0 = xt::prod(a, {1, 2}, xt::keep_dims | xt::evaluation_strategy::immediate | initial(0));
1001         EXPECT_TRUE(xt::all(equal(re0, 0.)));
1002 
1003         auto rex0 = xt::prod(a, {1, 2}, initial(0));
1004         EXPECT_TRUE(xt::all(equal(rex0, 0.)));
1005     }
1006 
TEST(xreducer,ones_first)1007     TEST(xreducer, ones_first)
1008     {
1009         auto a = xt::ones<int>(std::vector<int>({ 1,1,2,4 }));
1010         std::vector<int> arraxis = { 1 };
1011         auto result = xt::sum(a, arraxis, xt::keep_dims | xt::evaluation_strategy::immediate);
1012         EXPECT_EQ(a, result);
1013 
1014         std::vector<int> arraxis2 = { 1, 2 };
1015         auto res2 = xt::sum(a, arraxis2, xt::keep_dims | xt::evaluation_strategy::immediate);
1016         xt::xarray<int> expected = { {{{2, 2, 2, 2}}} };
1017         EXPECT_EQ(expected, res2);
1018     }
1019 
TEST(xreducer,empty_axes)1020     TEST(xreducer, empty_axes)
1021     {
1022         xarray<int> a = { {1, 2, 3}, {4, 5, 6} };
1023         std::vector<std::size_t> axes = {};
1024         auto res0 = xt::sum(a, axes);
1025         auto res1 = xt::sum(a, axes, xt::keep_dims | xt::evaluation_strategy::immediate);
1026 
1027         EXPECT_EQ(res0, a);
1028         EXPECT_EQ(res1, a);
1029     }
1030 
TEST(xreducer,zero_shape)1031     TEST(xreducer, zero_shape)
1032     {
1033         xt::xarray<int> x = xt::zeros<int>({ 0, 1 });
1034 
1035         auto res0 = xt::sum(x, { 0 }, xt::keep_dims);
1036         EXPECT_EQ(res0.shape()[0], size_t(1));
1037         EXPECT_EQ(res0.shape()[1], size_t(1));
1038         EXPECT_EQ(res0(0, 0), 0);
1039 
1040         auto res1 = xt::sum(x, { 1 }, xt::keep_dims);
1041         EXPECT_EQ(res1.shape()[0], size_t(0));
1042         EXPECT_EQ(res1.shape()[1], size_t(1));
1043         EXPECT_EQ(res1.size(), size_t(0));
1044 
1045         auto res2 = xt::sum(x, xt::keep_dims);
1046         EXPECT_EQ(res2.shape()[0], size_t(1));
1047         EXPECT_EQ(res2.shape()[1], size_t(1));
1048         EXPECT_EQ(res2(0, 0), 0);
1049     }
1050 
TEST(xreducer,empty_array)1051     TEST(xreducer, empty_array)
1052     {
1053         xt::xarray<double> a = xt::ones<double>({ 1, 2, 0, 1 });
1054         double result0 = xt::mean(a)();
1055         EXPECT_TRUE(std::isnan(result0));
1056 
1057         auto result1 = xt::mean(a, { 1 });
1058         auto expected1 = xt::xarray<double>::from_shape({1, 0, 1});
1059         EXPECT_EQ(result1, expected1);
1060 
1061         auto result2 = xt::mean(a, { 2 });
1062         auto expected2 = xt::xarray<double>::from_shape({1, 2, 1});
1063         EXPECT_EQ(result2.shape(), expected2.shape());
1064         EXPECT_TRUE(std::isnan(result2(0, 0, 0)));
1065         EXPECT_TRUE(std::isnan(result2(0, 1, 0)));
1066     }
1067 
TEST(xreducer,double_axis)1068     TEST(xreducer, double_axis)
1069     {
1070         xt::xarray<int> a = xt::ones<int>({ 3, 2});
1071         XT_EXPECT_ANY_THROW(xt::sum(a, {1, 1}));
1072     }
1073 
TEST(xreducer,sum_xtensor_of_fixed)1074     TEST(xreducer, sum_xtensor_of_fixed)
1075     {
1076         xt::xtensor_fixed<float, xt::xshape<3>> a = {1, 2, 3}, b = {1, 2, 3};
1077         xt::xtensor<xt::xtensor_fixed<float, xt::xshape<3>>, 1> c = {a, b};
1078 
1079         auto res = xt::sum(c)();
1080         EXPECT_EQ(res, a * 2.);
1081 
1082         xt::xtensor_fixed<float, xt::xshape<3>> res1 = res;
1083         EXPECT_EQ(res1, a * 2.);
1084     }
1085 }
1086