-
Notifications
You must be signed in to change notification settings - Fork 40
Closed
Description
I'm aware of #104 which was closed by #113, but Jacobians still allocate when using StaticArrays, even with a preallocated cache. Here's a barebones solution for a cache-free non-allocating Jacobian:
using StaticArrays
using BenchmarkTools
const RELSTEP = sqrt(eps(Float64))
const ABSSTEP = RELSTEP
function step(x)
return max(RELSTEP * abs(x), ABSSTEP)
end
function finite_difference_jacobian(f, x)
return finite_difference_jacobian(f, x, f(x))
end
@generated function finite_difference_jacobian(f, x::SVector{L}, fx) where {L}
if L > 32
# dimension too large for non-allocating Jacobian calculation
return :(finite_difference_jacobian_fallback(f, x, fx))
end
ex = Expr(:call, :hcat)
for i in 1:L
push!(ex.args, :(finite_difference_jacobian_column(f, x, fx, $i)))
end
return ex
end
function finite_difference_jacobian_column(f, x::SVector{L}, fx, i) where {L}
dxi = (x[i] + step(x[i])) - x[i] # Floating point accuracy trick https://twitter.com/willkurt/status/1330183861452541953
xdx = SVector{L}(j == i ? x[j] + dxi : x[j] for j in 1:L)
return (f(xdx) - fx) / dxi
end
f(x) = @SVector [x[2]^3, x[1]^2, x[1] - 3x[2]]
x = @SVector rand(2)
println("FiniteDiff.finite_difference_jacobian:")
using FiniteDiff; cache = FiniteDiff.JacobianCache(x)
@btime FiniteDiff.finite_difference_jacobian(f, $x, $cache)
println("Custom finite_difference_jacobian:")
@btime finite_difference_jacobian(f, Ref($x)[])Output:
FiniteDiff.finite_difference_jacobian:
272.326 ns (21 allocations: 624 bytes)
Custom finite_difference_jacobian:
15.568 ns (0 allocations: 0 bytes)
Could a specialization along these lines for small SVector problems be relevant for ForwardDiff? Unfortunately, I don't have the time to dig into the codebase and make a PR right now.
I had to use a generated function to hcat an arbitrary number of columns without splatting a generator, which apparently always allocates. Let me know if you know a better solution.
Edit: Included an ABSSTEP.
Edit 2: Fixed benchmark that was optimized away.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels