2
2
3
3
using BSplineKit
4
4
using QuadGK: quadgk
5
+ using StaticArrays
5
6
using ReverseDiff
6
7
using Test
7
8
@@ -49,7 +50,8 @@ function smoothing_objective(cs, R::AbstractBSplineBasis, xs, ys; weights = noth
49
50
loss
50
51
end
51
52
52
- function check_zero_gradient (S:: Spline , xs, ys; weights = nothing , λ)
53
+ # Scalar data
54
+ function _check_zero_gradient (:: Type{T} , S:: Spline , xs, ys; weights = nothing , λ, rtol = 1e-12 ) where {T <: Real }
53
55
R = basis (S) # usually a RecombinedBSplineBasis
54
56
cs = parent (coefficients (S)) # `parent` is useful if this is a PeriodicBSplineBasis
55
57
@@ -67,11 +69,27 @@ function check_zero_gradient(S::Spline, xs, ys; weights = nothing, λ)
67
69
# f ~ Y² and therefore ∂f/∂cⱼ ~ Y. So we compare it with the sum of |y_i|².
68
70
reference = sum (abs2, ys)
69
71
err = sum (abs2, ∇f)
70
- @test err / reference < 1e-12
72
+ @test err / reference < rtol
71
73
72
74
nothing
73
75
end
74
76
77
+ # Vector data (e.g. parametric splines)
78
+ # Currently all components are separately smoothed, so we verify them as separate scalar functions.
79
+ function _check_zero_gradient (:: Type{SVector{N, T}} , S:: Spline , xs, ys; kws... ) where {N, T}
80
+ R = basis (S)
81
+ cs = coefficients (S)
82
+ for i in 1 : N
83
+ Si = Spline (R, getindex .(cs, i))
84
+ check_zero_gradient (Si, xs, getindex .(ys, i); kws... )
85
+ end
86
+ nothing
87
+ end
88
+
89
+ function check_zero_gradient (S:: Spline , xs, ys; kws... )
90
+ _check_zero_gradient (eltype (ys), S, xs, ys; kws... )
91
+ end
92
+
75
93
# Returns the integral of |S''(x)| (the "curvature") over the whole spline.
76
94
function total_curvature (S:: Spline )
77
95
ts = knots (S)
148
166
Sw = fit (xs, ys, λ; weights)
149
167
check_zero_gradient (Sw, xs, ys; λ, weights)
150
168
end
169
+
170
+ @testset " Parametric" begin
171
+ λ = 1e-2
172
+ N = 100
173
+ ts = range (0 , 2 π; length = N + 1 )[1 : N]
174
+ vs = [0.1 * SVector (cos (t), sin (t)) .+ 0.01 * sin (10 * t) for t in ts]
175
+ S_nat = @inferred fit (ts, vs, λ, Natural ())
176
+ S_per = @inferred fit (ts, vs, λ, Periodic (2 π))
177
+
178
+ @testset " Natural" check_zero_gradient (S_nat, ts, vs; λ)
179
+ @testset " Periodic" check_zero_gradient (S_per, ts, vs; λ)
180
+
181
+ @testset " With weights" begin
182
+ weights = fill! (similar (ts), 1 )
183
+ weights[3 ] = 1000
184
+
185
+ S_nat = @inferred fit (ts, vs, λ, Natural (); weights)
186
+ S_per = @inferred fit (ts, vs, λ, Periodic (2 π); weights)
187
+
188
+ @testset " Natural" check_zero_gradient (S_nat, ts, vs; λ, weights)
189
+ @testset " Periodic" check_zero_gradient (S_per, ts, vs; λ, weights)
190
+ end
191
+ end
151
192
end
0 commit comments