Skip to content

Fix xview_stepper on broadcast and reverse traversals - #2957

Open
gouarin wants to merge 1 commit into
xtensor-stack:masterfrom
gouarin:fix-xview-stepper
Open

gouarin wants to merge 1 commit into
xtensor-stack:masterfrom
gouarin:fix-xview-stepper

Conversation

@gouarin

@gouarin gouarin commented Oct 29, 2026

Copy link
Copy Markdown
Member

Checklist

  • The title and commit message(s) are descriptive.
  • Small commits made to fix your PR have been squashed to avoid history pollution.
  • Tests have been added for new features or bug fixes.
  • API of new functions and classes are documented (no public API changes).

Description

xview_stepper, the stepper of views that are not strided (keep or drop slices), gave wrong results or read out of bounds in three cases:

xt::xarray<int> a = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
xt::xarray<int> z = xt::zeros<int>({2, 2, 2});
xt::xarray<int> r = z + xt::view(a, xt::keep(0, 2), xt::keep(1, 3));  // {{1, 1}, {3, 3}} instead of {{1, 3}, {9, 11}}
auto v = xt::view(a, 1, xt::keep(0, 2, 3));
std::vector<int> rev(v.rbegin(), v.rend());                          // crashes
auto w = xt::view(a, xt::keep(0, 2), 1);
std::vector<int> rev2(w.rbegin(), w.rend());                         // crashes
  • step and reset ignored m_offset, so a view broadcast to a higher rank moved along the wrong axes.
  • m_index_keeper was indexed by slice in the constructor and to_end, but by view dimension in step and reset.
  • to_end left the stepper one past the last element along the last axis of the underlying expression. That is not the innermost axis of the view when the last slice is integral or a newaxis, or when some axes have no slice.

The stepper now handles m_offset like xstepper, indexes m_index_keeper by slice, and builds the end position from the begin position. The reverse column-major check in test_view_iter, commented out because it failed for keep slices, is enabled again.

xview.keep_slice_reverse_iteration and xview.keep_broadcast_higher_rank fail on master and pass with the fix, also under AddressSanitizer and UBSan. The GCC 13/15 -Warray-bounds warnings in common_step_forward and common_reset go away too.

xview_stepper is the stepper of views that are not strided, such as
views with keep or drop slices. It had three bugs.

step() and reset() received a dimension of the iterated shape but used
it as a view dimension. When the view is broadcast to a higher rank,
the first m_offset dimensions of that shape are not view dimensions,
so the stepper moved along the wrong axes. reset() did not even skip
them. With a = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}},
zeros({2, 2, 2}) + view(a, keep(0, 2), keep(1, 3)) gave
{{1, 1}, {3, 3}} twice instead of {{1, 3}, {9, 11}}, and other shapes
read the strides of the underlying container out of bounds.

m_index_keeper is indexed like the slices in the constructor and in
to_end(), but step() and reset() indexed it by view dimension. The two
differ when an integral slice comes first, so view(a, 1, keep(0, 2, 3))
iterated backward from an uninitialized entry and crashed.

to_end() stepped back from the end of the underlying expression, which
leaves the stepper one past the last element along the last underlying
axis. That is not the innermost axis of the view when the last slice is
integral, when it is a newaxis or when the view has fewer slices than
dimensions, so rbegin() started at the wrong element or past the end.

The stepper now subtracts m_offset like xstepper, indexes
m_index_keeper by slice, delegates the dimensions without a slice to
the underlying stepper, and builds the end position from the begin
position, the way increment_stepper reaches it. to_begin() also moves
to the first element of the view, not of the underlying expression.

The reverse column major check in test_view_iter, disabled because it
failed for keep slices, now passes and is enabled again. The new
xview tests fail on master and pass with the fix, also under
AddressSanitizer and UBSan. This also removes the GCC -Warray-bounds
warnings reported in common_step_forward and common_reset.
@codspeed

codspeed Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

1 warning

⚠️ Unable to measure the execution of 1 benchmark

This benchmark measured no execution time, so its result is not comparable and counts as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

✅ 255 untouched benchmarks


Comparing gouarin:fix-xview-stepper (e78a19f) with master (57f3d07)

Open in CodSpeed

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant