Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Second derivative of univariate functions: hessian() only takes arrays #891

Closed
vlepori opened this issue Jan 27, 2021 · 4 comments · Fixed by #890
Closed

Second derivative of univariate functions: hessian() only takes arrays #891

vlepori opened this issue Jan 27, 2021 · 4 comments · Fixed by #890

Comments

@vlepori
Copy link

vlepori commented Jan 27, 2021

Unlike gradient, hessian only has an (f, x::AbstractArray) method, so it won't take Floats. One has to pass an array of length one, and use a wrapper (anonymous) function to extract the value. Example:

using Zygote

f(x) = (sin(x)/x)

g = x -> Zygote.gradient(f, x)
g(2.0) # OK

h = x -> Zygote.hessian(f, x)
h(2.0) # no method matching..

h = x -> Zygote.hessian(x-> f(x[1]), x)
h([2.0]) # OK

Btw, the f''() notation fails for my actual function with a Can't differentiate foreigncall expression error

@mcabbott
Copy link
Member

mcabbott commented Feb 1, 2021

This is a bug, the docstring says it should work.

But for now, you could consider writing ForwardDiff.derivative(x -> ForwardDiff.derivative(x -> sin(x)/x, x), pi/2) instead. Zygote's hessian uses ForwardDiff to do the second derivative, so your function will probably need to be something ForwardDiff can work with in any case.

@bors bors bot closed this as completed in #890 Feb 2, 2021
@bors bors bot closed this as completed in f41d803 Feb 2, 2021
@vlepori
Copy link
Author

vlepori commented Feb 2, 2021

Thank you, that works, and without all the wrapping. For completeness, f'' fails when my functions has default arguments stored in a parameter Dict. But calling hessian or derivative works just fine. Example:

prms = Dict{Symbol,Float64}(:c => 1.0)

function myfun(x, c = prms[:c])
  3*x^2 + 2*x + c
end

ForwardDiff.derivative(x -> ForwardDiff.derivative(myfun, x), 3.0) # OK
Zygote.hessian(x -> myfun(x[]), [3.0]) # OK
myfun''(3.0) # ERROR: Can't differentiate foreigncall expression

@mcabbott
Copy link
Member

mcabbott commented Feb 2, 2021

Yes, unfortunately Zygote alone does not handle second derivatives very well. The rules it uses to work out gradients are often not written in such a way that they themselves are differentiable. Sometimes they work but it hasn't been something people focused on.

For this myfun, since you aren't differentiating with respect to c, you might be able to just hide it from Zygote, using ignore or @ignore.

For high-order derivatives of scalar functions, you may also look into TaylorSeries.jl.

@vlepori
Copy link
Author

vlepori commented Feb 2, 2021

Great, thanks a lot for the explanations! I think ForwardDiff covers all my needs for the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants