Release notes
ConvolutionInterpolations.jl
The notes of every release, as published on GitHub, newest first. Each release is archived on Zenodo under its own DOI; the citation file in the repository has the details.
v0.28.2 — 2026-10-14
ConvolutionInterpolations v0.28.2
ConvolutionInterpolations.jl v0.28.2
Adds a CITATION.cff file so the package can be cited, with each release archived on Zenodo under a DOI. No code changes.
Breaking changes
None.
Migration guide
No changes needed.
v0.28.1 — 2026-09-29
ConvolutionInterpolations v0.28.1
ConvolutionInterpolations.jl v0.28.1
Patch release: Relaxes the Project compat field to be less restrictive in the patch version. Furthermore, the readme has been updated with the correct new integral orders.
v0.28.0 — 2026-09-28
ConvolutionInterpolations v0.28.0
ConvolutionInterpolations.jl v0.28.0
Highlights
Scattered data are now smoothed by default. fit_scattered and convolution_interpolation(points, values) now use mode = :smooth unless told otherwise. Scattered data are usually measurements, and even a little noise is harmful under exact interpolation: the high-order kernels reproduce the noise and amplify it between the points, most of all in derivatives. With smoothing as the default, noisy data give sound results without any tuning, while noise-free data still come out close to interpolation. Trusted, noise-free data can opt into exact interpolation with mode = :exact.
Faster, more reliable smoothing defaults. :smooth now fits with the :a4 kernel and roughness order q = 5 for every kernel. With noisy data the accuracy is set by the noise rather than the kernel, and :a4’s narrow stencil makes the fit 2–3× faster than before in 2D and 3–9× faster in 3D, at comparable accuracy. Wider kernels such as :b7 also become reliable in smoothing mode.
Clearer feedback. :smooth warns when the grid limit set by oversample stops the fit from resolving the chosen smoothing. It also raises a clear error when there are too few data points to smooth, which means 5^D or fewer: 25 in 2D, 125 in 3D.
Breaking changes
- The default
modeoffit_scatteredand ofconvolution_interpolation(points, values)is now:smoothinstead of:exact. Calls withoutmodenow smooth the data rather than interpolate it. - With the new default, the third value returned by
fit_scatteredis the named tuple(lambda, noise)instead of the constraint violationviol. :smoothdefaults to the:a4kernel andq = 5, so results ofmode = :smoothwithout an explicitkernelorqdiffer slightly from v0.27.0.:smoothraises an error for 5^D or fewer data points.
Migration guide
- For exact interpolation of trusted data, add
mode = :exact:convolution_interpolation(points, values)becomesconvolution_interpolation(points, values; mode = :exact), and the same applies tofit_scattered. Results are then identical to v0.27.0. - Code that used the third return value of
fit_scatteredasviolshould passmode = :exact. - For noisy data, no change is needed: the default now does the right thing. To reproduce v0.27.0’s smoothing exactly, pass
kernel = :b5. - For very small data sets, use
mode = :exact, or pass a lowerq.
v0.27.0 — 2026-09-27
ConvolutionInterpolations v0.27.0
ConvolutionInterpolations.jl v0.27.0
Highlights
Smoothing of noisy scattered data: mode = :smooth
fit_scattered and convolution_interpolation(points, values) gain mode = :smooth for noisy scattered data. Instead of passing through every point, the fit balances closeness to the data against smoothness, and chooses that balance itself by generalized cross-validation, so there is no smoothing parameter to tune. The result is a high-order interpolant like any other: evaluate it, differentiate it, integrate it.
itp = convolution_interpolation(points, values; mode=:smooth) # smoothing chosen automatically
knots, vals, info = fit_scattered(points, values; mode=:smooth) # the fit, with diagnostics
info.noise # estimated noise level of the data
info.lambda # the chosen smoothing; pass it back to reproduce the fitWhy it matters. The previous options for noisy scattered data each needed hand-tuning and fell short. The scattered_to_grid + convolution_smooth pipeline left the imprint of the nearest-neighbour gridding in the result and blurred fine features. mode = :lsq fitted the noise at its default settings and only worked once its grid density was tuned by hand for each noise level and signal. In my comparisons, :smooth with its automatic choice matched or beat both of them even at their best hand-tuned settings, with lower errors in the values and derivatives. It also reports an estimate of the data’s noise level, a useful check against a known measurement error. It stays fast: the fit’s grid is refined only as far as the chosen smoothing needs.
Breaking changes
fit_scattered(...; mode = :lsq)has been removed and now raises anArgumentError.mode = :smoothreplaces it.- With
mode = :smooth, the third value returned byfit_scatteredis a named tuple(lambda, noise)instead of a number.mode = :exactstill returns the constraint violation.
Deprecations
scattered_to_gridis deprecated and will be removed in v1.0. It still works, with a one-time warning.
Migration guide
Least-squares fits of noisy scattered data:
# before
knots, vals, _ = fit_scattered(points, values; mode=:lsq, gridsize=14)
# after: the grid and the smoothing are chosen automatically
knots, vals, info = fit_scattered(points, values; mode=:smooth)The gridding-and-smoothing pipeline:
# before
grid = scattered_to_grid(points, values, (xs, ys))
itp = convolution_interpolation((xs, ys), convolution_smooth((xs, ys), grid, 0.1))
# after: fit and smooth in one step
itp = convolution_interpolation(points, values; mode=:smooth)The smoothed interpolant is defined on the data’s bounding box rather than on a grid you choose, so evaluate inside that box, or pass extrap. For noisy data that is already on a grid, convolution_smooth remains the tool.
v0.26.0 — 2026-09-27
ConvolutionInterpolations v0.26.0
ConvolutionInterpolations.jl v0.26.0
Highlights
- Lazy mode gives exactly eager’s boundary values, in every dimension. Lazy boundary cells now compute the same ghost points as eager construction, including the
:detectboundary check. Lazy mode supports real boundary ghosts and derivatives in any dimension, and is thread-safe. - Lazy mode is chosen automatically for large grids. With the new default
lazy=nothing, a uniform grid is built lazily when eager expansion would be costly in memory (more than 2²⁷ coefficients) or construction time (more than 2²⁴ ghost points), and eagerly otherwise. - More accurate boundaries. Ghost points are computed with compensated summation directly from the data, removing a rounding error that grew on N-D edges and corners.
- Faster, simpler antiderivatives. One evaluator handles every integral order, with exactly derived weights; first-order integrals in 2D and 3D evaluate up to 2× faster, and integral construction is much faster.
Bug fixes
convolution_interpolation(knots, values)without keywords on nonuniform knots built a uniform interpolant; it now builds the nonuniform one, as the keyword call does.- Grids with different knot types per axis (e.g. a range and a vector) failed on the nonuniform
b-kernel path. - Lazy interpolants mixing
:a0/:a1axes with higher-order kernels failed at boundary cells.
Breaking changes
boundary_fallbackis deprecated and has no effect (a warning is shown; it will be removed in v1.0). Lazy interpolants that usedboundary_fallback=truenow return exact values near the boundaries instead of linear ones.- The default for
lazyis nownothing(chosen by grid size, see above) instead offalse. Large grids are built lazily unlesslazy=falseis given; keyword-free calls in 4D are no longer lazy for small grids. - Lazy interpolants with
bc=:detectnow follow eager’s boundary decisions, so their values near the boundaries can differ from v0.25. - Values near the boundaries and antiderivative values change at rounding level.
- First-order antiderivative interpolants display as
derivative=-1instead ofintegral.
Migration guide
- Remove
boundary_fallbackfrom your calls. - To keep eager construction regardless of grid size, pass
lazy=false; to force lazy mode, passlazy=true. - Code that accessed internal fields of the interpolant types (such as
left_values, the integral tail arrays,lazy_workspaceorboundary_fallback) must be updated; these fields were removed.
v0.25.0 — 2026-09-24
ConvolutionInterpolations v0.25.0
ConvolutionInterpolations.jl v0.25.0
Highlights
- Integrals of any order.
derivative = -mgives the m-fold antiderivative of the interpolant, anchored at zero at the leftmost interior knot, up to order 2 (:a0,:a1), 4 (other:akernels), 6 (:b5) or 8 (:b7–:b13). All orders are computed from exactly derived kernel antiderivatives and converge at the kernel’s full rate. - In any dimension, mixed freely. Orders can be chosen per dimension and combined with interpolation or derivatives, e.g.
derivative = (-2, 0)or(-3, 1, -1). - Faster integrals. Integral construction and evaluation do substantially less work, and mixed integral/derivative evaluation is 20–30% faster.
Fixes
- Lazy mode combined with an antiderivative now raises a clear error at construction, instead of failing at the first evaluation.
Breaking changes
- The internal fields and type parameters of
FastConvolutionInterpolationfor integrals have changed. This only affects code that accesses these internals directly. - Mixed integral interpolants now display as
derivative=(…)instead ofmixed=(…).
Migration guide
- No changes are needed for code using the public API.
Disclosure of AI assistance
This release was developed with the assistance of Claude (Anthropic).
v0.24.0 — 2026-09-24
ConvolutionInterpolations v0.24.0
ConvolutionInterpolations.jl v0.24.0
Highlights
- Exact kernel evaluation. The fast path no longer uses precomputed kernel tables. Kernel weights are computed exactly from each kernel’s polynomial pieces, so values, derivatives and integrals are exact to rounding in any precision (
Float32,Float64,BigFloat). - Faster evaluation, especially in higher dimensions, e.g. 3D
:b13from ~60 μs to under 1 μs per evaluation. - Nothing is written to disk. The kernel table artifact and the on-disk cache are gone, and so are the Artifacts, Scratch and Serialization dependencies.
Fixes
- Pure N-D integrals (
derivative=-1in all of 4 or more dimensions) skipped the trailing coefficients in every dimension and returned wrong values. - On densely sampled grids, rounding in the grid spacing raised the error floor in proportion to the number of points. Values, derivatives, integrals and resampling now reach machine precision.
Deprecations
- The
precomputeandsubgridkeywords no longer have any effect. They are still accepted, with a warning, and will be removed in a future release.
Breaking changes
- The type parameters of
FastConvolutionInterpolationhave changed. This only affects code that dispatches on or constructs that type with explicit parameters. - Invalid
subgrid/kernel combinations inconvolution_resampleno longer raise an error; the keyword is ignored with a deprecation warning. - Results differ slightly from earlier versions, now being exact rather than table-interpolated.
Migration guide
- Remove
precomputeandsubgridfrom your calls. - Optionally, delete the kernel cache written by earlier versions:
~/.julia/scratchspaces/75b5ba44-29d7-490a-960d-52c835d47405.
v0.23.2 — 2026-09-22
ConvolutionInterpolations v0.23.2
ConvolutionInterpolations.jl v0.23.2
Fixes
- Fixed an error when the same depot is used by several Julia versions (e.g. via juliaup): cached kernel tables written by a newer Julia could not be read by an older one. Each Julia minor version now keeps its own cache, and unreadable cache files are regenerated automatically.
- The zero-allocation tests now pass on Julia 1.10 (LTS). The functors were already allocation-free; the tests were measuring call-site overhead on 1.10.
Other changes
- CI now tests the minimum supported Julia version, the LTS, the latest stable release and the upcoming prerelease on Linux, plus the latest stable release on Windows.
Breaking changes
None.
Migration guide
No action needed. Kernel tables are cached once more on first use after upgrading.
v0.23.1 — 2026-09-22
ConvolutionInterpolations v0.23.1
ConvolutionInterpolations.jl v0.23.1
Changes
- The precomputed kernel tables used by the fast interpolation path are now shipped as a Julia artifact instead of as ~3 MB of source constants. This reduces precompilation time and source size.
- The artifact is downloaded automatically on install and verified to be bit-identical to the previous tables.
- Adds
Artifacts(standard library) as a dependency.
Breaking changes
None. Results are numerically identical to v0.23.0.
Migration guide
No action needed.
v0.23.0 — 2026-09-15
ConvolutionInterpolations v0.23.0
ConvolutionInterpolations.jl v0.23.0
A release focused on error handling and kernel routing. No API changes; correct existing code behaves identically.
Breaking changes
FastConvolutionInterpolationnow throws on nonuniform grids. Previously a direct call with nonuniform knots was accepted and silently interpolated on the wrong spacing (the fast kernels are precomputed on a uniform subgrid and cannot adapt their weights). Code that relied on this path was producing incorrect results; see the migration guide below.
Nothing else changes behaviour for code that previously worked correctly. Two call forms that used to error now work (:n3 on a uniform grid, and a direct ConvolutionInterpolation call with a Symbol kernel on a uniform grid).
Migration guide
If you construct FastConvolutionInterpolation directly and your knots are nonuniform in any dimension, switch to one of:
convolution_interpolation(knots, values; ...)— the default entry point. It detects the grid and selects the fast path for uniform grids and the weight-adjusting slow path for nonuniform grids automatically. This is the recommended replacement.ConvolutionInterpolation(knots, values; ...)— the slow constructor, whose kernels adjust their weights to nonuniform spacing.
Calls through convolution_interpolation need no changes; it already routed nonuniform grids away from the fast path.
If you pass :a4, :a5 or :a7 on a nonuniform grid, nothing changes in behaviour — those calls errored before and still do — but the docstrings no longer claim a fallback to :n3. On nonuniform grids the supported kernels are :a0, :a1, :a3/:n3 and the full b-series (:b5, :b7, :b9, :b11, :b13); all of these work on both uniform and nonuniform grids, with the b-series giving higher order at the cost of a per-interval weight projection on nonuniform data.
:n3 and :a3 are now interchangeable
The nonuniform cubic kernel :n3 previously raised a MethodError on a fully uniform grid, because it has no uniform evaluator. It is now routed to :a3, which is mathematically identical on uniform grids and faster. Since :a3 already fell back to :n3 on nonuniform grids, the two names now describe one cubic kernel: whichever you pass, a uniform grid evaluates with the fast :a3 kernel and a nonuniform grid with the :n3 weights.
This makes :n3 (or :a3) a good default when you don’t know in advance whether the data will be uniform and want construction to stay fast either way — for example, user-supplied spectral or measurement grids. The b-series kernels also work on both grid types and give higher order, but on nonuniform data they pay a per-interval weight projection at construction; :a3/:n3 is the choice when that cost matters more than the extra order.
Bug fixes
ConvolutionInterpolation(knots, values; kernel = :b7)— a direct call with aSymbolkernel on a uniform grid — failed withgetindex(::Symbol, ::Int). The uniform builder received the raw keyword instead of the normalised kernel tuple. This was masked byconvolution_interpolation, which always passes a tuple, and so never appeared in normal use.- Docstrings corrected: only
:a3falls back to:n3on nonuniform grids;:a4,:a5and:a7are uniform-only.
Tests
New tests cover :n3/:a3 equality on uniform grids, the direct-constructor Symbol path, nonuniform rejection in FastConvolutionInterpolation, and :a4/:a5/:a7 rejection on nonuniform grids. Gaussian kernel test tolerance adjusted. 1913 tests pass.
Housekeeping
Manifest.tomlremoved from the repository.SparseArrayscompat updated.
kernel-tables-v1 — 2026-09-22
v0.22.0 — 2026-08-18
ConvolutionInterpolations v0.22.0
ConvolutionInterpolations.jl v0.22.0
Exact interpolation of fully scattered data — the answer to “does it handle fully scattered points?” is now yes, directly:
itp = convolution_interpolation(points, values) # points: D x N matrixScattered values enter as exact linear constraints on an internal uniform kernel grid; the result is an ordinary completed grid, so the entire existing machinery, evaluation, derivatives, antiderivatives, applies unchanged.
Highlights
- Exact interpolation: passes through every data point (violations ~1e-12), ~7th-order convergence for quasi-uniform point sets, 1D-3D recommended
- Derivatives: ~6-7th-order accurate first derivatives via the standard
derivativekeyword - Box integrals of scattered data: mixed antiderivatives accurate to ~1e-13 via inclusion-exclusion, to my knowledge unique among scattered-data methods
- Noisy data:
mode=:lsqwith a coarsergridsizegives a least-squares regression (noise suppression ~ sqrt(DOF/N)); the gridding+smoothing pipeline remains the robust choice for wild data - Solvers:
:cholesky(fast, default),:qr(lowest error floor),:nullspace(direct, small systems, also the validation oracle in the test suite) - Per-dimension kernels, Float32/BigFloat support, selectable extrapolation, graceful least-squares fallback (with warning) for strongly clustered data
Breaking changes
None. The scattered-data entrypoint is a new method (convolution_interpolation(::AbstractMatrix, ::AbstractVector)) on a first-argument type no existing method accepts, and the new fit_scattered export introduces no conflicts. All existing calls, defaults, and results are unchanged.
Migration guide
No migration required. Existing code runs unmodified. To adopt the new feature, pass scattered points as a D x N matrix (each column one point) instead of knot vectors, see the README’s “Scattered Data Interpolation (trusted data)” section.
Other
- New dependency: SparseArrays (stdlib)
- Test suite grown to 1885 passing tests
This feature was prompted by an audience question at JuliaCon Mainz 2026 (the moment on tape) — asked on August 12, shipped in this release.
v0.21.0 — 2026-08-08
ConvolutionInterpolations v0.21.0
Non-uniform path: much faster construction, rigorous convergence rates
The per-interval weight construction for non-uniform grids has been substantially optimized:
- Extended-precision arithmetic replaces exact rationals in the internal projection: BigFloat throughout, truncated to
Float64only at the final storage step. Accuracy is unchanged; construction is dramatically faster. - Normalized local coordinates: knot spacings are scaled per interval before the projection, making the system’s conditioning independent of mesh size. Fine grids (tested to N = 10⁴ and beyond) now construct without numerical degradation.
- QR factorization for the projection solve, and parallelized per-interval computation (intervals are independent).
Together these make the non-uniform path viable at grid sizes where it was previously impractical.
The README now also states the rigorous convergence rates for non-uniform grids, verified in extended-precision arithmetic: the construction enforces polynomial reproduction up to each kernel’s full degree, so convergence order is degree + 1: 6th order for :b5, 8th for :b7, 10th for :b9, 12th for :b11, and 14th order for :b13. For :b7 and above this exceeds the uniform design order of 7. On the classical Runge benchmark, :b13 reaches Float64 machine precision at roughly 500 non-uniform samples.
Extended derivative support
Exact-arithmetic verification of the kernel piece polynomials showed the b-series kernels are smoother than previously documented: the family is C^(degree - 2), i.e. :b7 is C⁵ (previously stated C⁴), :b9 is C⁷, :b11 is C⁹, and :b13 is C¹¹. The :b5 documentation was already exact (C³).
Following this, the fast-path derivative range has been extended by one order for :b7 through :b13:
| Kernel | Continuity | Derivative range (was) |
|---|---|---|
:b7 |
C⁵ | 0..5 (0..4) |
:b9 |
C⁷ | 0..6 (0..5) |
:b11 |
C⁹ | 0..7 (0..6) |
:b13 |
C¹¹ | 0..7 (0..6) |
The line is drawn at the 7th derivative: although the kernels support deeper differentiation in exact arithmetic, derivative orders beyond 7 are dominated by roundoff noise amplification in Float64 (the chain-rule factor (1/h)ⁿ) and are deliberately not exposed on the fast path. The full ladder up to each kernel’s continuity class remains reachable by evaluating the exact rational kernel coefficients directly in extended precision: undocumented and unsupported, but there for those who need it.
New derivative orders are convergent on non-uniform grids (order degree + 1 − n) and were validated against analytical derivatives on randomly perturbed grids.
Breaking changes
None. The API is fully backwards compatible. One internal change may be observable: the non-uniform weight construction now uses extended-precision floating point instead of exact rational arithmetic, so stored Float64 weights may differ from v0.20.x at the last-bit level. Convergence behavior is unchanged or improved.
Migration guide
No action required. Existing code runs unchanged; the new derivative orders and faster non-uniform construction are available immediately.
Other
- Kernel-table regeneration fixes, including a corrected
:b5second-derivative table - Subgrid selection now falls back to
:linearat each kernel’s maximum derivative order (Hermite subgrids require derivative tables above the requested order) - README: rigorous continuity classes and non-uniform convergence rates throughout; refreshed resampling benchmarks; documented the dimension-dependent default kernel (
:b7for 1–2D,:b5for 3D,:a4for 4–5D,:a3for ≥6D)
Full test suite passing (1858+ tests).
v0.20.1 — 2026-08-05
ConvolutionInterpolations v0.20.1
ConvolutionInterpolations.jl v0.20.1
Pure optimization patch — no API or behavior changes.
Faster wrapped evaluation
The extrapolation wrapper now caches domain bounds (lo/hi/tolerance per dimension) as plain floats at construction instead of reconstructing them from the expanded knots on every call. Wrapped evaluation is now within ~1 ns of the bare interpolant (~23 ns for 1D :b7, previously ~26 ns).
Evaluation improved modestly across all kernels and dimensions; construction of b-series kernels in 1D/2D is now indistinguishable from the low-order kernels (~2 μs).
Breaking changes
None. Domain-check tolerance semantics are identical to v0.20.0 — the tolerance is now precomputed at wrap time rather than per evaluation, with the same values.
One internals note: ConvolutionExtrapolation gained three fields (lo, hi, tol). Code constructing this struct positionally or accessing its fields directly (not part of the public API) must account for them.
Migration guide
Nothing to do — update and benefit.
Full test suite passes.
v0.20.0 — 2026-08-05
ConvolutionInterpolations v0.20.0
ConvolutionInterpolations.jl v0.20.0
This release changes the default kernel selection to a dimension-dependent policy and fixes a long-standing boundary issue where evaluating at exact domain endpoints could throw under the default extrap=:throw.
Dimension-dependent default kernel
The default kernel keyword is now :auto, which selects the kernel based on dimension:
| Dimension | Default kernel |
|---|---|
| N ≤ 2 | :b7 |
| N = 3 | :b5 |
| N = 4–5 | :a4 |
| N ≥ 6 | :a3 |
The policy trades kernel width against tensor-product evaluation cost: in 1D and 2D the wider :b7 kernel improves accuracy at near-negligible evaluation cost, while in higher dimensions the (2·eqs)^N support growth favors narrower kernels. The keyword constructors and the positional fast constructors now share a single policy and cannot drift apart.
Passing an explicit kernel symbol or tuple behaves exactly as before; :auto may also be passed explicitly to opt into the policy.
Boundary tolerance fix
Evaluating at exact domain endpoints — for example itp(2π) on knots range(0, 2π, length=n) — could previously throw a domain error under the default extrap=:throw, because the domain bounds are reconstructed from the expanded knot arithmetic and could differ from the original endpoints by one ulp.
Domain checks now allow ulp-scale slack (relative to the bound magnitudes, with an absolute floor near zero) and clamp near-boundary queries before evaluation. Exact endpoints and ulp-adjacent points evaluate correctly; genuinely out-of-domain points still throw. The tolerance forgives floating-point rounding, not user error.
Breaking changes
- Default results change for constructions that did not specify a kernel. A 1D or 2D construction with default arguments now uses
:b7instead of:b5, producing slightly different (generally more accurate) values. Constructions with an explicitkernel=argument are unaffected. - Near-boundary behavior under
extrap=:throwchanges. Queries within one ulp of the domain boundary previously threw and now evaluate. Code relying on the throw as a strict boundary detector at ulp precision will observe different behavior.
No exported function signatures were removed or renamed; the kernel keyword’s default value changed from :b5 to :auto.
Migration guide
- To reproduce v0.19.x default results exactly: pass
kernel=:b5explicitly. - If you stored reference outputs generated with default kernels in 1D or 2D, regenerate them or pin
kernel=:b5. - If you relied on
extrap=:throwfor strict domain enforcement: the enforcement still holds beyond ulp-scale tolerance; no action needed unless you specifically depended on exact-endpoint queries throwing. - Everything else — explicit kernels, extrapolation modes, boundary conditions, derivatives, lazy mode — is unchanged.
Full test suite passes.
v0.19.1 — 2026-08-04
ConvolutionInterpolations v0.19.1
ConvolutionInterpolations.jl v0.19.1
Performance-focused patch release: constructors are ~2× faster with ~20× less memory churn in low dimensions, evaluation is slightly faster across the board, and one latent 2D bug is fixed. No API changes: existing code benefits automatically.
Faster construction
Precomputed kernel tables are now cached and shared between interpolants of the same kernel, derivative order, and element type, instead of being copied on every construction. Together with allocation reductions in the knots and boundary-setup paths, a 1D :b5 construction at 25 points goes from 3.1 μs / 33.8 KiB to 0.88 μs / 1.7 KiB. Construction is now faster than a comparable cubic spline setup in our benchmarks while retaining higher convergence order.
Range-typed knots (e.g. range(0, 1, 100)) are now preserved end-to-end rather than collected into vectors, eliminating copies during setup.
Faster evaluation
Evaluation functors now cache the grid origin and compute cell offsets arithmetically instead of indexing the knot vector, removing an index and a division per dimension per call. 1D :b5 evaluation drops from ~23 ns to ~21 ns, with similar small gains in higher dimensions. This also removes an evaluation penalty that range-typed knots would otherwise incur.
Bugfix
- Fixed the 2D quintic subgrid mode using the x-direction table spacing in the final y-direction Hermite step. Results were unaffected unless
precomputediffered between the two dimensions. - Fixed a missing element-type conversion for
:a1antiderivative kernel tables at non-Float64precision.
Internal
- Removed a legacy unused workspace array from the boundary-condition machinery.
Breaking changes
None in the API sense — no exported function signatures, keyword arguments, or defaults changed, and no user code needs modification.
Two of the bugfixes are strictly output-changing, which automated tooling may flag as breaking:
- Numerical results for 2D quintic subgrid with per-dimension
precomputevalues change (they were previously wrong due to the Hermite spacing bug). Results with the default or uniformprecomputeare unaffected. - Kernel table element types for
:a1antiderivative at non-Float64precision are now correct (previously leakedFloat64), which can shift results at the precision of the requested type.
Both are corrections toward the documented behavior, released as a patch per common Julia ecosystem practice for bugfixes.
Two internal representation changes are visible only to code reaching into non-API internals:
FastConvolutionInterpolationgained an internalx0field; code constructing this struct positionally must add it. The public constructors are unaffected.- Knots passed as ranges are now stored as ranges rather than collected
Vectors, so theknotsfield type parameter may differ from previous versions.
Migration guide
For users of the public API (convolution_interpolation, FastConvolutionInterpolation(knots, vs; kwargs...), ConvolutionInterpolation(knots, vs; kwargs...)): nothing to do — update and enjoy the speedup.
If you compared stored numerical outputs across versions and used 2D quintic mode with asymmetric precompute, regenerate your reference values; the new results are the correct ones.
If you dispatched on or accessed the internal struct fields directly (not part of the public API), account for the new x0::NTuple{N,T} field and the possibility of range-typed knots.
Full test suite (1858 tests) passes.
AI Assistance
I was assisted by Anthropic’s Claude in debugging the code and improving performance.
v0.19.0 — 2026-08-02
ConvolutionInterpolations v0.19.0
ConvolutionInterpolations v0.19.0
Breaking changes
bc=:detect(the default) now selects the polynomial ghost extension by a different criterion. Interpolated values near domain boundaries will differ from v0.18. Interior values are unchanged. The new guard tests thens-th finite-difference residual of the extension against the local data range, replacing a first/second/third-difference smoothness heuristic that rejected the polynomial extension on ~43% of smooth inputs, including data the extension reproduces exactly. On 20,000 held-out random signals the rate of selecting the worse branch drops from ~8% to ~3%.convolution_gaussiannow throws forB > 0.34. Above that value the kernel’s between-node relative error exceeds1e-12.convolution_smoothevaluates on grid nodes only and is unaffected at anyB.convolution_smoothandconvolution_gaussianreturn different values near boundaries, because two bugs there were fixed (see below). Constant data now reproduces to machine precision at the boundary; previously there was a 16% deficit atB=0.1.bc=:polyandbc=:linearchange only at roundoff level, from local rather than global mean-centring in the boundary setup.
Migration guide
- No source changes are required. The API is unchanged; only numerical results near boundaries differ.
- If you use
convolution_gaussianwithB > 0.34, either lowerB(0.1 is the usual choice) or switch toconvolution_smooth, which is exact at anyBwhen you only need values on the grid nodes. - If you have tests pinned to exact boundary values from v0.18, regenerate the expected values, or relax them to a tolerance.
- To keep the old behaviour, pin the previous minor version in
Project.toml:
[compat]
ConvolutionInterpolations = "0.18"Fixed
convolution_smoothandconvolution_gaussianallocatedeqs_dghost points but passedeqs = eqs_dto the boundary fill, which uses the conventionghosts = eqs - 1. The ghost slot adjacent to the data was never written and stayed zero, silently dropping one kernel tap at each boundary.- The Gaussian ghost fallback subtracted the mean twice, offsetting every ghost point by the mean of the input.
Performance
- Boundary setup now reads only the
ns+3samples it needs rather than the whole line. Construction is ~1.4× faster in 2D and ~1.7× in 3D, for every kernel including thea-family. Evaluation cost is unchanged.
Tests
- 1858 passing (up from 1826). New: constant preservation, linear reproduction, offset invariance and boundary symmetry for the Gaussian path; guard exactness and allocation-free checks for
:detect.
Parts of this release were developed with assistance from Claude (Anthropic); all code and results were verified by the author.
v0.18.0 — 2026-07-13
ConvolutionInterpolations v0.18.0
ConvolutionInterpolations.jl v0.18.0
Breaking changes
None. No exported API was changed or removed: v0.18.0 is additive. All v0.17.x code runs unchanged.
Migration guide
No action required. Existing code is unaffected. Optionally:
- Scattered data pipelines using an external gridding package can switch to the new built-in
scattered_to_grid. - Smoothing calls tuned to small
Bvalues may benefit from the updated guidance (B ≈ 0.1); see the README.
New features
scattered_to_grid – scattered data gridding
Grid unstructured data onto a regular grid by nearest-neighbor assignment, in any number of dimensions:
grid = scattered_to_grid(points, values, (xs, ys); k=1) # nearest point (k=1, default)
smooth = convolution_smooth((xs, ys), grid, 0.1) # B=0.1 is recommended
itp = convolution_interpolation((xs, ys), smooth)This completes the scattered-data-to-interpolant pipeline within the package: gridding, smoothing, and high-order interpolation/differentiation/integration of noisy scattered data, end to end. Nearest-neighbor gridding is deliberately simple: its error is spatially uncorrelated, which is exactly what the Gaussian smoother removes best.
Full Float32 support
Float32 in, Float32 out:
- Kernel tables are converted to the working precision at construction
- Scalar parameters (e.g. the smoothing parameter
B) accept anyReal - Query points promote to the interpolant’s type at evaluation
Together with the existing BigFloat support, the package now preserves types and computes at native precision from Float32 through BigFloat.
Changed
- Smoothing guidance:
B ≈ 0.1is recommended as a default (previouslyB ≤ 0.1). Testing across 1D–4D shows the optimum is shallow and two-sided: smallerBover-smooths and attenuates extrema, largerBleaves noise behind. - README: rewritten scattered data pipeline example (no external packages required), updated figures.
Dependencies
- New: NearestNeighbors.jl (KD-tree queries for
scattered_to_grid)
Tests
- 1699 → 1826 passing tests, including new suites for
scattered_to_grid(brute-force equivalence in 2D/3D, grid orientation, duplicate points, type handling) and Float32 (eltype preservation across all major paths, accuracy verification, mixed-type regression guards)
v0.17.1 — 2026-06-01
ConvolutionInterpolations v0.17.1
v0.17.1
Bugfixes, expanded testsuite, and :natural boundary condition reintroduced
List of improvements - Fixing a few stale things in the readme (tuple vs vector in boundary conditions) - Fix several printing bugs which had passed the full test suite - These bugs only surfaced when tested directly in the REPL - Another small bugfix relating to the nonuniform :n3 kernel (it was missing in a dictionary) - Reintroduced the :natural extrapolation option, usefull in lower dimensions (also in the readme) - Include testing in the testsuite of the :natural boundary condition - 1699 tests now passing
v0.17.0 — 2026-04-13
ConvolutionInterpolations v0.17.0
v0.17.0
New features
boundary_fallbacksupport for nonuniform lazy:n3(1D/2D/3D/ND). Near-boundary evaluation falls back to linear interpolation, consistent with uniform lazy behavior. Avoids expensive ghost point resolution in high-dimensional corners (e.g. 3⁸ = 6,561 weighted lookups reduced to 2⁸ = 256 linear interpolation).
Bug fixes
- Error guard for slow lazy functors. Attempting
lazy=true, fast=falseon uniform grids now raises a clear error at construction time, instead of silently compiling and failing withMethodErrorat evaluation.
Tests
- Split lazy tests into separate uniform and nonuniform files.
- Added
boundary_fallbacktests for nonuniform lazy:n3in 1D/2D/3D. - 1241 tests passing.
Breaking changes
This is a non-breaking release. All existing APIs, defaults, and behaviors are unchanged.
Migration guide
No migration steps required. Existing code will work without modification. New features (boundary_fallback for nonuniform lazy :n3) are opt-in and default to previous behavior.
v0.16.0 — 2026-04-12
ConvolutionInterpolations v0.16.0
v0.16.0
Breaking changes
- Removed
:periodicboundary condition. This was legacy code from early development that predated the polynomial boundary condition system. It did not converge and was not suitable for production use. - Removed dual extrapolation option. The double-pass boundary extrapolation mode has been removed.
- Removed slow lazy functors and
lazy_ghost_value. These untested legacy code paths have been removed. Fast lazy evaluation (lazy=true) is unaffected. convolution_resampledefault kernel changed from:b9to:b13. The cost difference is negligible while accuracy improves.convolution_resampledefault subgrid changed to:cubic. This eliminates precomputation for resampling and improves accuracy.
Migration guide
- Replace
bc=:periodicwithbc=:poly(recommended) orbc=:detect(default). Both offer superior accuracy with proven convergence. - If you relied on the dual extrapolation option, use standard single-pass extrapolation instead (
extrap=Line(),extrap=Flat(), orextrap=Throw()). - If you need the previous resampling defaults, pass
kernel=:b9, subgrid=:linearexplicitly toconvolution_resample. - If 3D evaluation speed is critical, pass
subgrid=:linearto recover the previous linear subgrid performance. The default:cubicsubgrid is slower per evaluation but significantly more accurate.
New features
- 3D cubic Hermite subgrid. 3D evaluation now uses cubic Hermite corner accumulation (64 accumulators, 21 Hermite calls) for both eager and lazy functors. This provides substantially better accuracy and convergence compared to linear subgrid.
- Zero-caching default path. With
:cubicsubgrid as default, precalculation is only triggered for BigFloat or top-derivative evaluation. New users will not encounter precomputation on first use in typical workflows. - Subgrid hierarchy. Cubic Hermite is now the default for 1D–3D. 4D and above default to linear subgrid where the cost of higher-order evaluation grows too quickly.
- Shared subgrid helpers. New
eval_sg_kandeval_sg_ktfunctions centralize subgrid kernel evaluation, reducing code duplication across functor paths. - 5D mixed integral tests. New test coverage for the N-integral-dims functor, bringing the total to 1184 tests.
Parts of this release were developed with assistance from Claude (Anthropic). All code, methods, and scientific content have been verified and validated by the author.
v0.15.0 — 2026-04-06
ConvolutionInterpolations v0.15.0
v0.15.0 Release Notes
⚠️ Breaking Changes
1. Boundary condition :auto renamed to :detect
The default boundary condition symbol has been renamed from :auto to :detect to better reflect its behavior, it detects the best boundary condition automatically based on the data.
Migration: Replace any explicit bc=:auto with bc=:detect. Users relying on the default behavior (never setting bc explicitly) are unaffected.
# Before
itp = convolution_interpolation(x, y; bc=:auto)
# After
itp = convolution_interpolation(x, y; bc=:detect)2. Gaussian smoothing split into dedicated functions
The B parameter previously accepted by convolution_interpolation has been removed. Gaussian smoothing is now available through two dedicated functions that provide a cleaner interface and significantly better performance.
Migration:
# Before
itp = convolution_interpolation(x, y; B=0.05)
# After, for an interpolant at arbitrary locations:
itp = convolution_gaussian(x, y, 0.05)
# After, for a smoothed array on the same grid:
y_smooth = convolution_smooth(x, y, 0.05)🆕 New Features
convolution_resample, High-order grid-to-grid resampling
Resample data from one uniform grid to another using high-order separable convolution interpolation. Significantly faster than constructing a full interpolant and evaluating at each output point, with the advantage growing with dimension:
| Grid | convolution_resample |
construct + eval | Speedup |
|---|---|---|---|
| 1D 500→1000 | 22 μs | 29 μs | 1.3× |
| 2D 100²→200² | 1.9 ms | 7.0 ms | 3.6× |
| 3D 30³→60³ | 8.9 ms | 127 ms | 14× |
- Default kernel
:b9, higher accuracy thanconvolution_interpolation’s default:b5 - O(N) allocations independent of grid size
- 1D to 10D type-stable default wrappers
- Supports per-dimension kernels and derivative resampling
- Antiderivatives not supported, use
convolution_interpolationwithderivative=-1
See Grid Resampling in the README.
convolution_smooth, Separable Gaussian smoothing
Apply Gaussian smoothing to recover clean signals from noisy data. Returns an array of the same size as the input.
| Grid | B=0.02 | B=0.05 | B=0.1 |
|---|---|---|---|
| 1D n=1000 | 22 μs | 13 μs | 9.6 μs |
| 2D 200² | 1.7 ms | 0.99 ms | 0.72 ms |
| 3D 50³ | 9.3 ms | 5.7 ms | 4.4 ms |
| 4D 40⁴ | 273 ms | 206 ms | 169 ms |
- 1000× speedup over previous Gaussian path via
expweight hoisting, weights precomputed once per call - Adaptive
eqs÷3boundary fit, wider kernels use more boundary points automatically - O(N) allocations independent of grid size
- B ≤ 0.1 recommended for effective noise reduction
See Smoothing Noisy Data in the README.
convolution_gaussian — Standalone Gaussian interpolant
Construct a Gaussian smoothing interpolant that supports evaluation at arbitrary locations, just like convolution_interpolation.
itp = convolution_gaussian(x, y_noisy, 0.05)
itp(1.5) # evaluate anywhere in the domain- Allocation-free evaluation via
Val{B}andVal{NT}type encoding of kernel parameters - Dedicated 1D, 2D, 3D and ND evaluation functors
Scattered Data Pipeline
The two new functions combine naturally with RBF packages for a complete scattered data workflow — see Scattered Data Pipeline in the README.
🚀 Performance Improvements
Full type stability throughout
Ref{Array{T,N}}ping-pong buffers eliminateCore.Boxboxing of loop variablesVal{UG}branch elimination removes allocating empty matrices in boundary condition pathsBoundaryWorkspace{T,1}viaVal(1)ensures fully concrete workspace types- Type-stable 1D to 10D default convenience wrappers, verified with
@code_warntype
Lazy evaluation functors reworked
The 1D, 2D, and 3D lazy evaluation functors have been fully reworked for type stability and performance. Furthermore: lazy construction speedup for 4D: 1320ms → 79ms.
ND pure antiderivative allocation-free
The N-dimensional pure antiderivative path is now allocation-free and supports linear/cubic/quintic subgrid selection, achieved via nexprs/nloops macros.
🧪 Tests
- 1178 tests passing (was 1086, +92 new tests)
- Comprehensive test suite for
convolution_resampleincluding derivative scaling - Comprehensive test suite for
convolution_smoothincluding boundary behavior - Lazy mode test suite substantially expanded, 1D/2D/3D with
boundary_fallback=false, 4D+ withboundary_fallback=true, nonuniform:n3, construction speed
📖 Documentation
README updated with: - New sections: Smoothing Noisy Data, Grid Resampling, Scattered Data Pipeline - All performance tables based on updated benchmarks - B-value comparison figure showing effect of smoothing parameter - Updated lazy construction timings reflecting actual improvements - Updated Performance Guidelines with recommendations for new functions
Parts of this release were developed with assistance from Claude (Anthropic). All code, methods, and scientific content have been verified and validated by the author.
v0.14.0 — 2026-03-28
ConvolutionInterpolations v0.14.0
v0.14.0: Fast Mixed Integral/Derivative, Zero-Allocation Evaluation
Highlights
The headline feature of v0.14.0 is a new class of fast mixed integral/derivative operators that combine antiderivatives, derivatives, and interpolation across different dimensions in a single allocation-free evaluation call. This extends the fast antiderivative path introduced in v0.13.0, which accelerated pure antiderivatives using precomputed tail sums, to the fully general mixed case.
New Features
Fast mixed integral/derivative operators
In v0.13.0, derivative=(-1,-1) in 2D used tail sums to achieve O(stencil^2) evaluation independent of grid size for pure antiderivatives. v0.14.0 extends this to arbitrary mixtures of integrals, derivatives, and interpolation across dimensions. For example:
# integrate in x, differentiate in y, interpolate in z, single O(stencil^3) call
itp = convolution_interpolation((xs,ys,zs), vs; derivative=(-1, 1, 0))
# definite integral via differences cancels the unknown constant:
itp(x2, y, z) - itp(x1, y, z) # ≈ ∫ₓ₁ˣ² f dx · ∂f/∂y · f(z)The fast path uses precomputed tail sums to evaluate contributions from coefficients outside the local stencil in O(1) per integral dimension, reducing the total cost to O(stencil^N) where N is the number of dimensions, the same asymptotic cost as pure interpolation. This is achieved by decomposing the evaluation domain, each part handled by a dedicated lookup into precomputed tail sum arrays built at construction time.
Mixed operators are supported for any combination of integral (-1), interpolation (0), and derivative (≥1) orders per dimension, in any number of dimensions. Up to 3 integral dimensions use the specialized fast path; 4 or more integral dimensions fall back to a general accurate path.
The direct (non-fast) path for mixed operators is also available via fast=false, and agreement between fast and direct paths is verified in the test suite.
End-to-end zero-allocation evaluation
All non-lazy evaluation paths, including the extrapolation wrapper, are now verified allocation-free via @allocated tests. This covers all kernels (:a0 through :b13), all derivative/integral/mixed combinations, and 1D through 4D. The key enabler was replacing runtime fields and symbol-based dispatch with compile-time singleton structs and Val-based type parameters throughout.
BigFloat precision preservation
All kernels correctly propagate BigFloat arithmetic through construction and evaluation without silent downcast to Float64. B-series kernels on dense grids (n=2000) achieve errors below 1e-16, well below Float64 precision, verified by new precision tests. This means the package can be used directly for extended precision computation by simply passing BigFloat grids and values.
Expanded test suite
Test count grows from 696 to 941, adding: - Mixed integral/derivative convergence in 2D, 3D, 4D (fast and direct paths) - Fast vs direct agreement tests in 2D, 3D, 4D - Zero allocation tests across all functors and derivative combinations - BigFloat type preservation and machine precision tests
Breaking Changes
Low-level extrapolation API: symbols → structs
The low-level ConvolutionExtrapolation constructor now requires singleton structs instead of symbols:
# v0.13 (no longer supported at low level)
ConvolutionExtrapolation(itp, :throw)
# v0.14
ConvolutionExtrapolation(itp, Throw())
ConvolutionExtrapolation(itp, Line())
ConvolutionExtrapolation(itp, Flat())This enables compile-time dispatch and is required for zero-allocation evaluation.
Migration Guide
For the convenience constructor (convolution_interpolation): no changes needed. Both symbols and structs are accepted:
# both still work
itp = convolution_interpolation(x, y; extrap=:line)
itp = convolution_interpolation(x, y; extrap=Line())For the low-level ConvolutionExtrapolation: replace symbols with the corresponding struct:
| Symbol | Struct |
|---|---|
:throw |
Throw() |
:line |
Line() |
:flat |
Flat() |
Declaration of AI Assistance
The development of v0.14.0 was carried out with assistance from Claude (Anthropic), including design of the fast mixed integral/derivative functors, type system cleanup, dispatch debugging, test suite development, and documentation. All code, methods, and scientific content have been verified and validated by the author.
v0.13.0 — 2026-03-22
ConvolutionInterpolations v0.13.0
ConvolutionInterpolations.jl v0.13.0 - 3D Fast Antiderivative Support
New Features
3D fast antiderivative evaluation The 3D antiderivative functor now uses a 27-region decomposition (1 center + 6 faces + 12 edges + 8 corners) with precomputed prefix/suffix sum arrays, achieving ~1000× speedup over the slow path on a 100×100×100 grid. Accuracy reaches 1e-15 with cubic subgrid interpolation.
Cubic and quintic subgrid modes are now enabled for 1D, 2D, and 3D integral evaluation (previously forced to :linear), giving significantly better accuracy at minimal extra cost.
Breaking Changes
:nearest subgrid removed The :nearest subgrid option has been removed. It was undocumented, only partially implemented for 1D, and offered poor accuracy relative to :linear at negligible speed gain.
Migration Guide
:nearest subgrid If you were using subgrid=:nearest, switch to subgrid=:linear:
# Before
itp = convolution_interpolation(x, vs; subgrid=:nearest)
# After
itp = convolution_interpolation(x, vs; subgrid=:linear)No other API changes are required. All other keyword arguments, kernel names, derivative orders, and boundary condition symbols are unchanged.
Internal Changes
These changes do not affect the public API. - Constructors simplified and unified to normalize all inputs to per-dim tuples - Functors reorganized for clarity and dispatch correctness - New abstract types added to prevent wrong functor dispatch - Improved type stability of 1D and 2D integral tail sums - Test suite reorganized, trimming redundant tests and adding new coverage for nonuniform per-dim kernel selection, per-dim derivative combinations, boundary conditions, extrapolation, subgrid downgrade, and Gaussian smoothing - All 696 tests pass and run in 4 minutes
v0.12.0 — 2026-03-18
ConvolutionInterpolations v0.12.0
ConvolutionInterpolations.jl v0.12.0
New Features
:a0 antiderivative support
The nearest-neighbor kernel now supports derivative=-1. The antiderivative of a piecewise constant function is piecewise linear, and integrates exactly. A delta-like spike integrates to a perfect Heaviside step:
n = 1001
x = range(0.0, 2π, length=n)
h = x[2] - x[1]
y = [abs(xi - π) ≈ 0.0 ? 1.0/h : 0.0 for xi in x]
itp = convolution_interpolation(x, y; kernel=:a0, derivative=-1)
itp(π + 0.1) # = 1.0
itp(π - 0.1) # = 0.0This also works in multiple dimensions and with per-dimension kernel tuples, enabling for example a sharp delta selector in one dimension combined with smooth high-order differentiation in another:
itp = convolution_interpolation((x, y), vals; kernel=(:a0, :b7), derivative=(-1, 1))Breaking Changes
bc=:polynomialrenamed tobc=:poly. This is a one-word change that makes the API more concise and consistent with the symbol-based convention used throughout.
Migration Guide
# Before
itp = convolution_interpolation(x, y; bc=:polynomial)
# After
itp = convolution_interpolation(x, y; bc=:poly)Improvements
- Multidimensional integration convergence claims updated: order is empirically flat (~7th) for general smooth integrands, but increases dramatically for symmetric integrands (from ~7th in 1D to ~12th in 5D for
:b7). The mechanism is not yet fully understood. - Technical background section corrected:
left_valuesis precomputed for all dimensions; prefix sums for O(1) tail lookups are available in 1D and 2D only. - Delta spike → Heaviside example added to README.
- Kernel reference table updated:
:a0derivative range now-1..0.
Bug Fixes
- Fixed per-dimension constructor crash when
:a0or:a1appeared in a kernel tuple alongside higher-order kernels (e.g.kernel=(:a0, :b7)). Ghost point computation now correctly skips dimensions witheqs=1.
Tests
- New
:a0antiderivative testset: constant function, delta spike, anchor condition, fast vs direct agreement. - Full test suite passing.
Co-developed with assistance from Claude (Anthropic).
v0.11.0 — 2026-03-17
ConvolutionInterpolations v0.11.0
ConvolutionInterpolations.jl v0.11.0 — API Stabilization
This release completes the API cleanup in preparation for v1.0.0. All breaking changes are simple renames, motivated by consistency and a gradual departure from conventions inherited from Interpolations.jl that didn’t fit this package’s design.
Breaking changes
| Before | After |
|---|---|
degree=:b5 |
kernel=:b5 |
kernel_bc=:auto |
bc=:auto |
extrapolation_bc=Throw() |
extrap=:throw |
Throw(), Line(), Flat(), Natural() |
:throw, :line, :flat, :natural |
itp.deg |
itp.kernel_sym |
itp.kernel_bc |
itp.bc |
Periodic and Reflect extrapolation modes removed — use bc=:periodic at construction for smooth periodicity instead, ideally combined with extrap=:natural.
lazy and boundary_fallback are now fully independent — high-dimensional lazy mode no longer silently forces boundary_fallback=true.
Julia compat bumped to 1.8.
Migration guide
# Before
itp = convolution_interpolation(x, y;
degree=:b7,
kernel_bc=:polynomial,
extrapolation_bc=Line(),
derivative=1)
# After
itp = convolution_interpolation(x, y;
kernel=:b7,
bc=:polynomial,
extrap=:line,
derivative=1)Improvements
- Test suite tightened throughout — most tolerances now
1e-6, no boundary avoidance anywhere - README: scattered data workflow, 2D integration convergence plot,
:naturalextrapolation description, updated lazy/boundary_fallback documentation showimproved for nonuniform kernel display and multi-dimension BC condensation
Co-developed with assistance from Claude (Anthropic).
v0.10.1 — 2026-03-16
ConvolutionInterpolations v0.10.1
v0.10.1 — Major Bugfix: All 2D/3D Operations Now at Machine Precision
Bug Fixed
A fundamental orientation error in the boundary condition ghost point computation has been corrected. When extracting 1D slices for right-boundary polynomial extrapolation in N>1 dimensions, the slice was ordered boundary->interior rather than interior->boundary. This caused the polynomial coefficients to extrapolate in the wrong direction, corrupting all ghost points on right boundaries in 2D and higher dimensions.
The fix is a single-line correction in the slice extraction loop:
# Before
workspace.slice[j] = c[idx - workspace.slice_offset[j]]
# After
workspace.slice[j] = c[idx - workspace.slice_offset[n_dim - j + 1]]Impact
All multidimensional operations are affected: interpolation, differentiation up to 6th order, antiderivatives, and any mixed combination thereof, in 2D and higher. All now converge to machine precision as expected from the tensor product theory.
- 1D: Unaffected: already at machine precision
- 2D/3D interpolation, differentiation, antiderivatives, and mixed operations: Now all at machine precision
Convergence Results
The fix unlocks the full power of the tensor product approach. For integration specifically, an unexpected dimensional enhancement is observed:
| Dimensions | Kernel order (1D) | Observed convergence order |
|---|---|---|
| 1D | 7th | 7th |
| 2D | 7th | ~9th |
| 3D | 7th | ~11th |
| 4D | 7th | ~13th |
A 10×10×10 grid already achieves ~1e-4 accuracy for smooth 3D integrands, with machine precision around n=100 per dimension.
Note
Despite being a patch release, this fix unlocks the true strength of ConvolutionInterpolations.jl: high-order machine precision evaluation of any combination of interpolation, differentiation, and integration in any number of dimensions, from a single set of sample points.
v0.10.0 — 2026-03-15
ConvolutionInterpolations v0.10.0
ConvolutionInterpolations.jl v0.10.0: Fast 1D/2D Antiderivative, Boundary Condition Fix
Breaking Change: :detect Kernel Boundary Condition Removed
The :detect kernel boundary condition has been removed. This option attempted to automatically detect periodic signals and apply a mixed linear-prediction boundary condition per slice. In practice, this per-slice detection was unreliable, different slices of the same 2D or 3D array could receive different boundary conditions, creating asymmetric coefficient arrays and degrading interpolation accuracy, particularly for wide kernels (:b9, :b11, :b13) with few grid points.
Migration guide: Replace kernel_bc=:detect with one of: - kernel_bc=:polynomial, optimal for most cases, preserves polynomial reproduction (default when sufficient points) - kernel_bc=:auto, uses :polynomial when sufficient points, falls back to :linear automatically - kernel_bc=:linear, safe, stable fallback for any function type - kernel_bc=:quadratic, second-order extrapolation (use with caution on wide kernels) - kernel_bc=:periodic, for known periodic signals with insufficient points for :polynomial
The default fallback for insufficient grid points has also changed from :detect to :linear. The :linear kernel boundary condition is unconditionally stable, it never produces the unbounded ghost point growth that the :quadratic kernel boundary condition can exhibit on small grids with wide kernels.
Fast Antiderivative: O(eqs) in 1D, O(eqs^2) in 2D
The fast path for derivative=-1 has been fundamentally redesigned. Previously, despite being called “fast”, the antiderivative evaluation looped over the entire coefficient array, O(N) per eval in 1D, O(N^2) in 2D, giving no advantage over the direct path for large grids.
What changed: The kernel antiderivative K̃(s) saturates to exactly +/- ½ outside the stencil support. This means the contribution of all coefficients outside the local stencil can be precomputed at construction time as a cumulative sum, the “tail”. At evaluation, the total integral decomposes into:
- Local stencil (O(eqs) terms): per-j kernel lookup, unchanged from before
- Left/right tails (O(1)): precomputed prefix sums, single array lookup
In 2D, the domain decomposes into 9 quadrants around the stencil. The four corner quadrants (both dimensions in tail) are O(1) via 2D cross-sum arrays. The four edge quadrants (one dimension in stencil, one in tail) are O(eqs) via 1D prefix lookups. Only the center quadrant (both in stencil) requires the O(eqs^2) double loop.
Speedup at N=200:
| Kernel | Fast vs direct |
|---|---|
:a3 |
115x |
:b5 |
44x |
:b7 |
1,513x |
:b11 |
1,957x |
:b13 |
2,117x |
At N=1000, speedups reach 11,000x for :b13. Evaluation time is now flat (O(1) in N) for all grid sizes.
Boundary Condition Improvements
Polynomial path (right boundary sign fix): The polynomial kernel boundary condition (:polynomial) matrix is applied to the reversed signal at right boundaries. A curvature continuity check now correctly determines whether the ghost points need to be negated, handling even functions (cosine-like) and odd functions (sine-like) correctly in all dimensions. Previously, a hardcoded dimension check (dim == 1) was used, which failed for even functions in the second dimension.
Recursive path: (:linear,:quadratic,:periodic) The signal is now reversed before recursive extrapolation at right boundaries, consistent with the polynomial path. This fixes asymmetric coefficient arrays that were visible when plotting itp.coefs for 2D even functions.
Tests
All 1046 tests pass (up from 1038). Several tolerances tightened.
Co-developed with assistance from Claude (Anthropic).
v0.9.0 — 2026-03-12
ConvolutionInterpolations v0.9.0
ConvolutionInterpolations.jl v0.9.0
New Features
Per-dimension kernels and derivatives
Both the uniform fast path and nonuniform b-kernel path now support per-dimension kernel degrees and derivative orders via tuples:
# Different kernel per dimension
itp = convolution_interpolation((x, y), vals; degree=(:b5, :b9))
# Different derivative order per dimension
itp = convolution_interpolation((x, y), vals; degree=(:b7, :b7), derivative=(1, 0))
# Works on nonuniform grids too
itp = convolution_interpolation((x_nu, y_nu), vals; degree=(:b5, :b9), derivative=(0, 1))On nonuniform grids each dimension is padded by its own stencil radius, with no wasted memory. Mixed uniform/nonuniform grids are handled per-dimension automatically.
Mixed antiderivative/derivative orders (uniform grids)
Pass a tuple with -1 entries to integrate in some dimensions and differentiate in others. This enables Leibniz-rule-style operations in a single interpolation object:
# Integrate in x, differentiate in y
itp = convolution_interpolation((x, y), vals; degree=:b7, derivative=(-1, 1))
itp(1.0, 2.0) # ≈ ∂/∂y ∫ f dxBoth slow and fast evaluation paths are supported. Not available on nonuniform grids.
Pretty printing
ConvolutionInterpolation, FastConvolutionInterpolation, and ConvolutionExtrapolation now display concise, human-readable summaries instead of the raw type parameter wall:
FastConvolutionInterpolation(Float64, 2D, coefs=38×42, degree=(:b5, :b9), derivative=(1, 0))
ConvolutionExtrapolation(ConvolutionInterpolation(Float64, 2D, coefs=40×44, degree=(:b5, :b9), derivative=(0, 0), nonuniform), bc=Throw)
Tests
- 1038 tests passing (up from 754)
- Added
test_nonuniform_perdim_derivatives.jl, test_perdim_derivatives.jl, and test_mixed_integral.jlwhich test combinations for per-dim uniform/nonuniform integrals/derivatives and scalar/tuple consistency
Breaking Changes
None. All existing code continues to work without modification. Scalar degree and derivative arguments behave identically to before.
Migration Guide
No migration needed. This release is fully backwards compatible.
The new tuple syntax is purely additive, existing scalar usage is unchanged:
# These still work exactly as before
itp = convolution_interpolation(x, y; degree=:b7)
itp = convolution_interpolation((x, y), vals; degree=:b7, derivative=1)v0.8.0 — 2026-03-08
ConvolutionInterpolations v0.8.0
ConvolutionInterpolations.jl v0.8.0 Release Notes
Breaking changes
None. All existing API defaults are preserved. All existing code works without modification.
New features
Antiderivative support (derivative=-1)
Pass derivative=-1 to compute the indefinite integral of the interpolant:
x = range(0.0, 2π, length=100)
y = sin.(x)
itp = convolution_interpolation(x, y; derivative=-1)
itp(1.0) # ≈ 0.4597 (= 1 - cos(1), anchored at x=0)The result is zero-anchored at the leftmost interior knot (F(anchor) = 0 exactly). In N dimensions the result is the mixed partial antiderivative, consistent with the tensor product structure used throughout the package.
Supported kernels (uniform grids only): :a1, :a3, :a4, :a5, :a7, :b5, :b7, :b9, :b11, :b13. Not yet supported on non-uniform grids.
Implementation. Each kernel K is analytically integrated to produce K̃ with coefficients stored as exact rational numbers. The fast path (default) precomputes the anchor-side sum Σⱼ cⱼ · K̃((anchor − xⱼ)/h) once at construction and stores it as left_values — one Vector{Float64} per dimension. Evaluation then only computes the K̃(x) half, giving zero-allocation O(stencil) performance identical in character to derivative=0. For b-series kernels the anchor-side sum uses exact Rational{BigInt} arithmetic before conversion to Float64, preserving the kernel’s full precision.
Dispatch type cleanup
IntegralOrder and DerivativeOrder{DO} are now the canonical types for the derivative slot of the interpolant type. This replaces the previous bare Val{0} usage and resolves a dispatch ambiguity in nonuniform functors that could produce incorrect method resolution at runtime.
Tests
754 tests (up from 671), including 83 new antiderivative tests covering 1D/2D/3D convergence for fast and direct paths, fast vs. direct agreement, anchor condition (F(anchor) = 0), and all supported kernels.
Migration guide v0.7.0 → v0.8.0
No changes required. New functionality is opt-in via derivative=-1.
# New in v0.8.0
x = range(0.0, 2π, length=100)
y = sin.(x)
itp = convolution_interpolation(x, y; derivative=-1) # antiderivativeDeclaration of AI assistance
Benchmarking, release notes, and documentation review performed with assistance from Claude (Anthropic).
v0.7.0 — 2026-03-06
ConvolutionInterpolations v0.7.0
ConvolutionInterpolations.jl v0.7.0 Release Notes
Breaking Changes
None. All existing API defaults are preserved. The default kernel remains :b5, lazy defaults to false, and all existing keyword arguments retain their previous behavior. This release adds new features and keyword arguments with backward-compatible defaults only.
New Features
Lazy evaluation system
New lazy keyword argument for all constructors. When lazy=true, ghost point expansion is skipped at construction time — boundary ghost values are computed on the fly during evaluation only when the stencil reaches near a domain boundary. Interior evaluation is identical to eager mode.
Construction speedup scales with grid size and dimensionality: up to 349× faster for :b5 on 4D 30⁴ grids. Lazy construction is constant-time (~0.12 ms) regardless of grid size.
Implemented for both uniform and nonuniform grids: - Uniform lazy: lazy_ghost_value computes ghost points per stencil point - Nonuniform lazy: _nonuniform_lazy_coef handles per-stencil boundary logic
When lazy=true, boundary_fallback and Line() extrapolation are automatically enabled to prevent expensive tensor-product ghost computation in high dimensions.
Dimension-independent linear extrapolation
Line() extrapolation now uses a 2-point directional derivative instead of per-dimension 4th-order finite differences. Cost is 2 interpolation evaluations regardless of dimensionality — up to 9× faster than the previous approach in 4D. The old calculate_gradient and calculate_component_gradient functions have been removed.
Boundary fallback protection
New boundary_fallback keyword argument. When lazy=true, this is automatically enabled together with Line() extrapolation, so near-boundary evaluations are linearly extrapolated from the interior instead of computing expensive tensor-product ghost points. This prevents the Julia REPL from hanging on expensive corner computations in high dimensions.
Nonuniform :a0 and :a1
Nearest-neighbor (:a0) and linear (:a1) interpolation now work natively on nonuniform grids without requiring ghost points. These kernels are automatically detected and routed to the nonuniform evaluation path.
A-series derivatives
Derivatives are now supported for all a-series kernels (:a3, :a4, :a5, :a7). The top derivative order automatically switches to linear interpolation of the kernel to match the kernel’s continuity class, via the max_smooth_derivative lookup and validate_subgrid_compatibility.
Specialized dimension dispatch
Optimized evaluation paths for 1D, 2D, and 3D via Val(N), with a separate generic HigherDimension(Val(N)) path for 4D+. This enables dimension-specific optimizations without affecting the general ND fallback.
Per-dimension kernel boundary conditions
kernel_bc now accepts a tuple of (left, right) symbol pairs for per-dimension, per-side control of boundary conditions. The constructor validates that each dimension has enough grid points for the selected boundary condition; when insufficient, :polynomial silently falls back to :detect for that dimension only, preserving optimal boundary handling in all other dimensions.
Improvements
Performance
- Shipped kernel tables: Pre-shipped tables at
precompute=101mean zero computation at construction time for the default settings (already present in v0.6.0, now documented more prominently) - Constant-time lazy construction: ~0.12 ms regardless of grid size or dimension when
lazy=true - 2-point directional extrapolation: O(1) in dimensionality, replacing O(N) finite difference gradient
Bug fixes
:n3now forcesfast=false: Fixes a bug where explicitly passingdegree=:n3to the low-levelFastConvolutionInterpolationconstructor could incorrectly use precomputed tables not designed for nonuniform kernels
Tests
- Low-level constructor tests: New test coverage for
ConvolutionInterpolation,FastConvolutionInterpolation, andConvolutionExtrapolationconstructors directly, across all kernels - Tightened convergence tests: Test thresholds now match actual kernel convergence orders (e.g. error ratio ≥ 64 per grid doubling for b-series) instead of the previous conservative threshold of 2. Separate thresholds for fast vs direct evaluation paths, and per-derivative loosening accounting for ~1 order loss per derivative
- Lazy correctness tests: Verify eager vs lazy agreement at interior and boundary points for uniform and nonuniform grids in 1D, 2D, and 3D
- Lazy boundary fallback tests: Verify auto-enabling of
boundary_fallbackin 4D+ and correctLine()extrapolation behavior - Nonuniform
:a0/:a1tests: Grid point reproduction, midpoint averaging, exact linear/bilinear/trilinear reproduction, and lazy field verification - Construction speed test: Verify lazy construction of 50³
:b5completes in under 100ms - Test suite: 671 tests total, covering uniform/nonuniform grids, lazy/eager modes, derivatives up to 2nd order, convergence rates, extrapolation boundary conditions, and boundary fallback across 1D–5D
- Test documentation: New
TESTING.mddocumenting the full test suite structure, strategies, and parameters
Documentation
- Updated docstrings: Comprehensive docstrings for
convolution_interpolation,ConvolutionInterpolation,FastConvolutionInterpolation,ConvolutionExtrapolation,GaussianConvolutionKernel,_domain_bounds, and individual per-kernel docstrings for all a-series and b-series kernels - Updated README: New high-dimensional interpolation section with benchmark tables, lazy mode documentation, updated kernel reference tables with nonuniform
:a0/:a1, three evaluation paths in Technical Background, updated Speed section referencing lazy mode, new performance guidelines, improved Quick Start examples
Internal Changes
- Three evaluation paths: direct, fast (precomputed tables), and binomial expansion
_domain_boundshelper accounts for lazy mode and boundary fallbackextrapolate_pointLine branch simplified from O(N) to O(1) evaluationsvalidate_subgrid_compatibilityenforces subgrid/derivative/kernel compatibilitymax_smooth_derivativedict maps each kernel to its maximum smooth derivative order- Dead code removed:
calculate_gradient,calculate_component_gradient
v0.6.0 — 2026-03-01
ConvolutionInterpolations v0.6.0
ConvolutionInterpolations.jl v0.6.0 Release Notes
Highlights
Constructor performance improved by up to 44× through pre-shipped kernel tables and allocation-free boundary computation. The default 1D/2D setup path now requires zero disk I/O. Evaluation performance is unchanged.
New features
Pre-shipped kernel tables. Kernel tables for all b-series kernels are now shipped as package constants computed at exact rational precision. The default constructor loads these from memory, i.e. no Scratch.jl caching or disk I/O needed. Higher resolutions, BigFloat, or linear subgrid still compute on demand and cache to disk as before.
Automatic linear subgrid for 3D+. Since cubic/quintic subgrid is only implemented for 1D and 2D, the constructor now explicitly falls back to subgrid=:linear with precompute=max(precompute, 10_000) for N≥3, ensuring adequate table resolution.
Allocation-free boundary computation. The BoundaryWorkspace now achieves fully allocation-free boundary condition computation.
Function barrier pattern. Internal helpers with concrete return types eliminate type instability from the constructor.
Performance
Representative 1D benchmarks (b5 kernel, 100-point grid):
| Metric | v0.5 | v0.6 | Speedup |
|---|---|---|---|
| Construction | 95 μs, 618 allocs | 5 μs, 40 allocs | 19× |
| Evaluation | 12 ns | 20 ns | — |
Evaluation is slightly higher than previously reported because benchmarks now include the extrapolation wrapper and the default :cubic subgrid. Bypassing the extrapolation (itp.itp(x)) and using :linear subgrid recovers lower timings. Construction speedups increase with dimensionality (up to 47× for 4D b13).
Migration guide
No breaking changes. All existing code works without modification.
Shipped tables. The default precompute=101 with :cubic subgrid now loads pre-shipped constants for 1D/2D.
3D+ automatic fallback. For N≥3, the constructor switches to subgrid=:linear, precompute=10_000. This uses the Scratch.jl cache.
POLYNOMIAL_GHOST_COEFFS stored as Float64. Previously Matrix{Int}. The integer values are exactly representable so there is no precision loss.
Internal changes
FastConvolutionInterpolationconstructor forcessubgrid=:linearwithprecompute≥10_000for N≥3apply_boundary_conditions_for_dim!refactored: ghost matrix fetched once, in-place mean-centering, polynomial/recursive path determined per sideget_boundary_indicesusesntuple(Val(N))for zero-allocation index constructionfill_ghost_points_polynomial!andfill_ghost_points_recursive!acceptAbstractVector{T}get_recursive_coefsextracted as standalone helper; oldboundary_coefsremoved- Old Scratch-only
get_precomputed_kernel_and_rangeremoved - Comprehensive docstring update across all source files
Declaration of AI assistance
Constructor optimization and docstring review performed with assistance from Claude (Anthropic).
v0.5.1 — 2026-02-28
ConvolutionInterpolations v0.5.1
ConvolutionInterpolations.jl v0.5.1
Breaking changes
No breaking changes. All changes are internal bugfixes and optimizations.
Bugfixes
- Fixed polynomial boundary condition sign error for right-side boundaries in 2D+ arrays, which caused incorrect ghost point extrapolation in multidimensional nonuniform interpolation.
- Tightened derivative convergence test tolerances, enabled by the boundary fix.
Performance
- Uniform dimensions in mixed uniform/nonuniform grids now skip the Vandermonde projection during weight precomputation, using direct binomial expansion instead. This halves setup time for 2D grids with one uniform and one non-uniform dimension, with larger savings as the number of uniform dimensions increases.
v0.5.0 — 2026-02-26
ConvolutionInterpolations v0.5.0
Breaking changes
No breaking changes. The API is unchanged and all changes are internal. Existing code works without modification. Non-uniform grids are detected automatically.
How it works
On uniform grids, kernel weights are the same for every interval. On non-uniform grids, each interval needs its own weights adapted to the local grid geometry. This is achieved by expanding the kernel in a binomial series (using Rational{BigInt} arithmetic to avoid floating-point contamination in the weight generation) around each interval’s local coordinate, then projecting through a Vandermonde system to enforce polynomial reproduction up to the kernel’s design order. The result is a compact set of polynomial coefficients per interval, evaluated via Horner’s method at query time.
Ghost point values near domain boundaries are also computed via Vandermonde solves using Rational{BigInt} arithmetic.
New features
- Non-uniform b-kernels:
:b5through:b13now work on non-uniform grids with per-interval weight polynomials precomputed via exact Rational{BigInt} arithmetic - Non-uniform derivatives: Derivatives d1 through d6 (depending on kernel continuity) on non-uniform grids. Each derivative order loses approximately one order of convergence
:n3kernel: Dedicated non-uniform cubic kernel equivalent to non-uniform Catmull-Rom splines. All:akernels fall back to:n3on non-uniform grids- Zero-allocation evaluation: ~40ns per point via Horner’s method
Tests
414 tests (up from ~300), including non-uniform grid point reproduction (1D/2D/3D), derivative accuracy (d1/d2 in 1D/2D), convergence rate verification (d0/d1/d2), and uniform convergence tests for both fast and direct evaluation paths.
Migration guide v0.4.x → v0.5.0
Before (v0.4.x) — non-uniform grids only supported cubic interpolation:
x = [0.0, 0.3, 1.0, 1.8, 3.0, 5.0]
y = sin.(x)
itp = convolution_interpolation(x, y) # falls back to :n3 (cubic)After (v0.5.0) — same API, now with high-order b-kernels and derivatives:
x = [0.0, 0.3, 1.0, 1.8, 3.0, 5.0, 5.5, 6.2]
y = sin.(x)
itp = convolution_interpolation(x, y; degree=:b5)
itp_d1 = convolution_interpolation(x, y; degree=:b5, derivative=1)Uniform grid code is completely unaffected.
v0.4.1 — 2026-02-23
ConvolutionInterpolations v0.4.1
v0.4.1
Improved
- Nonuniform grid boundary handling: Ghost points at domain boundaries are now computed via closed-form Vandermonde-derived coefficients that reproduce polynomials up to degree 2, replacing the previous clamp-based approach. Recovers the uniform integer coefficients [3, -3, 1] in the uniform limit. Clean O(h³) convergence on nonuniform grids, consistent with interior accuracy.
v0.4.0 — 2026-02-22
ConvolutionInterpolations v0.4.0
v0.4.0 — Nonuniform Grid Support
New Features
Nonuniform grid interpolation in any dimension. Nonuniform grids are detected automatically — the existing API works without changes:
x = [0.0, 0.3, 1.0, 1.8, 3.0, 4.0, 4.5, 5.0]
itp = convolution_interpolation(x, sin.(x))
itp(2.5) # just worksThis extends to N-dimensional tensor product grids where each dimension can have its own nonuniform spacing:
kx = [0.0, 0.3, 1.0, 1.8, 3.0]
ky = [0.0, 0.5, 1.2, 2.0, 3.5]
data = [sin(x) * cos(y) for x in kx, y in ky]
itp = convolution_interpolation((kx, ky), data)Properties: - Cubic polynomial weights with 3rd order convergence (O(h³)) - Exact reproduction of polynomials up to degree 2 - Allocation-free evaluation (~12 ns in 1D) - Recovers Keys’ cubic kernel (a = −1/2) in the uniform limit - Mathematically equivalent to nonuniform Catmull-Rom interpolation
Adaptive refinement. Unlike Chebyshev interpolation (which requires recalculating all samples) or uniform grids (which require roughly doubling samples), nonuniform grids allow adding a single sample exactly where needed — all existing samples are reused.
Migration Guide
No breaking changes. The existing API is unchanged. Uniform grids continue to use the same optimized fast path. The nonuniform path activates automatically only when non-equal spacings are detected. No migration required. All existing code works without changes.
v0.3.1 — 2026-02-22
ConvolutionInterpolations v0.3.1
v0.3.1 Release Notes
Bugfix: Pre-range type promotion causing allocations in ND evaluation
The precomputed kernel range (pre_range) was stored as Vector{Rational{BigInt}} instead of Vector{Float64}, causing type promotion to BigFloat during evaluation. This resulted in massive allocations and degraded performance in 2D+ interpolation.
Impact: 2D evaluation ~50× slower than expected, 3D-4D progressively worse. 1D unaffected due to specialized dispatch.
Fix: pre_range is now converted to the target float type before caching. Kernel precomputation still uses exact rational arithmetic internally for precision.
Action required
Clear the cached kernels to remove stale Rational{BigInt} data:
using Scratch
Scratch.clear_scratchspaces!()Kernels will regenerate automatically on next use.
v0.3.0 — 2026-02-12
ConvolutionInterpolations v0.3.0
ConvolutionInterpolations.jl v0.3.0 Release Notes
Breaking
- Renamed
b3→a4: The classic cubic 4-tap kernel (Keys 1981) now lives in thea-series where it belongs. Thebprefix is reserved exclusively for the novel kernel family discovered in this package.
New features
- Derivative interpolation: Compute derivatives through analytically predifferentiated convolution kernels — no finite differences, no step-size tuning. Supports up to 6th order depending on kernel continuity class.
- Subgrid interpolation modes: Cubic and quintic Hermite subgrid interpolation using predifferentiated kernels. Achieves high accuracy with as few as 100 precomputed points, dramatically reducing memory while improving quality over linear subgrid.
Corrections
- Continuity classes rigorously verified: b5 (C³), b7 (C⁴), b9 (C⁵), b11 (C⁶), b13 (C⁶). Previous claims of higher continuity did not hold up under verification. C⁶ is still far beyond the C¹ that literature kernels actually achieve.
Migration guide: v0.2.0 → v0.3.0
Kernel rename: b3 → a4
This is the only breaking change. If you used the b3 kernel, replace it with a4:
# Before (v0.2.0)
itp = convolution_interpolation(x, y; degree=:b3)
# After (v0.3.0)
itp = convolution_interpolation(x, y; degree=:a4)This rename reflects the kernel’s origin: a4 is the 4-tap cubic kernel from the literature (Keys 1981), not a member of the novel b-series family. The kernel itself is unchanged — only the name is different.
No other breaking changes
All other API remains the same. The new derivative and subgrid keyword arguments are additive and default to their v0.2.0 behavior when not specified:
# These calls work identically in v0.2.0 and v0.3.0
itp = convolution_interpolation(x, y)
itp = convolution_interpolation(x, y; degree=:b5)
itp = convolution_interpolation((xs, ys), data; kernel_bc=:polynomial)New features available after upgrading
# Derivatives (new in v0.3.0)
itp_d1 = convolution_interpolation(x, y; derivative=1) # first derivative
itp_d2 = convolution_interpolation(x, y; derivative=2) # second derivative
# Subgrid modes (new in v0.3.0)
itp = convolution_interpolation(x, y; subgrid=:cubic) # default, uses 1st derivative
itp = convolution_interpolation(x, y; subgrid=:quintic) # uses 1st and 2nd derivatives
itp = convolution_interpolation(x, y; subgrid=:linear) # v0.2.0 behaviorv0.2.0 — 2025-11-20
ConvolutionInterpolations v0.2.0
ConvolutionInterpolations.jl - Significant Release
This release represents a significant evolution of ConvolutionInterpolations.jl, featuring a breakthrough in boundary condition handling that unlocks the full potential of the existing b-series kernels, along with substantial performance optimizations. While the main interface remains largely unchanged, several defaults and internal behaviors have been updated for optimal performance.
🎯 Highlights
- Novel polynomial boundary conditions: Vandermonde-based ghost point computation enabling b-series kernels to achieve their theoretical 7th order convergence all the way to domain edges (previously these kernels existed but couldn’t reach their design accuracy)
- Default kernel changed: Now defaults to
:b5(quintic reproduction, C³ continuity) instead of:a3 - True O(1) performance: Elimination of binary search operations for constant-time interpolation
- Persistent kernel caching: First-use generation with disk caching via Scratch.jl for instant subsequent loads
- Extended precision support: Kernel coefficients stored as exact rationals enabling BigFloat arithmetic
- Allocation-free evaluation: All fast-mode evaluations allocate zero memory
- Kernel discovery tools: New
gen/folder with scripts for deriving kernels and boundary conditions
🔄 Breaking Changes
Default Kernel Changed
Before: Default was :a3 (cubic, C¹ continuous, 4th order accurate) After: Default is :b5 (quintic, C³ continuous, 7th order accurate)
Migration: If you require the old behavior, explicitly specify degree=:a3:
# Old code (implicit default)
itp = convolution_interpolation(x, y)
# New code for same behavior
itp = convolution_interpolation(x, y; degree=:a3)Rationale: The b5 kernel provides dramatically better accuracy (7th order vs 4th order) with comparable performance. With the new polynomial boundary conditions, it can now achieve its designed accuracy, making it a superior default for most use cases.
Boundary Condition Default Changed
Before: Default was kernel_bc=:detect After: Default is kernel_bc=:auto
Migration: No action needed in most cases. The :auto option intelligently: - Prioritizes :polynomial boundary conditions when sufficient grid points exist - Falls back to :detect for sparse grids or compatibility
Rationale: The new :polynomial boundary condition (computed via Vandermonde systems) preserves polynomial reproduction properties at boundaries, ensuring 7th order convergence extends to domain edges.
Precompute Default Increased
Before: Default was precompute=1_000 (1,000 points) After: Default is precompute=100_000 (100,000 points)
Migration: For memory-constrained applications, explicitly reduce:
itp = convolution_interpolation(x, y; precompute=10_000)Rationale: The default now provides maximum accuracy with negligible memory cost (~1.6 MB per kernel). Since kernel lookups are O(1) regardless of precompute resolution, there’s no performance penalty for the higher default.
Kernel Coefficients Now Type-Generic
Before: Kernel coefficients were stored as Float64 After: Coefficients stored as exact Rational{Int} and converted to evaluation type
Impact: This is transparent for Float64 usage but enables extended precision:
# Now supports arbitrary precision
x_big = big.(x)
y_big = big.(y)
itp = convolution_interpolation(x_big, y_big) # Uses BigFloat precisionMigration: No changes needed. Existing Float64 code works identically.
New Dependencies
Before: Zero external dependencies After: Minimal dependencies added for caching functionality - LinearAlgebra: Standard library (for Vandermonde systems) - Serialization: Standard library (for kernel caching) - Scratch.jl: External package (for persistent disk cache management)
Migration: No code changes needed. Scratch.jl will be installed automatically as a dependency.
Rationale: These minimal dependencies enable persistent kernel caching (eliminating redundant computation) and the new polynomial boundary condition method.
✨ New Features
Breakthrough: Polynomial Boundary Conditions
The major advancement in this release: a Vandermonde-based boundary condition method that enables the existing b-series kernels to achieve their theoretical convergence properties.
Background: The b-series kernels (b5, b7, b9, b11, b13) have existed in the package since their discovery via systematic analytical search. However, they suffered from poor boundary handling that prevented them from achieving their designed 7th order convergence.
The Breakthrough: Novel :polynomial boundary condition method: - Computes optimal ghost point values by solving Vandermonde systems - Preserves each kernel’s polynomial reproduction properties at domain boundaries - Ensures 7th order convergence extends all the way to domain edges - Automatically selected by :auto when sufficient grid points available
Impact: The b-series kernels can now achieve their designed accuracy on the challenging Runge function:
| Kernel | Polynomial Reproduction | Continuity | Order | Runge Accuracy (1000 pts, Float64) |
|---|---|---|---|---|
:b5 |
Quintic (degree 5) | C³ | 7th | ~10⁻¹⁴ |
:b7 |
Septic (degree 7) | C⁵ | 7th | ~10⁻¹² |
:b9 |
Septic (degree 7) | C⁷ | 7th | ~10⁻¹⁰ |
:b11 |
Septic (degree 7) | C⁹ | 7th | ~10⁻⁹ |
:b13 |
Septic (degree 7) | C¹¹ | 7th | ~10⁻⁸ |
Note: Higher-degree kernels are more affected by Float64 machine precision limitations. All kernels continue converging beyond Float64 precision when using BigFloat arithmetic.
# Explicitly use polynomial boundary conditions
itp = convolution_interpolation(x, y; kernel_bc=:polynomial)Kernel Discovery Tools
New gen/ folder containing scripts for kernel and boundary condition discovery:
generalized_kernel_derivation.py: Systematic kernel discovery via symbolic boundary value problems- Generalizes Keys (1981) method to arbitrary polynomial degrees
- Exhaustive search through boundary condition combinations
- Taylor series verification of convergence order
- Frequency domain optimization via FFT analysis
- Can reproduce and verify published kernels (Keys cubic, Meijering quintic/septic)
compute_ghost_coefficients.jl: Boundary condition computation- Solves Vandermonde systems for optimal ghost point coefficients
- Ensures polynomial reproduction at domain boundaries
- Generates coefficients for any kernel degree
- Outputs exact rational coefficients for arbitrary precision
These tools enable researchers to: - Discover new convolution kernels for specific applications - Verify published kernel properties - Compute optimal boundary conditions for custom kernels - Extend the method to specialized interpolation requirements
Persistent Kernel Caching
Discretized kernels are computed once and cached to disk using Scratch.jl: - First use: 0.1-10 seconds to generate (depends on kernel order and precompute size) - Subsequent uses: <1ms to load from disk cache - Precision-aware: Separate caches for Float64, Float32, BigFloat(256), etc. - Automatic: No user intervention required
Extended Precision Support
All kernels support arbitrary precision arithmetic:
# Use BigFloat for accuracy beyond Float64 machine precision
setprecision(256) # 256-bit precision
x = range(big(0.0), big(2π), length=100)
y = sin.(x)
itp = convolution_interpolation(x, y) # Automatically uses BigFloatKernel coefficients stored as exact rationals enable convergence well beyond standard machine precision.
🚀 Performance Improvements
True O(1) Interpolation
- Eliminated all binary search operations
- Direct index calculation on uniform grids
- Interpolation time independent of grid size (verified: same ~15ns from 10 to 10,000 points)
Allocation-Free Evaluation
All fast-mode kernel evaluations are allocation-free:
# Zero allocations per evaluation
@allocated itp(0.5) # Returns 0Kernel Discretization with Linear Interpolation
Novel implementation approach: - Discretizes kernels at high resolution (default 100,000 points) - Uses linear interpolation between precomputed values - More numerically stable than direct polynomial evaluation for practical kernel degrees - Faster than Horner’s method while maintaining accuracy
Benchmark Results (1D)
| Kernel | Initialization | Evaluation | Accuracy (Runge, 1000 pts, Float64) |
|---|---|---|---|
:a1 |
~75 μs | 4 ns | ~10⁻³ |
:b5 |
~1.9 ms | 15 ns | ~10⁻¹⁴ |
:b7 |
~2.4 ms | 16 ns | ~10⁻¹² |
:b9 |
~2.8 ms | 17 ns | ~10⁻¹⁰ |
:b11 |
~3.2 ms | 18 ns | ~10⁻⁹ |
:b13 |
~3.5 ms | 19 ns | ~10⁻⁸ |
Note: Higher-degree kernels (b9-b13) are progressively more affected by Float64 machine precision limitations. For applications requiring their full accuracy potential, use BigFloat arithmetic - all kernels continue converging beyond Float64 precision.
📚 API Additions
Kernel Options (Clarification)
The following kernels were already available in the package:
Low-order kernels:
:a0, :a1, :a3, :b3Higher-order a-series (from literature):
:a5, :a7B-series kernels (7th order convergence):
:b5, :b7, :b9, :b11, :b13Note: These kernels existed in previous versions but could not achieve their designed accuracy due to boundary condition limitations. With the new polynomial boundary conditions, they now work as intended.
Gaussian smoothing:
B=1.0 # ExistingNew Boundary Condition Options
# Automatic (prioritizes :polynomial, falls back to :detect)
kernel_bc=:auto # ⭐ New default
# Polynomial reproduction (optimal)
kernel_bc=:polynomial # ⭐ New
# Existing options
kernel_bc=:detect, :linear, :quadratic, :periodic🔧 Migration Guide
Minimal Migration (Keep Existing Behavior)
If you want to maintain exact previous behavior:
# Change from:
itp = convolution_interpolation(x, y)
# To:
itp = convolution_interpolation(x, y; degree=:a3, kernel_bc=:detect, precompute=1_000)Recommended Migration (Adopt New Defaults)
Most users should simply update their code to use the new defaults, which provide better accuracy and performance:
# Old code
itp = convolution_interpolation(x, y) # Was :a3
# New code - no changes needed, now uses :b5!
itp = convolution_interpolation(x, y)The b5 kernel with polynomial boundary conditions provides superior accuracy (7th order vs 4th order) with comparable performance to the old cubic default.
Memory-Constrained Environments
If memory is extremely limited (embedded systems, etc.):
# Reduce precompute resolution (degrades accuracy slightly)
itp = convolution_interpolation(x, y; precompute=10_000)
# Or disable fast mode entirely (slower but minimal memory)
itp = convolution_interpolation(x, y; fast=false)High-Precision Applications
For applications requiring extended precision:
# Use BigFloat with desired precision
setprecision(512) # 512-bit precision
x = big.(your_data_x)
y = big.(your_data_y)
itp = convolution_interpolation(x, y) # Automatically uses BigFloat📊 Comparison with Previous Version
| Aspect | Previous | Current | Improvement |
|---|---|---|---|
| Default kernel | :a3 (cubic) |
:b5 (quintic) |
500× better accuracy |
| B-kernel functionality | Existed but inaccurate | Fully functional | Achieves designed 7th order |
| Convergence order | 4th | 7th | 1.75× order improvement |
| Boundary handling | Detect-based | Polynomial-optimal | Maintains accuracy at edges |
| Evaluation performance | Slower, some type instabilities | ~4-19ns (1D), O(1) guaranteed | Major performance upgrade |
| Memory allocations | Variable, not allocation-free | Zero (fast mode) | Allocation-free |
| Initialization | Per-call overhead | Cached after first use | ~1000× faster subsequent uses |
| Precision support | Float64 only | Any (Float32, Float64, BigFloat) | Unlimited precision |
| Dependencies | Zero | Minimal (stdlib + Scratch.jl) | Enables caching & boundaries |
🐛 Bug Fixes
- Fixed binary search scaling in higher dimensions
- Corrected boundary coefficient computation for periodic signals
- Improved numerical stability of kernel evaluation near boundaries
- Fixed type promotion issues with mixed-precision data
🔬 Technical Details
Kernel Discovery Method
The b-series kernels were discovered through systematic analytical search using SymPy: 1. Formulate boundary value problem as symbolic equations 2. Exhaustively search boundary condition combinations 3. Verify polynomial reproduction via Taylor series expansion 4. Validate frequency domain characteristics via FFT analysis 5. Select kernels with optimal accuracy/smoothness balance
This automated discovery process generalizes the approach of Keys (1981) to arbitrary polynomial degrees, enabling kernels that would be intractable to derive by hand.
Exact Rational Storage
Kernel coefficients stored as Rational{Int}:
# Example b5 kernel coefficient
-1228311859//656862720 # Exact rational, not Float64This enables: - Arbitrary precision evaluation - No accumulated rounding errors in coefficient storage - Reproducible results across platforms and precision levels
📖 References
Novel Contributions (This Work)
- Polynomial boundary condition method via Vandermonde systems (major breakthrough)
- Kernel discretization with linear interpolation approach
- O(1) performance via direct indexing on uniform grids
- Persistent kernel caching via Scratch.jl
- B-series kernel discovery via systematic analytical search (kernels existed in package previously, but discovery method is novel)
Foundational Work
- R.G. Keys, “Cubic Convolution Interpolation for Digital Image Processing,” IEEE Trans. ASSP, 1981
- E.H.W. Meijering et al., “Image Reconstruction by Convolution with Symmetrical Piecewise nth-Order Polynomial Kernels,” IEEE Trans. IP, 1999
🙏 Acknowledgments
Special thanks to the Interpolations.jl maintainers for API design inspiration and to the Julia community for feedback during development.
📝 License
MIT License - see LICENSE file for details
Questions or Issues? - GitHub Issues: [ConvolutionInterpolations.jl/issues] - Documentation: [Full package documentation] - Examples: See README.md for comprehensive examples
Upgrading from v0.x? Most code will work without changes. For exact previous behavior, see the “Minimal Migration” section above.
Merged pull requests: - Bump actions/checkout from 4 to 5 (#5) (@dependabot [bot])
v0.1.4 — 2025-08-03
ConvolutionInterpolations v0.1.4
v0.1.3 — 2025-08-02
ConvolutionInterpolations v0.1.3
v0.1.2 — 2025-05-22
ConvolutionInterpolations v0.1.2
Closed issues: - Support for non-uniform grid? (#3)