Releases: ProjectTorreyPines/FastInterpolations.jl
v0.4.9
What's New
Extended Grid Type Support (#116, #117, #118, #119, #120)
All interpolation methods now support ForwardDiff.Dual and Measurements.Measurement as grid coordinates, enabling grid-sensitivity AD and uncertainty propagation. Previously only linear_interp handled these types; now cubic, quadratic, pchip, cardinal, akima, constant, and hermite all work. (Closes #81)
using FastInterpolations, ForwardDiff
x = 0:10
y = x.^3
xq = [1.2, 2.4]
# Grid-sensitivity: ∂/∂t of interpolated values w.r.t. grid scaling
ForwardDiff.derivative(t -> cubic_interp(t * x, y, xq), 2.0)using FastInterpolations, Measurements
x = [1.0 ± 0.1, 2.0 ± 0.1, 3.0 ± 0.1, 4.0 ± 0.1, 5.0 ± 0.1]
y = [1.0, 4.0, 9.0, 16.0, 25.0]
cubic_interp(x, y, 2.5) # → 6.25 ± 0.45Zero-Alloc Integer Grid One-Shot (#121, #122)
Integer grids (Vector{Int}, UnitRange{Int}) now achieve zero heap allocation on one-shot scalar and in-place batch paths — all 8 methods. Previously, Int grids allocated a new Vector{Float64} per call (96–672 bytes).
What's Changed
- (feat): duck-typed grid support for linear interpolation by @mgyoo86 in #116
- (feat): Hermite family duck-typed grid support by @mgyoo86 in #117
- (feat): Constant + Quadratic duck-typed grid support by @mgyoo86 in #118
- (feat): Cubic — duck-typed grid support by @mgyoo86 in #119
- (feat): ND Dual Grid — duck-typed grid support for all ND paths by @mgyoo86 in #120
- (refac): Refactor type promotion — eliminate unnecessary allocations by @mgyoo86 in #121
- (perf): Zero-alloc Int grid one-shot interpolation by @mgyoo86 in #122
Full Changelog: v0.4.8...v0.4.9
v0.4.8
What's New
Local Cubic Hermite Family (#106, #108, #113, #114)
New C¹ local cubic Hermite interpolation family — four C¹ variants sharing a single cubic Hermite basis, differing only in how per-cell slopes are determined:
hermite_interp(x, y, dydx, xq) # user-supplied slopes
pchip_interp(x, y, xq) # Fritsch-Carlson (monotone-preserving)
cardinal_interp(x, y, xq; tension=0.5) # Catmull-Rom with tension
akima_interp(x, y, xq) # Akima (outlier-robust)Full 1D API: native adjoints, analytic derivatives, integrate / cumulative_integrate(!), and AutoDiff support. ND forward evaluation works through both the per-method entry points (e.g. pchip_interp((x, y), data)) and the unified interp API; ND adjoint is on the roadmap.
OnTheFly Coefficient Strategy + AutoCoeffs (#109, #110, #112)
New coeffs keyword chooses between building coefficients upfront (PreCompute() — more memory and build time, fastest eval) or deriving them per query from a local stencil (OnTheFly() — zero build cost and lower memory, but slower per query, especially for Quadratic / Cubic). AutoCoeffs() — the new default — picks automatically based on the call shape.
cubic_interp(x, y, 0.5) # auto → OnTheFly (scalar)
cubic_interp(x, y, queries) # auto → PreCompute (batch)
itp = cubic_interp(x, y; coeffs=OnTheFly()) # explicit overrideFor the Hermite family, the ND path now uses a cell-local stencil instead of scanning the full fiber, with substantial speedups on large grids.
Fixes
- ND mixed-partial BC consistency (#111) — Clairaut symmetry (
∂²/∂x∂y = ∂²/∂y∂x) restored across every supported boundary condition;PreComputeandOnTheFlypaths are now ULP-equivalent.
What's Changed
- (feat): Add local cubic Hermite interpolation family (Hermite, PCHIP, Cardinal, Akima) by @mgyoo86 in #106
- (feat): Hermite Family 1D — Adjoint, Integration, AD, and API Refactoring by @mgyoo86 in #108
- (feat):
OnTheFlyCoefficient Strategy +AutoCoeffsSmart Resolution by @mgyoo86 in #109 - (perf): zero-alloc
OnTheFlyND +AutoCoeffsdefault for scalar oneshot by @mgyoo86 in #110 - (fix): ND mixed-partial BC consistency — restore Clairaut symmetry by @mgyoo86 in #111
- (perf): cell-local OnTheFly for Hermite ND by @mgyoo86 in #112
- (feat): Hermite 1D — OnTheFly integrate + in-place
cumulative_integrate!by @mgyoo86 in #113 - (feat): pchip/cardinal/akima — improve ND entry API by @mgyoo86 in #114
Full Changelog: v0.4.7...v0.4.8
v0.4.7
New Features
One-Shot Series Interpolation (#102)
Evaluate multiple y-series at shared query points without constructing an interpolant. All 4 methods supported (constant, linear, quadratic, cubic), with PeriodicBC and duck-typed values.
cubic_interp(x, Series(y_sin, y_cos), 0.5) # allocating
cubic_interp!(out, x, Series(y_sin, y_cos), xq) # zero-allocHeterogeneous ND Adjoint & AD (#98)
HeteroAdjointND — adjoint operator for per-axis heterogeneous interpolation. Mixed-radix compact storage, zero-alloc Val(d) recursive dispatch. Full Zygote/ForwardDiff/Enzyme integration.
adj = hetero_adjoint((x, y), (xq, yq); methods=(CubicInterp(), LinearInterp()))
adj(f_bar, y_bar) # zero-alloc in-placeArbitrary DerivOp{N} (#99, #101)
Removed N ≤ 3 restriction — orders beyond polynomial degree return zero via generic fallbacks. All paths covered: scalar, ND, adjoint, series, deriv_view.
What's Changed
- (fix): restore rtol in periodic endpoint checks by @mgyoo86 in #97
- (feat): Adjoint operator for heterogeneous ND interpolation by @mgyoo86 in #98
- Arbitrary DerivOp{N} — remove N≤3 restriction with zero-returning fallbacks by @mgyoo86 in #99
- (fix): add DerivOp{4+} fallbacks for series interpolant paths by @mgyoo86 in #101
- (feat): One-shot Series interpolation by @mgyoo86 in #102
- (test): Enable AdaptiveArrayPools
runtime_checkin test environment by @mgyoo86 in #103 - (refactor): Enforce
_CachedRangenormalization at all API boundaries by @mgyoo86 in #104 - (refac): Unified Anchor Infrastructure Refactor by @mgyoo86 in #105
Full Changelog: v0.4.6...v0.4.7
v0.4.6
What's New
1. Unified interp API — Homogeneous & Heterogeneous Methods (#91)
The new unified interp API supports different interpolation methods for each axis, in addition to the homogeneous method as usual.
# Homogeneous method
itp = interp((x, y), data; method=CubicInterp())
# Heterogeneous methods
itp = interp((x, y), data; method=(LinearInterp(), CubicInterp()))
itp((0.5, 0.3))
# In-place one-shot with heterogeneous methods
interp!(out, (x, y), data, (0.5, 0.3); method=(LinearInterp(), CubicInterp()))2. GridIdx(k) — Efficient Slicing at Known Grid Points (#92, #93)
Evaluate at exact grid indices to bypass the grid search phase entirely.
# eval at x=0.5 on the 10th y-grid point
itp((0.5, y[10]))
# same result, but faster (skips grid search)
itp((0.5, GridIdx(10))) 3. NoInterp() — Skip Interpolation on Discrete Axes (#92)
Axes marked NoInterp() skip precomputation and kernel entirely — both build and eval reduce to the interpolated dimensions only:
itp = interp((lon, depth, time), temp; method=(CubicInterp(), LinearInterp(), NoInterp()))
itp((45.3, 1200.0, GridIdx(7))) # 7th time slice, only 2D interp cost5. nodal_partials — Access Precomputed Derivatives at Grid Nodes (#96)
New public API to extract precomputed partial derivatives stored at grid nodes. Returns a zero-copy view, replacing brittle internal field access with a stable, validated interface.
6. Relaxed Periodic Endpoint Check (#94)
PeriodicBC()usesisapprox(atol = 8eps(T)) instead of strict==—sin.(range(0, 2π, N))now works out of the boxPeriodicBC(check=false)opt-out for scaled data
See Unified interp API for full details.
What's Changed
- Unified
interpAPI with per-axis heterogeneous methods by @mgyoo86 in #91 - (feat): Add
NoInterp()andGridIdxfor per-axis discrete slicing by @mgyoo86 in #92 - (feat): Scalar Coordinate Protocol:
GridIdx <: Realby @mgyoo86 in #93 - (refac): Relax periodic endpoint check +
PeriodicBC(check=false)opt-out by @mgyoo86 in #94 - (refac): Rename
TensorProductInterpolantNDtoHeteroInterpolantNDby @mgyoo86 in #95 - (feat):
nodal_partials— Public API for Precomputed Partial Derivatives by @mgyoo86 in #96
Full Changelog: v0.4.5...v0.4.6
v0.4.5
What's New
1. Full AD Support — All 4 Interpolant Types
- Zygote and Enzyme now support constant, linear, quadratic, and cubic interpolation (previously cubic-only).
- Both 1D one-shot functions and ND struct API work out of the box.
- Internally, 8 separate CRC rrules consolidated into 1 via a shared trait-dispatch layer (
_InterpMethod). (#89)
2. Complete Adjoint Operator Suite
- All four interpolant types —
constant_adjoint,linear_adjoint,quadratic_adjoint,cubic_adjoint— now have matrix-freeWᵀȳsupport in both 1D and ND for∂loss/∂data. (#83, #84)
3. _CachedRange — Unified Range Normalizer
- New internal
_CachedRange{T}cachesfirst,last,step,inv(step)as plainT, bypassingTwicePrecisionoverhead on Intel x86. - Range grid search is 5.7x faster on Intel; ARM is unaffected. Fixes #85. (#87)
4. InBounds() Extrap Type
- New
extrap=InBounds()skips domain checks for pre-validated queries. - Batch
NoExtrapqueries automatically convert toInBounds()after upfront validation — type-stable and zero-cost. (#88)
Performance
AbstractGridSpacingextended to quadratic solver, integration, and coefficients paths — eliminatesTwicePrecisionoverhead on Range grids across remaining hot paths (#86)
Internal
- Unified Range/Vector eval via
_get_h/_get_inv_haccessors — removes duplicate codepaths (#88) - AD trait layer (
ad_traits.jl) shared by CRC and Enzyme extensions (#89) - ND adjoint scatter infrastructure extracted to
core/nd_adjoint_scatter.jlfor cross-interpolant reuse (#84)
What's Changed
- (feat): Constant Adjoint Operator (1D + ND) by @mgyoo86 in #83
- (feat): QuadraticAdjoint 1D + ND — adjoint operator for quadratic splines by @mgyoo86 in #84
- (perf): extend
AbstractGridSpacingto remaining hot paths by @mgyoo86 in #86 - (feat): introduce
_CachedRangeunified Range normalizer (fixes #85) by @mgyoo86 in #87 - (refac): introduce
InBounds()and tidy-ups by @mgyoo86 in #88 - (feat): Full Zygote/Enzyme Support for All Interpolant Types by @mgyoo86 in #89
Full Changelog: v0.4.4...v0.4.5
v0.4.4
What's New
Linear Adjoint Operator (1D + ND)
Matrix-free Wᵀȳ for linear interpolation, matching the cubic adjoint API. Supports all extrap modes, 1D and arbitrary-dimension grids. (#77)
Bug Fixes
- Mutation safety — Constructors now defensively copy grids/data. Zero-cost for
Rangeinputs. (#82) - ND NoExtrap pre-validation — Domain checks run before
@inboundseval loop. (#78)
Internal
- Unified 1D callable protocol via
AbstractInterpolant1D(#79) - AdaptiveArrayPools v0.3 API update (
unsafe_acquire!→acquire!)
What's Changed
- (feat): Linear Adjoint Operator (1D + ND) by @mgyoo86 in #77
- (fix): Pre-loop NoExtrap domain validation for all ND eval paths by @mgyoo86 in #78
- (refac): unify 1D callables & remove ND query protocol by @mgyoo86 in #79
- (fix): mutation safety — defensive copy in all constructors by @mgyoo86 in #82
Full Changelog: v0.4.3...v0.4.4
v0.4.3
What's New
Extensible Query Protocol for ND Evaluation
ND batch APIs now accept any query container type via a 3-function protocol — SVector, custom containers, etc. work out of the box. Sorted AoS queries automatically get LinearBinarySearch (~25–55% faster).
AbstractAdjointND Type Hierarchy
New abstract type for ND adjoint operators. CubicAdjointND now subtypes it; future adjoint operators inherit shared callables for free.
Internal
- –169 net lines: unified 8 duplicate SoA/AoS batch methods into protocol-based paths
- Zero-alloc AD pullback paths (ChainRules/Enzyme)
What's Changed
Full Changelog: v0.4.2...v0.4.3
v0.4.2
What's New
Cubic Adjoint Operator — Matrix-Free Wᵀȳ
Native matrix-free adjoint for 1D and ND cubic spline interpolation. Computes f̄ = Wᵀȳ without materializing the weight matrix — O(n+m) with zero allocation (in-place). Enables efficient ∂loss/∂data for inverse problems, data assimilation, and PDE-constrained optimization.
Zygote and Enzyme use the adjoint operator automatically via registered AD rules — ∂loss/∂data works out of the box.
# Build once (query-baked, data-free)
adj = cubic_adjoint(x, xq)
f̄ = adj(ȳ) # allocating
adj(f̄, ȳ) # in-place, zero-alloc
# AD backends use it automatically
using Zygote
grad = Zygote.gradient(f -> sum(cubic_interp(x, f, xq)), f)[1]1D Adjoint Docs 📖 · ND Adjoint Docs 📖 (#69, #71)
value_gradient
Computes value and gradient in a single call with one interval search. Zero-allocation on tuple queries. Works with all ND interpolant types.
Bug Fixes
- Exclusive periodic BC type instability — inline
isabranch replaces_extend_gridUnion return (#64) - Mixed-precision FillExtrap/ClampExtrap — OOB path now promotes result to match kernel return type via arithmetic (#74)
What's Changed
- Fix/type stability exclusive periodic by @mgyoo86 in #64
- (ci): Two-tier benchmark regression verification with auto re-runs by @mgyoo86 in #65
- Streamline plots by @JeffFessler in #66
- Tweak benchmark readme by @JeffFessler in #67
- (feat):
CubicAdjointoperator for 1D cubic spline interpolation by @mgyoo86 in #69 - (feat): CubicAdjointND — N-Dimensional Cubic Adjoint Operator by @mgyoo86 in #71
- (feat): new
value_gradientAPI for ND Interpolants by @mgyoo86 in #73 - Fix mixed-precision type instability in FillExtrap/ClampExtrap OOB paths by @mgyoo86 in #74
Full Changelog: v0.4.1...v0.4.2
v0.4.1
New: FillExtrap(v)
Returns a user-specified constant for out-of-domain queries. (Docs 📖)
itp = cubic_interp(x, y; extrap=FillExtrap(NaN)) # 1D
itp = cubic_interp((x, y), data; extrap=FillExtrap(0.0)) # ND
itp = cubic_interp(x, y; extrap=Extrap(:fill; fill_value=NaN)) # factory-style Deprecation: ConstExtrap → ClampExtrap
Renamed to better reflect its behavior (clamp to boundary value). Old names remain functional with depwarn. (Docs 📖)
| Deprecated | Replacement |
|---|---|
ConstExtrap() |
ClampExtrap() |
Extrap(:constant) |
Extrap(:clamp) |
Bug Fixes
- ND autodiff TypeError — ChainRulesCore extension updated for
DerivOpAPI (was still passingInttuples) (Docs 📖) - AD generalized to all methods —
frule/rrulenow dispatch onAbstractInterpolant/AbstractInterpolantND, not just Cubic (Docs 📖) - Complex-valued pullback — corrected adjoint to
real(conj(Δy) * f'(x))forf: R → C
Style
- Applied Runic.jl formatting (173 files) + added CI enforcement workflow
What's Changed
- (feat): Rename
ConstExtrap→ClampExtrapand addFillExtrapby @mgyoo86 in #61 - (fix): ChainRulesCore extension + improve AD docs by @mgyoo86 in #62
- (style): Apply Runic formatting and add CI check by @mgyoo86 in #63
Full Changelog: v0.4.0...v0.4.1
v0.4.0
⚠️ Breaking Changes
1. Multi-series API changed
Passing bare Matrix or Vector{Vector} to create a SeriesInterpolant is no longer supported. Use the Series() wrapper:
# Before
itp = cubic_interp(x, [y1, y2])
# After (v0.4)
itp = cubic_interp(x, Series(y1, y2))2. PeriodicBC() strict endpoint check
The default :inclusive mode now checks y[1] == y[end] (strict) instead of isapprox. This catches silent data errors from floating-point periodic data (e.g., sin(2π) ≈ -2.4e-16 ≠ 0.0). Ensure your periodic data satisfies y[1] === y[end] exactly.
📖 See details:
New Features
Custom value types
- Any vector-space type (
SVector, measurement types, etc.) that supports+,-,*now works across all interpolation methods, BCs, derivatives, and ND paths. (docs)
Adjoint AD (∂f/∂y)
- Compute derivatives with respect to data values via
ForwardDiff, enabled by the custom value type support above. (docs)
What's Changed
- Introduce
Serieswrapper for multi-series interpolation by @mgyoo86 in #53 - Support custom value types (Duck-typing) by @mgyoo86 in #54
- Add
Aqua.jlQuality Assurance by @mgyoo86 in #57
Full Changelog: v0.3.1...v0.4.0