Skip to content

Fix stack overflow in quantile_mut / selection routines on large constant arrays (#86)#116

Open
teddytennant wants to merge 1 commit into
rust-ndarray:masterfrom
teddytennant:fix/quantile-stack-overflow-86
Open

Fix stack overflow in quantile_mut / selection routines on large constant arrays (#86)#116
teddytennant wants to merge 1 commit into
rust-ndarray:masterfrom
teddytennant:fix/quantile-stack-overflow-86

Conversation

@teddytennant

Copy link
Copy Markdown

What

Rewrites the quickselect-based selection routines in src/sort.rs so that recursion depth is bounded to O(log n):

  • _get_many_from_sorted_mut_unchecked now recurses into the sub-problem with fewer array elements after each partition_mut and iterates (via a loop) on the larger one, instead of recursing into both sides.
  • Sort1dExt::get_from_sorted_mut now narrows its view in place inside a loop (via slice_axis_inplace) instead of recursing.

Adds a regression test in tests/quantile.rs.

Why

Both routines previously recursed once per partition step. On a large constant (all-equal) array, Hoare's partition_mut places the pivot at index 0, so each step peels off exactly one element and recurses on the remaining n - 1 — an O(n) recursion depth. For large inputs (e.g. the median of Array1::<N64>::ones(100_000)) this overflows the thread stack and aborts the whole process rather than returning a result.

Recursing only into the smaller partition and iterating on the larger one is the standard technique for bounding quickselect's stack depth to O(log n). The rearranged elements and returned order statistics are unchanged; only stack usage is bounded.

Fixes #86.

Testing

  • Added quantile_mut_on_large_constant_array_does_not_overflow_stack, which aborts with thread has overflowed its stack on master and passes with this change.
  • cargo test passes in full, including the existing quickcheck quantile properties (test_quantiles_mut, test_quantiles_axis_mut) that compare bulk vs. single-quantile results, and the get_many_from_sorted_mut unit tests in src/sort.rs.
  • cargo fmt --check is clean.

The quickselect-based selection routines in `src/sort.rs` recursed once
per partition step. On pathological inputs such as a large constant
(all-equal) array, Hoare's partition peels off only a single element at
each step, so the recursion depth grows linearly with the array length.
For large arrays this overflows the thread stack and aborts the process
(e.g. taking the median of `Array1::<N64>::ones(100_000)`).

Rewrite `_get_many_from_sorted_mut_unchecked` and
`Sort1dExt::get_from_sorted_mut` to recurse into the sub-problem with
fewer array elements and iterate on the larger one, the standard
technique that bounds the recursion depth to O(log n). The computed
results are unchanged; only the stack usage is bounded.

Closes rust-ndarray#86
Copilot AI review requested due to automatic review settings July 8, 2026 17:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

quantile_mut: fatal runtime error: stack overflow

2 participants