Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions include/xtensor/core/xmath.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,59 @@ namespace xt
* @defgroup red_functions reducing functions
*/

namespace detail
{
template <class F, class E, class X, class O>
inline auto logical_reduce(F f, bool identity, E&& e, X&& axes, O options)
{
auto functors = make_xreducer_functor(f, xt::const_value<bool>(identity));
if constexpr (xtl::is_integral<std::decay_t<X>>::value)
{
return xt::reduce(std::move(functors), std::forward<E>(e), {axes}, options);
}
else
{
return xt::reduce(std::move(functors), std::forward<E>(e), std::forward<X>(axes), options);
}
}
}

/**
* @ingroup red_functions
* @brief Test whether any element is true along the given axes.
* An empty reduction yields false. With no axes argument, the scalar
* overload returns a bool and stops as soon as the result is known.
*/
template <class E, class X, class O = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(std::negation<is_reducer_options<X>>)>
inline auto any(E&& e, X&& axes, O options = O())
{
return detail::logical_reduce(detail::logical_or(), false, std::forward<E>(e), std::forward<X>(axes), options);
}

template <class E, class I, std::size_t N, class O = DEFAULT_STRATEGY_REDUCERS>
inline auto any(E&& e, const I (&axes)[N], O options = O())
{
return detail::logical_reduce(detail::logical_or(), false, std::forward<E>(e), axes, options);
}

/**
* @ingroup red_functions
* @brief Test whether all elements are true along the given axes.
* An empty reduction yields true. With no axes argument, the scalar
* overload returns a bool and stops as soon as the result is known.
*/
template <class E, class X, class O = DEFAULT_STRATEGY_REDUCERS, XTL_REQUIRES(std::negation<is_reducer_options<X>>)>
inline auto all(E&& e, X&& axes, O options = O())
{
return detail::logical_reduce(detail::logical_and(), true, std::forward<E>(e), std::forward<X>(axes), options);
}

template <class E, class I, std::size_t N, class O = DEFAULT_STRATEGY_REDUCERS>
inline auto all(E&& e, const I (&axes)[N], O options = O())
{
return detail::logical_reduce(detail::logical_and(), true, std::forward<E>(e), axes, options);
}

/**
* @ingroup red_functions
* @brief Sum of elements over given axes.
Expand Down
10 changes: 10 additions & 0 deletions include/xtensor/reducers/xreducer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,16 @@ namespace xt
return result;
}

// Empty slices have no first element to initialize a partial reduction.
if (e.size() == 0)
{
if (result.size() != 0)
{
result.fill(options_t::has_initial_value ? options.initial_value : init_fct());
}
return result;
}

std::size_t leading_ax = axes[(e.layout() == layout_type::row_major) ? axes.size() - 1 : 0];
auto strides_finder = e.strides().begin() + static_cast<std::ptrdiff_t>(leading_ax);
// The computed strides contain "0" where the shape is 1 -- therefore find the next none-zero number
Expand Down
53 changes: 53 additions & 0 deletions test/test_xmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,59 @@ namespace xt
* Basic operations *
********************/

TEST(xmath, logical_reduction_axes)
{
xarray<int> a = {{0, 0, 0}, {-1, 0, 2}};
static_assert(std::is_same_v<decltype(xt::any(a)), bool>);
static_assert(std::is_same_v<decltype(xt::all(a)), bool>);
auto rows = xt::any(a, -1);
static_assert(std::is_same_v<typename decltype(rows)::value_type, bool>);
EXPECT_EQ(rows.shape(), (shape_type{2}));
EXPECT_FALSE(rows(0));
EXPECT_TRUE(rows(1));
EXPECT_TRUE(xt::any(a, {0, 1})());
EXPECT_FALSE(xt::all(a, {0, 1})());

std::vector<int> axes = {0};
auto kept = xt::all(a + 1, axes, xt::keep_dims | xt::evaluation_strategy::immediate);
EXPECT_EQ(kept.shape(), (shape_type{1, 3}));
EXPECT_FALSE(kept(0, 0));
EXPECT_TRUE(kept(0, 1));
EXPECT_TRUE(kept(0, 2));

xarray<int> mixed = {{{0, 0}, {1, 2}}, {{0, 3}, {-1, 4}}};
auto columns = xt::all(mixed, {0, 2});
EXPECT_EQ(columns.shape(), (shape_type{2}));
EXPECT_FALSE(columns(0));
EXPECT_TRUE(columns(1));

std::vector<int> no_axes;
auto elementwise = xt::any(a, no_axes, xt::evaluation_strategy::immediate);
EXPECT_EQ(elementwise.shape(), a.shape());
EXPECT_FALSE(elementwise(0, 1));
EXPECT_TRUE(elementwise(1, 0));

const xarray<int> empty = xarray<int>::from_shape({2, 0});
auto none = xt::any(empty, 1);
auto every = xt::all(empty, 1);
EXPECT_FALSE(none(0));
EXPECT_FALSE(none(1));
EXPECT_TRUE(every(0));
EXPECT_TRUE(every(1));

auto empty_rows = xarray<int>::from_shape({0, 3});
auto by_column = xt::any(empty_rows, 0, xt::evaluation_strategy::immediate);
auto every_column = xt::all(empty_rows, 0, xt::evaluation_strategy::immediate);
EXPECT_EQ(by_column.shape(), (shape_type{3}));
EXPECT_FALSE(xt::any(by_column));
EXPECT_EQ(every_column.shape(), (shape_type{3}));
EXPECT_TRUE(xt::all(every_column));
EXPECT_EQ(xt::any(empty_rows, 1, xt::evaluation_strategy::immediate).size(), 0u);
EXPECT_EQ(xt::all(empty_rows, 1, xt::evaluation_strategy::immediate).size(), 0u);
auto seeded = xt::sum(empty_rows, 0, xt::initial(7) | xt::evaluation_strategy::immediate);
EXPECT_TRUE(xt::all(xt::equal(seeded, 7)));
}

TEST(xmath, abs)
{
shape_type shape = {3, 2};
Expand Down