From 6a9963d71c505f5e21ca6d0f4eeb66068ad513ef Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 09:28:45 -0400 Subject: [PATCH 01/11] Fix loop variable in test --- test/interpolation_tests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index e8862469..e71fd811 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -90,7 +90,7 @@ end @test @inferred(LinearInterpolation( u_s, t; extrapolation = ExtrapolationType.Extension)) isa LinearInterpolation A_s = LinearInterpolation(u_s, t; extrapolation = ExtrapolationType.Extension) - for _x in (0, 5.5, 11) + for x in (0, 5.5, 11) @test A(x) == A_s(x) end @test A_s(0) isa SVector{length(y)} From 5c62660965c0f5c798f321809a2d0f8502d219cd Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 09:41:53 -0400 Subject: [PATCH 02/11] Add allocation test with `Vector{SVector}` input --- test/interpolation_tests.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index e71fd811..070dcd67 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -331,6 +331,7 @@ end @test @inferred(output_dim(A)) == 1 @test @inferred(output_size(A)) == (2,) + # Vector{Vector} interpolation test u_ = [1.0, 4.0, 9.0, 16.0]' .* ones(5) u = [u_[:, i] for i in 1:size(u_, 2)] A = @inferred(QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.Extension)) @@ -341,7 +342,18 @@ end @test A(5.0) == 25.0 * ones(5) @test @inferred(output_dim(A)) == 1 @test @inferred(output_size(A)) == (5,) + # Test allocation-free interpolation with Vector{StaticArrays.SVector} + u_s = [convert(SVector{length(u[1])}, i) for i in u] + @test @inferred(QuadraticInterpolation( + u_s, t; extrapolation = ExtrapolationType.Extension)) isa QuadraticInterpolation + A_s = QuadraticInterpolation(u_s, t; extrapolation = ExtrapolationType.Extension) + for x in (0, 1.5, 2.5, 3.5, 5.0) + @test A(x) == A_s(x) + end + @test A_s(0) isa SVector{length(u[1])} + @test_nowarn test_allocs(A_s, 0) + # Vector{Matrix} interpolation test u = [repeat(u[i], 1, 3) for i in 1:4] @test_broken @inferred(QuadraticInterpolation( u, t; extrapolation = ExtrapolationType.Extension)) isa QuadraticInterpolation From 67de387073fe72d097de7f41986f2a95c4de1397 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 10:45:08 -0400 Subject: [PATCH 03/11] Revise `SmoothedConstantInterpolation`, test The `zero(one(eltype(u)) / 2)` in `smoothed_constant_interpolation_parameters` made this interpolation fail for array-valued interpolations because `one(::AbstractArray)` is not defined. I replaced this with `zero(first(u) / 2)` which should maintain the same type stability across the different branches of the function. Tests added verifying the new functionality. --- src/parameter_caches.jl | 2 +- test/interpolation_tests.jl | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/parameter_caches.jl b/src/parameter_caches.jl index 31206829..bd904377 100644 --- a/src/parameter_caches.jl +++ b/src/parameter_caches.jl @@ -61,7 +61,7 @@ function smoothed_constant_interpolation_parameters( else d = isone(idx) ? min(t[2] - t[1], 2d_max) / 2 : min(t[end] - t[end - 1], 2d_max) / 2 - d, zero(one(eltype(u)) / 2) + d, zero(first(u) / 2) end else min(t[idx] - t[idx - 1], t[idx + 1] - t[idx], 2d_max) / 2, (u[idx] - u[idx - 1]) / 2 diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index 070dcd67..b6572435 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -659,6 +659,30 @@ end @test A(1.9) == u[1] @test A(3.1) == u[2] @test A(2.5) ≈ (u[1] + u[2]) / 2 + + u_ = u' .* ones(5) # [u for i in 1:length(t)] + uv = [u_[:, i] for i in 1:size(u_, 2)] + # Test Vector{Vector} interpolation + A = SmoothedConstantInterpolation(uv, t; d_max) + @test A(1.9) == uv[1] + @test A(3.1) == uv[2] + @test A(2.5) ≈ (uv[1] + uv[2]) / 2 + # Test allocation-free interpolation with Vector{StaticArrays.SVector} + u_s = [convert(SVector{length(uv[1])}, i) for i in uv] + @test_broken @inferred(SmoothedConstantInterpolation( + u_s, t; d_max)) isa SmoothedConstantInterpolation + A_s = SmoothedConstantInterpolation(u_s, t; d_max) + for x in (1.9, 3.1, 2.5) + @test A(x) == A_s(x) + end + @test A_s(1.9) isa SVector{length(uv[1])} + @test_nowarn test_allocs(A_s, 1.9) + # Test Vector{Matrix} interpolation + um = [repeat(uv[i], 1, 3) for i in 1:length(t)] + A = SmoothedConstantInterpolation(um, t; d_max) + @test A(1.9) == u[1] * ones(5, 3) + @test A(3.1) == u[2] * ones(5, 3) + @test A(2.5) ≈ ((u[1] + u[2]) / 2) * ones(5, 3) end @testset "QuadraticSpline Interpolation" begin From 435601b827adb209763cd469841e4149b88ece47 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 11:09:30 -0400 Subject: [PATCH 04/11] Previously broken CubicSpline tests now pass 1.11? --- test/interpolation_tests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index b6572435..a6762fac 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -777,8 +777,8 @@ end @test @inferred(output_size(A)) == (4,) u = [repeat(u[i], 1, 3) for i in 1:3] - @test_broken @inferred(CubicSpline( - u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline + @test @inferred(CubicSpline( + u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline broken=VERSION < v"1.11" A = CubicSpline(u, t; extrapolation = ExtrapolationType.Extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4, 3) @@ -814,7 +814,7 @@ end 0.0 cos(2t)] t = 0.1:0.1:1.0 u3d = f3d.(t) - @test_broken @inferred(CubicSpline(u3d, t)) isa CubicSpline + @test @inferred(CubicSpline(u3d, t)) isa CubicSpline broken=VERSION < v"1.11" c = CubicSpline(u3d, t) t_test = 0.1:0.05:1.0 u_test = reduce(hcat, c.(t_test)) From 5d09d2a8b413c38aae8a497d4cecc514586a7b8e Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 11:20:15 -0400 Subject: [PATCH 05/11] Add `Vector{SVector}` tests to `CubicSpline` --- test/interpolation_tests.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index a6762fac..c0f0d19f 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -764,6 +764,7 @@ end u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] + # Test Vector{Vector} interpolation @test @inferred(CubicSpline(u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline broken=VERSION < v"1.11" A = CubicSpline(u, t; extrapolation = ExtrapolationType.Extension) @@ -775,7 +776,18 @@ end end @test @inferred(output_dim(A)) == 1 @test @inferred(output_size(A)) == (4,) + # Test allocation-free interpolation with StaticArrays + u_s = [convert(SVector{length(first(u))}, i) for i in u] + @test_broken @inferred(CubicSpline( + u_s, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline + A_s = CubicSpline(u_s, t; extrapolation = ExtrapolationType.Extension) + for x in (-1.5, -0.5, -0.7) + @test A(x) == A_s(x) + end + @test A_s(0) isa SVector{length(first(u))} + @test_nowarn test_allocs(A_s, 0) + # Test Vector{Matrix} interpolation u = [repeat(u[i], 1, 3) for i in 1:3] @test @inferred(CubicSpline( u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline broken=VERSION < v"1.11" From 6ec9cedc33434f03c6c3abd973004d8b90730a51 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 11:27:02 -0400 Subject: [PATCH 06/11] Add `Vector{SVector}` allocation test to `ConstantInterpolation` --- test/interpolation_tests.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index c0f0d19f..d109afc6 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -580,6 +580,16 @@ end test_cached_index(A) @test @inferred(output_dim(A)) == 1 @test @inferred(output_size(A)) == (2,) + + # Test allocation-free interpolation with StaticArrays + u_s = [convert(SVector{length(first(u))}, i) for i in u] + A_s = @inferred(ConstantInterpolation( + u_s, t; extrapolation = ExtrapolationType.Extension)) + for x in 0.5:0.5:4.5 + @test A(x) == A_s(x) + end + @test A_s(0) isa SVector{length(first(u))} + @test_nowarn test_allocs(A_s, 0) end @testset "Vector of Matrices case" for u in [ From c1641beec171162a2bfa8763f3714218976ee1da Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 11:51:24 -0400 Subject: [PATCH 07/11] Assert return type of `findfirst` It can be Int or Nothing, but bounds are checked during extrapolation so we can assume it returns an Int. This fixes a dynamic dispatch found by AllocCheck.jl. --- src/interpolation_utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpolation_utils.jl b/src/interpolation_utils.jl index ff16d56f..f04aa5b6 100644 --- a/src/interpolation_utils.jl +++ b/src/interpolation_utils.jl @@ -38,7 +38,7 @@ function spline_coefficients!(N, d, k, u::Number) N[end] = one(u) return length(N):length(N) else - i = findfirst(x -> x > u, k) - 1 + i = findfirst(x -> x > u, k)::Int - 1 N[i] = one(u) for deg in 1:d N[i - deg] = (k[i + 1] - u) / (k[i + 1] - k[i - deg + 1]) * N[i - deg + 1] From 7d7bf9883eab069d7a97ff0249be6a243bc2ef5f Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 12:03:44 -0400 Subject: [PATCH 08/11] Add `Vector{SVector}` tests for `QuadraticSpline` --- test/interpolation_tests.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index d109afc6..f5ec8995 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -717,6 +717,7 @@ end @test @inferred(output_dim(A)) == 0 @test @inferred(output_size(A)) == () + # Test Vector{Vector} interpolation u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.Extension) @@ -726,7 +727,16 @@ end @test A(2.0) == P₁(2.0) * ones(4) @test @inferred(output_dim(A)) == 1 @test @inferred(output_size(A)) == (4,) + # Test allocation-free interpolation with Vector{StaticArrays.SVector} + u_s = [convert(SVector{length(u[1])}, i) for i in u] + A_s = @inferred(QuadraticSpline(u_s, t; extrapolation = ExtrapolationType.Extension)) + for x in (-2.0, -0.5, 0.7, 2.0) + @test A(x) == A_s(x) + end + @test A_s(0.7) isa SVector{length(u[1])} + @test_nowarn test_allocs(A_s, 0.7) + # Test Vector{Matrix} interpolation u = [repeat(u[i], 1, 3) for i in 1:3] A = @inferred(QuadraticSpline(u, t; extrapolation = ExtrapolationType.Extension)) @test A(-2.0) == P₁(-2.0) * ones(4, 3) From f12ec69a4613c1d50884f78b63b7586ca2c1fdd2 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 12:10:21 -0400 Subject: [PATCH 09/11] `Vector{SVector}` tests for `CubicHermiteSpline` --- test/interpolation_tests.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index f5ec8995..c5d4815a 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -1020,8 +1020,18 @@ end @testset "Vector of Vectors case" begin u2 = [[u[i], u[i] + 1] for i in eachindex(u)] du2 = [[du[i], du[i]] for i in eachindex(du)] - A2 = CubicHermiteSpline(du2, u2, t) + A2 = CubicHermiteSpline(du2, u2, t; extrapolation = ExtrapolationType.Extension) @test u2 ≈ A2.(t) + @test A2(100.0) ≈ repeat([10.106770], 2) + [0, 1] rtol=1e-5 + @test A2(300.0) ≈ repeat([9.901542], 2) + [0, 1] rtol=1e-5 + # Test allocation-free interpolation with Vector{StaticArrays.SVector} + u2_s = [convert(SVector{length(u2[1])}, i) for i in u2] + du2_s = [convert(SVector{length(du2[1])}, i) for i in du2] + A2_s = @inferred(CubicHermiteSpline(du2_s, u2_s, t; extrapolation = ExtrapolationType.Extension)) + @test A2_s(100.0) == A2(100.0) + @test A2_s(300.0) == A2(300.0) + @test A2_s(0.7) isa SVector{length(u2[1])} + @test_nowarn test_allocs(A2_s, 0.7) end @testset "Vector of Matrices case" begin u3 = [[u[i] u[i] + 1] for i in eachindex(u)] From 01b5a8dbae98ffaf6109632c0fbab2988b0c81b2 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 12:12:48 -0400 Subject: [PATCH 10/11] `Vector{SVector}` tests for `QuinticHermiteSpline` --- test/interpolation_tests.jl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index c5d4815a..10f59f0e 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -1081,8 +1081,19 @@ end u2 = [[u[i], u[i] + 1] for i in eachindex(u)] du2 = [[du[i], du[i]] for i in eachindex(du)] ddu2 = [[ddu[i], ddu[i]] for i in eachindex(ddu)] - A2 = QuinticHermiteSpline(ddu2, du2, u2, t) + A2 = QuinticHermiteSpline(ddu2, du2, u2, t; extrapolation = ExtrapolationType.Extension) @test u2 ≈ A2.(t) + @test A2(100.0) ≈ repeat([10.107996], 2) + [0, 1] rtol=1e-5 + @test A2(300.0) ≈ repeat([11.364162], 2) + [0, 1] rtol=1e-5 + # Test allocation-free interpolation with Vector{StaticArrays.SVector} + u2_s = [convert(SVector{length(u2[1])}, i) for i in u2] + du2_s = [convert(SVector{length(du2[1])}, i) for i in du2] + ddu2_s = [convert(SVector{length(du2[1])}, i) for i in ddu2] + A2_s = @inferred(QuinticHermiteSpline(ddu2_s, du2_s, u2_s, t; extrapolation = ExtrapolationType.Extension)) + @test A2_s(100.0) == A2(100.0) + @test A2_s(300.0) == A2(300.0) + @test A2_s(0.7) isa SVector{length(u2[1])} + @test_nowarn test_allocs(A2_s, 0.7) end @testset "Vector of Matrices case" begin u3 = [[u[i] u[i] + 1] for i in eachindex(u)] From b119ee688357a4eafba992cd5f0d62926c723161 Mon Sep 17 00:00:00 2001 From: cgarling Date: Wed, 3 Sep 2025 12:41:24 -0400 Subject: [PATCH 11/11] Fix `@inferred` tests on Julia < 1.11 --- test/interpolation_tests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index 10f59f0e..4ea6cb26 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -786,7 +786,7 @@ end u = [u_[:, i] for i in 1:size(u_, 2)] # Test Vector{Vector} interpolation @test @inferred(CubicSpline(u, t; extrapolation = ExtrapolationType.Extension)) isa - CubicSpline broken=VERSION < v"1.11" + CubicSpline A = CubicSpline(u, t; extrapolation = ExtrapolationType.Extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4) @@ -810,7 +810,7 @@ end # Test Vector{Matrix} interpolation u = [repeat(u[i], 1, 3) for i in 1:3] @test @inferred(CubicSpline( - u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline broken=VERSION < v"1.11" + u, t; extrapolation = ExtrapolationType.Extension)) isa CubicSpline A = CubicSpline(u, t; extrapolation = ExtrapolationType.Extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4, 3) @@ -846,7 +846,7 @@ end 0.0 cos(2t)] t = 0.1:0.1:1.0 u3d = f3d.(t) - @test @inferred(CubicSpline(u3d, t)) isa CubicSpline broken=VERSION < v"1.11" + @test @inferred(CubicSpline(u3d, t)) isa CubicSpline c = CubicSpline(u3d, t) t_test = 0.1:0.05:1.0 u_test = reduce(hcat, c.(t_test))