@@ -288,6 +288,66 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T} <:
288288 end
289289end
290290
291+ # In-place scalar kernel for computing the Akima / makima coefficients.
292+ # Allocates a single length-(n+3) buffer for the padded divided differences;
293+ # every other intermediate (dm, f1, f2, f12, w1, w2, ind, b-default) from the
294+ # original vectorized formulation is fused into a scalar pass.
295+ function _akima_init! (
296+ b:: AbstractVector{T} , c:: AbstractVector{T} , d:: AbstractVector{T} ,
297+ u:: AbstractVector , t:: AbstractVector , :: Val{modified}
298+ ) where {T, modified}
299+ n = length (u)
300+ m = Vector {T} (undef, n + 3 )
301+ @inbounds begin
302+ for i in 1 : (n - 1 )
303+ m[i + 2 ] = (u[i + 1 ] - u[i]) / (t[i + 1 ] - t[i])
304+ end
305+ m[2 ] = 2 * m[3 ] - m[4 ]
306+ m[1 ] = 2 * m[2 ] - m[3 ]
307+ m[n + 2 ] = 2 * m[n + 1 ] - m[n]
308+ m[n + 3 ] = 2 * m[n + 2 ] - m[n + 1 ]
309+
310+ # First pass: maximum weight, used as the small-weight cutoff
311+ wmax = zero (T)
312+ for i in 1 : n
313+ if modified
314+ w1 = abs (m[i + 3 ] - m[i + 2 ]) + abs (m[i + 3 ] + m[i + 2 ]) / 2
315+ w2 = abs (m[i + 1 ] - m[i]) + abs (m[i + 1 ] + m[i]) / 2
316+ else
317+ w1 = abs (m[i + 3 ] - m[i + 2 ])
318+ w2 = abs (m[i + 1 ] - m[i])
319+ end
320+ w12 = w1 + w2
321+ wmax = ifelse (w12 > wmax, w12, wmax)
322+ end
323+ tol = T (1.0e-9 ) * wmax
324+
325+ # Second pass: coefficients
326+ for i in 1 : n
327+ if modified
328+ w1 = abs (m[i + 3 ] - m[i + 2 ]) + abs (m[i + 3 ] + m[i + 2 ]) / 2
329+ w2 = abs (m[i + 1 ] - m[i]) + abs (m[i + 1 ] + m[i]) / 2
330+ bdefault = (m[i + 1 ] + m[i + 2 ]) / 2
331+ else
332+ w1 = abs (m[i + 3 ] - m[i + 2 ])
333+ w2 = abs (m[i + 1 ] - m[i])
334+ bdefault = (m[i + 3 ] + m[i]) / 2
335+ end
336+ w12 = w1 + w2
337+ b[i] = w12 > tol ?
338+ (w1 * m[i + 1 ] + w2 * m[i + 2 ]) / w12 :
339+ bdefault
340+ end
341+
342+ for i in 1 : (n - 1 )
343+ dt = t[i + 1 ] - t[i]
344+ c[i] = (3 * m[i + 2 ] - 2 * b[i] - b[i + 1 ]) / dt
345+ d[i] = (b[i] + b[i + 1 ] - 2 * m[i + 2 ]) / (dt * dt)
346+ end
347+ end
348+ return nothing
349+ end
350+
291351function AkimaInterpolation (
292352 u, t; modified:: Bool = false ,
293353 extrapolation:: ExtrapolationType.T = ExtrapolationType. None,
@@ -301,40 +361,11 @@ function AkimaInterpolation(
301361 u, t = munge_data (u, t)
302362 linear_lookup = seems_linear (assume_linear_t, t)
303363 n = length (t)
304- dt = diff (t)
305- m = Array {eltype(u)} (undef, n + 3 )
306- m[3 : (end - 2 )] = diff (u) ./ dt
307- m[2 ] = 2 m[3 ] - m[4 ]
308- m[1 ] = 2 m[2 ] - m[3 ]
309- m[end - 1 ] = 2 m[end - 2 ] - m[end - 3 ]
310- m[end ] = 2 m[end - 1 ] - m[end - 2 ]
311-
312- if modified
313- # Modified Akima (makima): adds |m_{i+1} + m_i| / 2 to each weight, which
314- # reduces overshoot on flat regions. The simple-average fallback still
315- # guards the case where all four neighboring slopes vanish.
316- w1 = abs .(m[4 : end ] .- m[3 : (end - 1 )]) .+
317- abs .(m[4 : end ] .+ m[3 : (end - 1 )]) ./ 2
318- w2 = abs .(m[2 : (end - 2 )] .- m[1 : (end - 3 )]) .+
319- abs .(m[2 : (end - 2 )] .+ m[1 : (end - 3 )]) ./ 2
320- w12 = w1 .+ w2
321- b = (m[2 : (end - 2 )] .+ m[3 : (end - 1 )]) ./ 2
322- ind = findall (w12 .> 1.0e-9 * maximum (w12))
323- b[ind] = (w1[ind] .* m[ind .+ 1 ] .+ w2[ind] .* m[ind .+ 2 ]) ./ w12[ind]
324- else
325- b = (m[4 : end ] .+ m[1 : (end - 3 )]) ./ 2
326- dm = abs .(diff (m))
327- f1 = dm[3 : (n + 2 )]
328- f2 = dm[1 : n]
329- f12 = f1 + f2
330- ind = findall (f12 .> 1.0e-9 * maximum (f12))
331- b[ind] = (
332- f1[ind] .* m[ind .+ 1 ] .+
333- f2[ind] .* m[ind .+ 2 ]
334- ) ./ f12[ind]
335- end
336- c = (3 .* m[3 : (end - 2 )] .- 2 .* b[1 : (end - 1 )] .- b[2 : end ]) ./ dt
337- d = (b[1 : (end - 1 )] .+ b[2 : end ] .- 2 .* m[3 : (end - 2 )]) ./ dt .^ 2
364+ T = eltype (u)
365+ b = Vector {T} (undef, n)
366+ c = Vector {T} (undef, n - 1 )
367+ d = Vector {T} (undef, n - 1 )
368+ _akima_init! (b, c, d, u, t, Val (modified))
338369
339370 A = AkimaInterpolation (
340371 u, t, nothing , b, c, d, extrapolation_left,
0 commit comments