Skip to content

Sorted-batch evaluators for the remaining piecewise interpolation types (7-13x)#528

Draft
ChrisRackauckas-Claude wants to merge 2 commits into
SciML:masterfrom
ChrisRackauckas-Claude:sorted-batch-remaining-types
Draft

Sorted-batch evaluators for the remaining piecewise interpolation types (7-13x)#528
ChrisRackauckas-Claude wants to merge 2 commits into
SciML:masterfrom
ChrisRackauckas-Claude:sorted-batch-remaining-types

Conversation

@ChrisRackauckas-Claude
Copy link
Copy Markdown
Contributor

Summary

Extends the sorted-batch fast path to all remaining piecewise types with a fixed per-segment polynomial form:

  • ConstantInterpolation (both :left and :right directions)
  • QuadraticInterpolation
  • QuadraticSpline
  • CubicHermiteSpline
  • QuinticHermiteSpline

Pairs with #527 (Linear + CubicSpline) and the earlier #526 (Akima). After both this PR and #527 land, every piecewise interpolation type in DataInterpolations.jl has an O(n+m) batch evaluator for sorted queries.

This PR is stacked on #527 — branched off sorted-batch-linear-cubic. If reviewed first I'll rebase onto master once #527 lands. Order-independent otherwise.

Why a shared helper now

Adds a _sorted_batch_extrapolation_supported(A) predicate used by all five new evaluators (None / Constant / Linear / Extension on each side). The earlier per-type predicates (_akima_eval_fast_applicable, _linear_eval_fast_applicable, _cubicspline_eval_fast_applicable) are left in place to keep this PR's diff focused on the new types; they're all equivalent and can be folded into the shared helper in a 5-line follow-up if desired.

Benchmarks

Julia 1.10, n=256 knots, m=4096 sorted queries, BenchmarkTools minimum:

Type before after speedup
ConstantInterpolation 107 μs 14.6 μs 7.3×
QuadraticInterpolation 166 μs 15.2 μs 10.9×
QuadraticSpline 858 μs 67.5 μs 12.7×
CubicHermiteSpline 167 μs 16.8 μs 9.9×
QuinticHermiteSpline 278 μs 26.9 μs 10.3×

QuadraticSpline gets the biggest absolute win because its per-segment cost is dominated by get_parameters(A, idx) (the segment α, β coefficients) — the lockstep walk amortizes that across all queries in the segment instead of recomputing per query.

What is in this PR

  • src/interpolation_methods.jl:
    • Adds the shared _sorted_batch_extrapolation_supported(A) helper near the top.
    • Adds (A::T)(out, tt) callables + the corresponding _*_eval_sorted! inner loops for each of the 5 types, each following the same shape: dispatch on extrapolation type outside the per-region loop, hoist segment-local coefficients once per segment.
    • CubicHermiteSpline and QuinticHermiteSpline use the Hermite property that the slope at each knot equals du[idx], so Linear extrapolation reduces to a single multiply.
    • ConstantInterpolation's eltype constraint is relaxed to <:AbstractVector (not <:AbstractVector{<:Number}) so it works for non-numeric data like ["a", "b", ...] — no arithmetic is performed.
  • test/interpolation_tests.jl: "Sorted-batch evaluator" sub-testsets in each type's section. Coverage matches Optimize AkimaInterpolation constructor (3.6-4.1x) and add sorted-batch evaluator (8-9x) #526 and Sorted-batch evaluators for LinearInterpolation and CubicSpline (~9x speedup) #527: all fast-path extrapolation modes paired left × right, knot pass-through, Periodic/Reflective fallback, None throwing, unsorted fallback, DimensionMismatch, plus cache_parameters=true for QuadraticSpline and the non-Number eltype path for ConstantInterpolation.

What is intentionally NOT in this PR

SmoothedConstantInterpolation — its boundary smoothing means adjacent queries within the same segment can fall in either the smoothed region (at the segment edges) or the constant region (in the middle), so the per-segment hoist pattern doesn't apply cleanly. Worth a separate PR if someone wants it.

Test plan

  • CI passes
  • Pkg.test() all groups pass locally on Julia 1.10 (Core 5036 / Methods 42151 / Extensions 13178 / Misc 11)
  • Output matches the per-point path bitwise on all test inputs (verified via max diff check before adding the testset)
  • Runic-formatted

Please ignore until reviewed by @ChrisRackauckas.

🤖 Generated with Claude Code

Extends the pattern from SciML#526 to two more piecewise interpolation types.
When the query vector is sorted and the extrapolation modes are simple
(None / Constant / Linear / Extension), walk the knots and queries in
lockstep in O(n+m) instead of running a per-query binary search via the
iguesser. Falls back to `map!` when the inputs don't fit the fast path:
unsorted `tt`, Periodic/Reflective extrapolation, or (for
LinearInterpolation) `u` containing NaN — preserving the existing
NaN-handling semantics from `_interpolate(::LinearInterpolation, ...)`.

The scalar `_interpolate`, derivative, integral, AD, and plotting paths
are unchanged. Output matches the per-point path to floating-point
round-off, verified across each fast-path extrapolation mode paired
left × right, both `cache_parameters` settings for CubicSpline, knot
pass-through, the Periodic/Reflective fallback, the `None` throwing
behavior on out-of-range sorted queries, and the NaN-in-u fallback for
LinearInterpolation.

Benchmarks (Julia 1.10, n=256, m=4096 sorted queries, BenchmarkTools
`minimum`):

  LinearInterpolation:  126 us  ->  13.6 us   9.3x
  CubicSpline:          167 us  ->  19.7 us   8.5x

Unsorted-input fallback path stays at ~450 us for both (matches the
prior `map!` cost; the `issorted` short-circuits on the first
out-of-order pair).

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Extends the sorted-batch fast path to the rest of the piecewise types
that have a fixed per-segment polynomial form:

  - ConstantInterpolation (both :left and :right dir)
  - QuadraticInterpolation
  - QuadraticSpline
  - CubicHermiteSpline
  - QuinticHermiteSpline

Also extracts a shared `_sorted_batch_extrapolation_supported(A)`
predicate (None / Constant / Linear / Extension on each side), used by
all five new evaluators. The earlier per-type predicates for Akima /
Linear / CubicSpline are left in place to keep this PR's diff focused
on the new types; they can be folded into the shared helper in a
follow-up if desired.

The scalar `_interpolate`, derivative, integral, AD, and plotting paths
are unchanged. Output matches the per-point path to floating-point
round-off across every fast-path extrapolation mode paired left × right,
both `cache_parameters` settings for QuadraticSpline, knot
pass-through, Periodic/Reflective fallback, the `None` throwing
behavior on out-of-range sorted queries, the unsorted fallback, and
non-Number eltypes for ConstantInterpolation.

Benchmarks (Julia 1.10, n=256, m=4096 sorted queries, BenchmarkTools
`minimum`):

  Type                       before     after     speedup
  ConstantInterpolation      107 us     14.6 us     7.3x
  QuadraticInterpolation     166 us     15.2 us    10.9x
  QuadraticSpline            858 us     67.5 us    12.7x
  CubicHermiteSpline         167 us     16.8 us     9.9x
  QuinticHermiteSpline       278 us     26.9 us    10.3x

SmoothedConstantInterpolation is intentionally left out: its boundary
smoothing logic means adjacent queries can fall in the smoothed region
or in the constant region within the same segment, which doesn't fit
cleanly into the per-segment hoist pattern used here. Worth a separate
focused PR if anyone wants it.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
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.

2 participants