/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" #include "xtensor/core/xvectorize.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/views/xview.hpp" #include "test_common.hpp" #include "test_common_macros.hpp" namespace xt { using std::size_t; struct xfunction_features { xarray m_a; // shape = { 3, 2, 4 } xarray m_b; // shape = { 3, 1, 4 } xarray m_c; // shape = { 4, 3, 2, 4 } xfunction_features(); }; xfunction_features::xfunction_features() { row_major_result<> rm; m_a.resize(rm.shape(), rm.strides()); std::copy(rm.storage().cbegin(), rm.storage().cend(), m_a.begin()); unit_shape_result<> us; m_b.resize(us.shape(), us.strides()); std::copy(us.storage().cbegin(), us.storage().cend(), m_b.begin()); using shape_type = layout_result<>::shape_type; shape_type sh = {4, 3, 2, 4}; m_c.resize(sh); for (size_t i = 0; i < sh[0]; ++i) { for (size_t j = 0; j < sh[1]; ++j) { for (size_t k = 0; k < sh[2]; ++k) { for (size_t l = 0; l < sh[3]; ++l) { m_c(i, j, k, l) = m_a(j, k, l) + static_cast(i); } } } } } template auto normalize(E&& expr) { return E{expr} / xt::view(xt::sum(E{expr}, {1}), xt::all(), xt::newaxis()); } TEST(xfunction, copy_constructor) { xt::xarray freqs({10, 5}, 1); xt::xarray other_freqs({10, 5}, 2); // Compilation test xarray probs = normalize(freqs + other_freqs); } TEST(xfunction, broadcast_shape) { using shape_type = layout_result<>::shape_type; xfunction_features f; SUBCASE("same shape") { shape_type sh = uninitialized_shape(3); bool trivial = (f.m_a + f.m_a).broadcast_shape(sh); EXPECT_EQ(sh, f.m_a.shape()); ASSERT_TRUE(trivial); } SUBCASE("different shape") { shape_type sh = uninitialized_shape(3); bool trivial = (f.m_a + f.m_b).broadcast_shape(sh); EXPECT_EQ(sh, f.m_a.shape()); ASSERT_FALSE(trivial); } SUBCASE("different dimensions") { shape_type sh = uninitialized_shape(4); bool trivial = (f.m_a + f.m_c).broadcast_shape(sh); EXPECT_EQ(sh, f.m_c.shape()); ASSERT_FALSE(trivial); } } TEST(xfunction, broadcast_shape_exception) { xt::xarray arr1{{1.0, 2.0, 3.0}}; xt::xarray arr2{5.0, 6.0, 7.0, 99.0}; XT_EXPECT_ANY_THROW(xt::xarray res = arr1 * arr2); } TEST(xfunction, shape) { xfunction_features f; auto func = f.m_a + f.m_c; const auto& sh = func.shape(); for (std::size_t i = 0; i < sh.size(); ++i) { EXPECT_EQ(sh[i], func.shape(i)); } } TEST(xfunction, layout_type) { xarray m_d; xarray m_r; xarray m_c; xscalar m_a(2); auto res_dd = m_d + m_d; EXPECT_EQ(res_dd.layout(), layout_type::dynamic); auto res_dr = m_d + m_r; EXPECT_EQ(res_dr.layout(), layout_type::dynamic); auto res_dc = m_d + m_c; EXPECT_EQ(res_dc.layout(), layout_type::dynamic); auto res_da = m_d + m_a; EXPECT_EQ(res_da.layout(), layout_type::dynamic); auto res_rd = m_r + m_d; EXPECT_EQ(res_rd.layout(), layout_type::dynamic); auto res_rr = m_r + m_r; EXPECT_EQ(res_rr.layout(), layout_type::row_major); auto res_rc = m_r + m_c; EXPECT_EQ(res_rc.layout(), layout_type::dynamic); auto res_ra = m_r + m_a; EXPECT_EQ(res_ra.layout(), layout_type::row_major); auto res_cd = m_c + m_d; EXPECT_EQ(res_cd.layout(), layout_type::dynamic); auto res_cr = m_c + m_r; EXPECT_EQ(res_cr.layout(), layout_type::dynamic); auto res_cc = m_c + m_c; EXPECT_EQ(res_cc.layout(), layout_type::column_major); auto res_ca = m_c + m_a; EXPECT_EQ(res_ca.layout(), layout_type::column_major); auto res_ad = m_a + m_d; EXPECT_EQ(res_ad.layout(), layout_type::dynamic); auto res_ar = m_a + m_r; EXPECT_EQ(res_ar.layout(), layout_type::row_major); auto res_ac = m_a + m_c; EXPECT_EQ(res_ac.layout(), layout_type::column_major); auto res_aa = m_a + m_a; EXPECT_EQ(res_aa.layout(), layout_type::any); } TEST(xfunction, access) { xfunction_features f; size_t i = f.m_a.shape()[0] - 1; size_t j = f.m_a.shape()[1] - 1; size_t k = f.m_a.shape()[2] - 1; SUBCASE("same shape") { int a = (f.m_a + f.m_a)(i, j, k); int b = f.m_a(i, j, k) + f.m_a(i, j, k); EXPECT_EQ(a, b); } SUBCASE("different shape") { int a = (f.m_a + f.m_b)(i, j, k); int b = f.m_a(i, j, k) + f.m_b(i, 0, k); EXPECT_EQ(a, b); } SUBCASE("different dimensions") { int a = (f.m_a + f.m_c)(1, i, j, k); int b = f.m_a(i, j, k) + f.m_c(1, i, j, k); EXPECT_EQ(a, b); } } TEST(xfunction, access_dangling_reference) { auto values_original = xt::xtensor{1., 2., 3.}; auto indexes = xt::arange(values_original.size()); auto map = [&](const size_t& index) -> auto& { return values_original[index]; }; auto values_mapped = xt::vectorize(map)(indexes); for (size_t i = 0; i < values_original.size(); ++i) { EXPECT_TRUE(&(values_mapped[i]) == &(values_original[i])); } } TEST(xfunction, unchecked) { xfunction_features f; size_t i = f.m_a.shape()[0] - 1; size_t j = f.m_a.shape()[1] - 1; size_t k = f.m_a.shape()[2] - 1; SUBCASE("same shape") { int a = (f.m_a + f.m_a)(i, j, k); int b = (f.m_a + f.m_a).unchecked(i, j, k); EXPECT_EQ(a, b); } SUBCASE("different shape") { int a = (f.m_a + f.m_b)(i, j, k); int b = (f.m_a + f.m_b).unchecked(i, j, k); EXPECT_EQ(a, b); } } TEST(xfunction, at) { xfunction_features f; size_t i = f.m_a.shape()[0] - 1; size_t j = f.m_a.shape()[1] - 1; size_t k = f.m_a.shape()[2] - 1; SUBCASE("same shape") { int a = (f.m_a + f.m_a).at(i, j, k); int b = f.m_a.at(i, j, k) + f.m_a.at(i, j, k); EXPECT_EQ(a, b); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(0, 0, 0, 0)); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(10, 10, 10)); } SUBCASE("different shape") { int a = (f.m_a + f.m_b).at(i, j, k); int b = f.m_a.at(i, j, k) + f.m_b.at(i, 0, k); EXPECT_EQ(a, b); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(0, 0, 0, 0)); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(10, 10, 10)); } SUBCASE("different dimensions") { int a = (f.m_a + f.m_c).at(1, i, j, k); int b = f.m_a.at(i, j, k) + f.m_c.at(1, i, j, k); EXPECT_EQ(a, b); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(0, 0, 0, 0, 0)); XT_EXPECT_ANY_THROW((f.m_a + f.m_a).at(10, 10, 10, 10)); } } TEST(xfunction, periodic) { xfunction_features f; size_t i = f.m_a.shape()[0]; size_t j = f.m_a.shape()[1]; size_t k = f.m_a.shape()[2]; SUBCASE("same shape") { int a = (f.m_a + f.m_a)(0, 0, 0); int b = (f.m_a + f.m_a).periodic(i, j, k); EXPECT_EQ(a, b); } SUBCASE("different shape") { int a = (f.m_a + f.m_b)(0, 0, 0); int b = (f.m_a + f.m_b).periodic(i, j, k); EXPECT_EQ(a, b); } } TEST(xfunction, front) { xfunction_features f; int a = (f.m_a + f.m_a).front(); int b = f.m_a.front() + f.m_a.front(); EXPECT_EQ(a, b); } TEST(xfunction, back) { xfunction_features f; int a = (f.m_a + f.m_a).back(); int b = f.m_a.back() + f.m_a.back(); EXPECT_EQ(a, b); } TEST(xfunction, flat) { xfunction_features f; int a = (f.m_a + f.m_a).flat(0); int b = f.m_a.flat(0) + f.m_a.flat(0); EXPECT_EQ(a, b); } TEST(xfunction, in_bounds) { xfunction_features f; size_t i = f.m_a.shape()[0]; size_t j = f.m_a.shape()[1]; size_t k = f.m_a.shape()[2]; SUBCASE("same shape") { EXPECT_TRUE((f.m_a + f.m_a).in_bounds(0, 0, 0) == true); EXPECT_TRUE((f.m_a + f.m_a).in_bounds(i, j, k) == false); } SUBCASE("different shape") { EXPECT_TRUE((f.m_a + f.m_b).in_bounds(0, 0, 0) == true); EXPECT_TRUE((f.m_a + f.m_b).in_bounds(i, j, k) == false); } } TEST(xfunction, indexed_access) { xfunction_features f; xindex index(f.m_a.dimension()); index[0] = f.m_a.shape()[0] - 1; index[1] = f.m_a.shape()[1] - 1; index[2] = f.m_a.shape()[2] - 1; SUBCASE("same shape") { int a = (f.m_a + f.m_a)[index]; int b = f.m_a[index] + f.m_a[index]; EXPECT_EQ(a, b); EXPECT_EQ(((f.m_a + f.m_a)[{0, 0, 0}]), (f.m_a[{0, 0, 0}] + f.m_a[{0, 0, 0}])); } SUBCASE("different shape") { int a = (f.m_a + f.m_b)[index]; xindex index2 = index; index2[1] = 0; int b = f.m_a[index] + f.m_b[index2]; EXPECT_EQ(a, b); EXPECT_EQ(((f.m_a + f.m_b)[{0, 0, 0}]), (f.m_a[{0, 0, 0}] + f.m_b[{0, 0, 0}])); } SUBCASE("different dimensions") { xindex index2(f.m_c.dimension()); index2[0] = 1; index2[1] = index[0]; index2[2] = index[1]; index2[3] = index[2]; int a = (f.m_a + f.m_c)[index2]; int b = f.m_a[index] + f.m_c[index2]; EXPECT_EQ(a, b); EXPECT_EQ(((f.m_a + f.m_c)[{0, 0, 0, 0}]), (f.m_a[{0, 0, 0, 0}] + f.m_c[{0, 0, 0, 0}])); } } void test_xfunction_iterator(const xarray& a, const xarray& b) { auto func = (a + b); auto iter = func.begin(); auto itera = a.begin(); auto iterb = b.begin(a.shape()); auto nb_iter = a.shape().back() * 2 + 1; for (size_t i = 0; i < nb_iter; ++i) { ++iter, ++itera, ++iterb; EXPECT_EQ(*iter, *itera + *iterb); } EXPECT_EQ(*iter, *itera + *iterb); } TEST(xfunction, iterator) { xfunction_features f; SUBCASE("same shape") { test_xfunction_iterator(f.m_a, f.m_a); } SUBCASE("different shape") { test_xfunction_iterator(f.m_a, f.m_b); } SUBCASE("different dimensions") { test_xfunction_iterator(f.m_c, f.m_a); } } void test_xfunction_iterator_end(const xarray& a, const xarray& b) { auto func = (a + b); auto iter = func.begin(); auto iter_end = func.end(); auto size = a.size(); for (size_t i = 0; i < size; ++i) { ++iter; } EXPECT_EQ(iter, iter_end); } TEST(xfunction, iterator_end) { xfunction_features f; SUBCASE("same shape") { test_xfunction_iterator_end(f.m_a, f.m_a); } SUBCASE("different shape") { test_xfunction_iterator_end(f.m_a, f.m_b); } SUBCASE("different dimensions") { test_xfunction_iterator_end(f.m_c, f.m_a); } } template void iterator_tester(const F& func) { auto res1 = empty_like(func); auto res2 = empty_like(func); auto res3 = empty_like(func); auto res4 = empty_like(func); auto res5 = func; auto resit1 = res1.template begin(); for (auto it = func.template begin(); it != func.template end(); ++it) { *(resit1++) = *it; } auto resit3 = res3.template rbegin(); for (auto it = func.template rbegin(); it != func.template rend(); ++it) { *(resit3++) = *it; } if (func.has_linear_assign(res2.strides())) { auto resit2 = res2.template begin(); for (auto it = func.linear_begin(); it != func.linear_end(); ++it) { *(resit2++) = *it; } auto resit4 = res4.template rbegin(); for (auto it = func.linear_rbegin(); it != func.linear_rend(); ++it) { *(resit4++) = *it; } EXPECT_TENSOR_EQ(res2, res5); EXPECT_TENSOR_EQ(res4, res5); } EXPECT_TENSOR_EQ(res1, res5); EXPECT_TENSOR_EQ(res3, res5); EXPECT_TENSOR_EQ(func, res5); } TEST(xfunction, all_iterators) { xt::xarray x = xt::random::rand({5, 5, 5}); xt::xarray y = xt::random::rand({5}); xt::xarray z = xt::random::rand({5, 5}); auto f1 = 2.0 * x; auto f2 = x * 2.0 * x; iterator_tester(f1); iterator_tester(f2); iterator_tester(x * y * 3.0); iterator_tester(5.0 + x * y * 3.0); iterator_tester(x * y * z); iterator_tester(x / y / z); } TEST(xfunction, xfunction_in_xfunction) { using Point3 = xt::xtensor_fixed>; Point3 a1({1., 2., 3.}); Point3 a2({3., 1., 3.}); Point3 a3({54., 5., 5.}); xtensor a({a1, a2, a3}); xtensor c = a + a; Point3 r1({2., 4., 6.}), r2({6., 2., 6.}), r3({108., 10., 10.}); xtensor res{r1, r2, r3}; EXPECT_EQ(c, res); } }