From 29d00c9efd4f3f895c6418b6cf3e98156ec49b7a Mon Sep 17 00:00:00 2001 From: Allen Flux <26239570+allenflux@users.noreply.github.com> Date: Sun, 11 Oct 2026 10:03:58 +0700 Subject: [PATCH 1/2] Fix drop slice index ordering and repeated normalization --- include/xtensor/views/xslice.hpp | 3 ++ test/test_xview.cpp | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/xtensor/views/xslice.hpp b/include/xtensor/views/xslice.hpp index cdae97c9f..efcc2463e 100644 --- a/include/xtensor/views/xslice.hpp +++ b/include/xtensor/views/xslice.hpp @@ -10,6 +10,7 @@ #ifndef XTENSOR_SLICE_HPP #define XTENSOR_SLICE_HPP +#include #include #include #include @@ -1506,6 +1507,8 @@ namespace xt m_indices[i] = m_raw_indices[i] < 0 ? static_cast(shape) + m_raw_indices[i] : m_raw_indices[i]; } + std::sort(m_indices.begin(), m_indices.end()); + m_inc.clear(); size_type cum = size_type(0); size_type prev_cum = cum; for (std::size_t i = 0; i < sz; ++i) diff --git a/test/test_xview.cpp b/test/test_xview.cpp index 6547b9df1..145894cf1 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -1259,6 +1259,55 @@ namespace xt EXPECT_EQ(a(1, 1, 1), 456); } + TEST(xview, drop_unsorted) + { + xtensor a = {0, 1, 2, 3, 4, 5}; + xtensor expected = {1, 3, 4, 5}; + auto v = xt::view(a, drop(2, 0)); + EXPECT_EQ(v, expected); + test_view_iter(v, expected); + + std::vector indices = {2, 0}; + xtensor result = xt::view(a, drop(indices)); + EXPECT_EQ(result, expected); + + xtensor kept = xt::view(a, keep(2, 0)); + xtensor expected_kept = {2, 0}; + EXPECT_EQ(kept, expected_kept); + + v = -1; + xtensor expected_a = {0, -1, 2, -1, -1, -1}; + EXPECT_EQ(a, expected_a); + } + + TEST(xview, drop_unsorted_negative) + { + xtensor a = {{0, 1, 2, 3, 4, 5}, {6, 7, 8, 9, 10, 11}}; + xtensor expected = {{1, 2, 3, 4}, {7, 8, 9, 10}}; + auto v = xt::view(a, all(), drop(-1, 0)); + EXPECT_EQ(v, expected); + test_view_iter(v, expected); + + xtensor result = v; + EXPECT_EQ(result, expected); + } + + TEST(xview, drop_normalize_reuse) + { + auto slice = drop(-2, -1); + slice.normalize(4); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice(0), 0); + EXPECT_EQ(slice(1), 1); + + slice.normalize(6); + EXPECT_EQ(slice.size(), 4); + for (std::ptrdiff_t i = 0; i < slice.size(); ++i) + { + EXPECT_EQ(slice(i), i); + } + } + TEST(xview, drop_negative) { xtensor a = { From 19e11a2edc7b3e00a50a67bd75badacc8dbf19dd Mon Sep 17 00:00:00 2001 From: allenflux <26239570+allenflux@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:46:00 +0700 Subject: [PATCH 2/2] Require ordered drop indices and avoid internal sorting Document ordering after negative-index resolution and retain the repeated-normalization offset-map fix with its regression. Generated with OpenAI Codex --- docs/source/view.rst | 6 +++++ include/xtensor/views/xslice.hpp | 5 ++-- test/test_xview.cpp | 42 +------------------------------- 3 files changed, 10 insertions(+), 43 deletions(-) diff --git a/docs/source/view.rst b/docs/source/view.rst index 95b6365b8..446154e25 100644 --- a/docs/source/view.rst +++ b/docs/source/view.rst @@ -30,6 +30,12 @@ Slices can be specified in the following ways: - :cpp:func:`xt::keep(i0, i1, i2, ...) ` a slice selecting non-contiguous indices to keep on the underlying expression - :cpp:func:`xt::drop(i0, i1, i2, ...) ` a slice selecting non-contiguous indices to drop on the underlying expression +For ``xt::drop``, indices must be in increasing order after resolving negative indices +relative to the sliced axis. For an axis of size 6, ``xt::drop(0, -1)`` gives the ordered +positions 0 and 5, whereas ``xt::drop(-1, 0)`` does not satisfy this requirement. +The library does not sort or check the order. When reusing a slice with a different +axis size, its resolved indices must still satisfy this requirement. + .. code:: #include diff --git a/include/xtensor/views/xslice.hpp b/include/xtensor/views/xslice.hpp index efcc2463e..6cd2865a4 100644 --- a/include/xtensor/views/xslice.hpp +++ b/include/xtensor/views/xslice.hpp @@ -10,7 +10,6 @@ #ifndef XTENSOR_SLICE_HPP #define XTENSOR_SLICE_HPP -#include #include #include #include @@ -479,6 +478,9 @@ namespace xt * xt::view(a, xt::drop(0, 2); // => {{3, 4, 5}} * @endcode * + * The indices must be in increasing order after resolving negative indices + * relative to the sliced axis. The library does not sort or check their order. + * * @param indices The container of indices to drop * @return instance of xdrop_slice */ @@ -1507,7 +1509,6 @@ namespace xt m_indices[i] = m_raw_indices[i] < 0 ? static_cast(shape) + m_raw_indices[i] : m_raw_indices[i]; } - std::sort(m_indices.begin(), m_indices.end()); m_inc.clear(); size_type cum = size_type(0); size_type prev_cum = cum; diff --git a/test/test_xview.cpp b/test/test_xview.cpp index 145894cf1..8069bb016 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -1259,53 +1259,13 @@ namespace xt EXPECT_EQ(a(1, 1, 1), 456); } - TEST(xview, drop_unsorted) - { - xtensor a = {0, 1, 2, 3, 4, 5}; - xtensor expected = {1, 3, 4, 5}; - auto v = xt::view(a, drop(2, 0)); - EXPECT_EQ(v, expected); - test_view_iter(v, expected); - - std::vector indices = {2, 0}; - xtensor result = xt::view(a, drop(indices)); - EXPECT_EQ(result, expected); - - xtensor kept = xt::view(a, keep(2, 0)); - xtensor expected_kept = {2, 0}; - EXPECT_EQ(kept, expected_kept); - - v = -1; - xtensor expected_a = {0, -1, 2, -1, -1, -1}; - EXPECT_EQ(a, expected_a); - } - - TEST(xview, drop_unsorted_negative) - { - xtensor a = {{0, 1, 2, 3, 4, 5}, {6, 7, 8, 9, 10, 11}}; - xtensor expected = {{1, 2, 3, 4}, {7, 8, 9, 10}}; - auto v = xt::view(a, all(), drop(-1, 0)); - EXPECT_EQ(v, expected); - test_view_iter(v, expected); - - xtensor result = v; - EXPECT_EQ(result, expected); - } - TEST(xview, drop_normalize_reuse) { auto slice = drop(-2, -1); slice.normalize(4); - EXPECT_EQ(slice.size(), 2); - EXPECT_EQ(slice(0), 0); EXPECT_EQ(slice(1), 1); - slice.normalize(6); - EXPECT_EQ(slice.size(), 4); - for (std::ptrdiff_t i = 0; i < slice.size(); ++i) - { - EXPECT_EQ(slice(i), i); - } + EXPECT_EQ(slice(2), 2); } TEST(xview, drop_negative)