Skip to content

Commit 30bc8ea

Browse files
committed
Use one aggregate_impl for reducers with and without keep_dims
xreducer_stepper::aggregate_impl had two overloads. The one used without keep_dims walks the reduced axes, m_axes[0] to m_axes.back(). The keep_dims one walked every dimension of the expression, looked each one up in m_axes with std::find, and recursed through the dimensions that are not reduced without doing anything there. Both do the same steps on the underlying stepper: keep_dims only changes the shape of the result, which the stepper does not use here. The extra size != 0 test of the keep_dims overload is also redundant, since aggregate() returns the initial value for empty expressions before calling aggregate_impl. The keep_dims overload is removed. GCC 13 at -O3 inlined that recursion and warned about m_e.shape()[dim] for dim == dimension() (-Warray-bounds), a path that the axis validation in the constructor rules out. The warning goes away with the overload. The new test compares lazy keep_dims sums with immediate ones for every set of axes of a 4-D array, in row and column major order and with an initial value.
1 parent 57f3d07 commit 30bc8ea

2 files changed

Lines changed: 37 additions & 46 deletions

File tree

‎include/xtensor/reducers/xreducer.hpp‎

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,8 +1123,7 @@ namespace xt
11231123

11241124
reference initial_value() const;
11251125
reference aggregate(size_type dim) const;
1126-
reference aggregate_impl(size_type dim, /*keep_dims=*/std::false_type) const;
1127-
reference aggregate_impl(size_type dim, /*keep_dims=*/std::true_type) const;
1126+
reference aggregate_impl(size_type dim) const;
11281127

11291128
substepper_type get_substepper_begin() const;
11301129
size_type get_dim(size_type dim) const noexcept;
@@ -1789,7 +1788,7 @@ namespace xt
17891788
}
17901789
else
17911790
{
1792-
res = aggregate_impl(dim, typename O::keep_dims());
1791+
res = aggregate_impl(dim);
17931792
if (O::has_initial_value && dim == 0)
17941793
{
17951794
res = m_reducer->m_merge(m_reducer->m_options.initial_value, res);
@@ -1798,20 +1797,23 @@ namespace xt
17981797
return res;
17991798
}
18001799

1800+
// Reduces along the axes m_axes[dim], ..., m_axes.back() at the current position
1801+
// of the stepper, then moves the stepper back along m_axes[dim]. Dimensions that
1802+
// are not reduced are not visited, with or without keep_dims.
18011803
template <class F, class CT, class X, class O>
1802-
inline auto xreducer_stepper<F, CT, X, O>::aggregate_impl(size_type dim, std::false_type) const -> reference
1804+
inline auto xreducer_stepper<F, CT, X, O>::aggregate_impl(size_type dim) const -> reference
18031805
{
18041806
// reference can be std::array, hence the {} initializer
18051807
reference res = {};
18061808
size_type index = axis(dim);
18071809
size_type size = shape(index);
18081810
if (dim != m_reducer->m_axes.size() - 1)
18091811
{
1810-
res = aggregate_impl(dim + 1, typename O::keep_dims());
1812+
res = aggregate_impl(dim + 1);
18111813
for (size_type i = 1; i != size; ++i)
18121814
{
18131815
m_stepper.step(index);
1814-
res = m_reducer->m_merge(res, aggregate_impl(dim + 1, typename O::keep_dims()));
1816+
res = m_reducer->m_merge(res, aggregate_impl(dim + 1));
18151817
}
18161818
}
18171819
else
@@ -1827,46 +1829,6 @@ namespace xt
18271829
return res;
18281830
}
18291831

1830-
template <class F, class CT, class X, class O>
1831-
inline auto xreducer_stepper<F, CT, X, O>::aggregate_impl(size_type dim, std::true_type) const -> reference
1832-
{
1833-
// reference can be std::array, hence the {} initializer
1834-
reference res = {};
1835-
auto ax_it = std::find(m_reducer->m_axes.begin(), m_reducer->m_axes.end(), dim);
1836-
if (ax_it != m_reducer->m_axes.end())
1837-
{
1838-
size_type index = dim;
1839-
size_type size = m_reducer->m_e.shape()[index];
1840-
if (ax_it != m_reducer->m_axes.end() - 1 && size != 0)
1841-
{
1842-
res = aggregate_impl(dim + 1, typename O::keep_dims());
1843-
for (size_type i = 1; i != size; ++i)
1844-
{
1845-
m_stepper.step(index);
1846-
res = m_reducer->m_merge(res, aggregate_impl(dim + 1, typename O::keep_dims()));
1847-
}
1848-
}
1849-
else
1850-
{
1851-
res = m_reducer->m_reduce(static_cast<reference>(m_reducer->m_init()), *m_stepper);
1852-
for (size_type i = 1; i != size; ++i)
1853-
{
1854-
m_stepper.step(index);
1855-
res = m_reducer->m_reduce(res, *m_stepper);
1856-
}
1857-
}
1858-
m_stepper.reset(index);
1859-
}
1860-
else
1861-
{
1862-
if (dim < m_reducer->m_e.dimension())
1863-
{
1864-
res = aggregate_impl(dim + 1, typename O::keep_dims());
1865-
}
1866-
}
1867-
return res;
1868-
}
1869-
18701832
template <class F, class CT, class X, class O>
18711833
inline auto xreducer_stepper<F, CT, X, O>::get_substepper_begin() const -> substepper_type
18721834
{

‎test/test_xreducer.cpp‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,35 @@ namespace xt
10171017
EXPECT_TRUE(xt::all(equal(rex0, 0.)));
10181018
}
10191019

1020+
TEST(xreducer, keep_dims_lazy_all_axes)
1021+
{
1022+
xt::xarray<int> a = xt::reshape_view(xt::arange<int>(2 * 3 * 4 * 5), {2, 3, 4, 5});
1023+
xt::xarray<int, xt::layout_type::column_major> c = a;
1024+
1025+
// Every non-empty set of axes, including non-adjacent ones.
1026+
for (std::size_t mask = 1; mask < 16; ++mask)
1027+
{
1028+
std::vector<std::size_t> axes;
1029+
for (std::size_t d = 0; d < 4; ++d)
1030+
{
1031+
if (mask & (std::size_t(1) << d))
1032+
{
1033+
axes.push_back(d);
1034+
}
1035+
}
1036+
auto expected = xt::sum(a, axes, xt::keep_dims | xt::evaluation_strategy::immediate);
1037+
1038+
xt::xarray<int> lazy = xt::sum(a, axes, xt::keep_dims);
1039+
EXPECT_EQ(lazy, expected);
1040+
1041+
xt::xarray<int> lazy_init = xt::sum(a, axes, xt::keep_dims | initial(7));
1042+
EXPECT_EQ(lazy_init, expected + 7);
1043+
1044+
xt::xarray<int> lazy_col = xt::sum(c, axes, xt::keep_dims);
1045+
EXPECT_EQ(lazy_col, expected);
1046+
}
1047+
}
1048+
10201049
TEST(xreducer, ones_first)
10211050
{
10221051
auto a = xt::ones<int>(std::vector<int>({1, 1, 2, 4}));

0 commit comments

Comments
 (0)