JuliaDiff / JuliaDiff/ForwardDiff.jl

Implement hessian! for scalar x

Open
#661 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Julia
Stars
1k
Forks
160
PR merge metrics
No merged PRs in 30d

Description

If I have a function f(x::Real)::Real I have two options for computing f(x), f'(x), f''(x):

using ForwardDiff, DiffResults, StaticArrays, BenchmarkTools

function computehessian(f, x::AbstractArray)
    result = DiffResults.HessianResult(x)
    result = ForwardDiff.hessian!(result, f, x)
    return DiffResults.value(result), DiffResults.gradient(result), DiffResults.hessian(result)
end
computehessian(f, x::T) where T <: Number = map(first, computehessian(f ∘ first, SVector{1, T}(x)))

function computehessian2(f, x)
    df(x)  = ForwardDiff.derivative(f, x)
    d2f(x) = ForwardDiff.derivative(df, x)
    return (f(x), df(x), d2f(x))
end

x = rand()
w = rand()
s1 = rand()
s2 = rand()
f(w, s1, s2, x) = log(w * s1 * exp(-0.5 * x * s1 ^ 2) + (1-w) * s2 * exp(-0.5 * x * s2 ^ 2))
expandfunc(args, v) = args[1](args[2:end]..., v)
fixallbutlast(func, args...) = Base.Fix1(expandfunc, (func, args...))
g = fixallbutlast(f, w, s1, s2)
@btime computehessian($g, $x)
@btime computehessian2($g, $x)

  69.075 ns (1 allocation: 64 bytes)
  83.289 ns (0 allocations: 0 bytes)

The first approach, using the hessian! function, is faster because the value, 1st and 2nd derivatives can all be computed with a single call to f (using a DiffResult structure), whereas the second approach requires 4 calls of f to perform the same calculation. If f is slow, then the second approach is much worse. However, the first approach doesn't support scalar x::T (where T <: Number), requiring a wrapper to get around this. Furthermore, it causes an allocation, making the calculation slower than necessary.

Is it possible to implement the functionality of hessian! with a DiffResult input, but for scalar x? If so, is it possible to avoid the allocation (assuming f doesn't allocate)?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by inspecting the existing ForwardDiff.hessian! entry point and its DiffResults.HessianResult usage for array inputs. Trace how scalar x is currently wrapped through a one-element SVector, then determine the expected scalar API and allocation behavior. Done means scalar inputs support the same value, gradient, and Hessian result without the wrapper allocation when f itself does not allocate.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.