Native reverse-order dispatch (closes #67)#68
Merged
ChrisRackauckas merged 1 commit intoMay 20, 2026
Conversation
Closes SciML#67. Drops the `BinaryBracket` / `LinearScan` fallback paths that `InterpolationSearch`, `ExpFromLeft`, `SIMDLinearScan`, and `BitInterpolationSearch` used to take for `Base.Order.Reverse` ordering. Approach per strategy: - **InterpolationSearch**: the linear-extrapolation guess `(x - vlo) / (vhi - vlo)` works for both polarities of `vlo`/`vhi` (negative span for `Reverse`-sorted decreasing data) — the ratio still lands in [0, 1] for queries inside the range. Dropping the Forward-only guard makes it native for both orderings via `BracketGallop`'s already-order-aware refinement. - **ExpFromLeft**: parameterised `searchsortedfirstexp` on `order`, with `!lt(order, v[ind], x)` replacing the hardcoded `x <= v[ind]`. Added a sibling `searchsortedlastexp` that uses the strict comparison `lt(order, x, v[ind])` to find the last position correctly — the old code post-processed `searchsortedfirstexp`'s result with `isequal(v[y], x) ? y : y - 1`, which returned the FIRST occurrence when `x` had duplicates instead of the LAST. The duplicates bug was pre-existing under Forward too but never exercised because the prior custom-order tests used a no-duplicates `10.0:-1.0:1.0` range; the new Reverse parity tests on random Int64 in [-100, 100] across 200 elements (lots of duplicates) caught it. - **SIMDLinearScan**: four new LLVM IR predicates generated from the existing `_simd_scan_ir(t, pred)` template: `slt` / `sle` for Int64 and `olt` / `ole` for Float64. Backing primitives `_simd_first_lt` / `_simd_first_le` in `simd_ir.jl`. `_simdscan_last_specialized` and `_simdscan_first_specialized` now take an `order::Ordering` argument and branch on `order === Forward` vs `Reverse` at runtime — the branch compiles away in concrete callsites where order is statically known. A `_simd_supported_order` helper gates the SIMD path to `Forward` and `Reverse` only; custom orderings (`By`, `Lt`, etc.) fall back to scalar `LinearScan` (which is order-agnostic). - **BitInterpolationSearch**: `_bit_interp_guess_f64` branches on the ordering and mirrors the unsigned arithmetic for the descending case (`span = vlo_bits - vhi_bits`, `num = vlo_bits - xu`). A new `_bit_interp_eligible` helper folds the positivity / finiteness / supported-order guards into one check. Custom orderings fall back to `BinaryBracket`. Tests: a new "Native reverse-order paths (issue SciML#67)" testset adds: - 5000 random parity comparisons each on Reverse-sorted Float64 and Int64 vectors of length 200, across every shipped strategy at every hint position, vs `Base.searchsorted{first,last}(v, x, Reverse)`. - 400 parity comparisons specifically for `BitInterpolationSearch` on Reverse log-spaced positive Float64 (the strategy's intended regime), plus signed-Float64 fallback checks. - Edge cases: queries past either end of the array, hint at boundary. 100047 tests pass (was 84626; +15421 from the new parity tests). Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
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.
Closes #67. Implements native
Base.Order.Reversepaths for the fourstrategies that used to fall back to
BinaryBracket/LinearScan:InterpolationSearch,ExpFromLeft,SIMDLinearScan,BitInterpolationSearch.Approach
InterpolationSearch: the existing
_interp_guessmath(
(x - vlo) / (vhi - vlo)) handles negative span for Reverse-sorteddata — the ratio still lands in [0, 1] for queries inside the range.
Dropping the Forward-only guard makes it native via
BracketGallop'salready-order-aware refinement.
ExpFromLeft: parameterised
searchsortedfirstexponorder(
!lt(order, v[ind], x)replacing the hardcodedx <= v[ind]).Added a sibling
searchsortedlastexpusing the strict comparisonlt(order, x, v[ind])to find the last position correctly. The oldcode post-processed
searchsortedfirstexp's result withisequal(v[y], x) ? y : y - 1, which returned the FIRST occurrencewhen
xhad duplicates. The duplicates bug was pre-existing underForward too but never exercised because the prior custom-order tests
used a no-duplicates
10.0:-1.0:1.0range; the new Reverse paritytests on random Int64 in [-100, 100] across 200 elements (lots of
duplicates) caught it.
SIMDLinearScan: four new LLVM IR predicates generated from the
existing
_simd_scan_ir(t, pred)template:slt/slefor Int64and
olt/olefor Float64. Backing primitives_simd_first_lt/_simd_first_leinsimd_ir.jl._simdscan_last_specializedand_simdscan_first_specializednow take anorder::Orderingargumentand branch on
ForwardvsReverseat runtime — the branchcompiles away when order is statically known. Non-Forward /
non-Reverse orderings fall back to scalar
LinearScan(order-agnostic).
BitInterpolationSearch:
_bit_interp_guess_f64branches on theordering and mirrors the unsigned arithmetic for the descending case
(
span = vlo_bits - vhi_bits,num = vlo_bits - xu). A new_bit_interp_eligiblehelper folds the positivity / finiteness /supported-order guards into one check.
Tests
A new "Native reverse-order paths (issue #67)" testset:
Int64 vectors of length 200, across every shipped strategy at every
hint position, vs
Base.searchsorted{first,last}(v, x, Reverse).BitInterpolationSearchon Reverselog-spaced positive Float64 (the strategy's intended regime), plus
signed-Float64 fallback checks.
100047 tests pass (was 84626; +15421 from the new parity tests).
Bonus item deliberately not done
The "generalize beyond
Reverseto arbitrary user-supplied orderings"bonus from the issue is not implemented.
SIMDLinearScanandBitInterpolationSearchuse raw machine compares (icmp/fcmp) thataren't aware of
Base.Order, so customBy/Ltorderings continueto fall back to scalar
LinearScan(which is order-agnostic byconstruction).
🤖 Note for reviewer: this PR is in draft. Please ignore until reviewed
by @ChrisRackauckas.