fix: normalize negative integer slice bounds in indexing#1156
Merged
Conversation
Negative slice bounds raised `ValueError: begin < end required` (e.g. `h[1:-2]`) because plain negative ints were passed straight to Boost.Histogram, which does not apply NumPy-style negative indexing. `Axis._process_callable` now normalizes plain negative ints relative to the axis length, like Python/NumPy. Locators (`bh.loc`, `bh.tag.at`, rebin/sum steps) are callables handled earlier and stay untouched. The fix flows through `_process_loc`, so it applies to both `__getitem__` and `__setitem__`. Scalar negative indexing already worked via `_compute_uhi_index`. Mirrors scikit-hep/hist#699, fixing the bug at the underlying layer. Assisted-by: ClaudeCode:claude-opus-4.8
Out-of-range slice bounds now clamp to [0, len] like NumPy slicing (e.g. h[5:100] -> h[5:], h[-15:5] -> h[:5]), instead of relying on Boost's internal behavior or raising low-level errors. NumPy returns an empty array for slices like h[1:-15] or h[5:2], but a Boost.Histogram axis cannot have zero bins. These now raise a clear IndexError explaining the limitation instead of the cryptic "begin < end required" / "bins > 0 required". The check lives in _handle_slice so that intentional flow access via integration (e.g. h[3::sum] summing the overflow) is preserved. Assisted-by: ClaudeCode:claude-opus-4.8
numpy 2.5's stubs use a `type X = ...` statement that mypy rejects under
the downstream hist session ("Type statement is only supported in Python
3.12 and greater"), failing the Hist 3.14 job. Pin numpy below 2.5 until
the toolchain catches up.
Assisted-by: ClaudeCode:claude-opus-4.8
henryiii
marked this pull request as ready for review
June 26, 2026 04:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 AI text below 🤖
Brings NumPy-style negative and out-of-range slice indexing to boost-histogram, fixing the bug at the underlying layer (mirrors scikit-hep/hist#699, so hist and other UHI consumers get it for free).
Negative bounds
Negative slice bounds raised
ValueError: begin < end required(e.g.h[1:-2]) because plain negative ints were passed straight to Boost.Histogram, which does not apply NumPy-style negative indexing.Axis._process_callablenow normalizes plain negative ints relative to the axis length. Locators (bh.loc,bh.tag.at, rebin/sum steps) are callables handled earlier and stay untouched, so flow access liketag.at(-1)still reaches underflow. Scalar negative indexing (h[-1]) already worked via_compute_uhi_index.Out-of-range bounds (clamping)
Out-of-range bounds now clamp to
[0, len]like NumPy, so every non-empty slice matches NumPy exactly:h[5:100]h[5:]h[:100]h[:]h[-15:5]h[:5]h[-15:]h[:]Empty slices
NumPy returns an empty array for slices like
h[1:-15]orh[5:2], but a Boost.HistogramRegular/Variableaxis cannot have zero bins. These now raise a clearIndexErrorexplaining the limitation instead of the crypticbegin < end required/bins > 0 required. The check lives in_handle_slice, so intentional flow access via integration — e.g.h[3::sum]summing the overflow on a size-3 axis — is preserved.Tests
tests/test_histogram_indexing.py:test_negative_index_access— negative scalars, slices, one-sided, dict-form, per-axis length on 2D.test_out_of_range_slice_clamps_like_numpy— extents matched againstnp.arangefor clamped cases.test_empty_slice_raises—IndexErrorfor the empty cases, including reversedh[5:2:sum].One pre-existing assertion in
test_single_flow_binwas updated:h[3::sum]on a 3-category axis (start past the last bin) now raises the clearerIndexErrorinstead ofValueError.Verified slice extents match
numpyacross negative/out-of-range/empty cases. Full suite: 1127 passed, 1 xfailed.Note on the
Hist 🐍 3.14CI job: any failure there is unrelated — the downstream hist test suite passes; only its trailingmypystep trips on a numpy-stubs/Python-3.14 version mismatch (numpy/__init__.pyi: Type statement is only supported in Python 3.12 and greater).