-
Notifications
You must be signed in to change notification settings - Fork 15
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
Add dual number–based second derivatives for ForwardDiff
#310
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #310 +/- ##
==========================================
+ Coverage 96.05% 96.08% +0.02%
==========================================
Files 95 95
Lines 4365 4398 +33
==========================================
+ Hits 4193 4226 +33
Misses 172 172 ☔ View full report in Codecov by Sentry. |
You can disregard the Enzyme failures, they're obviously unrelated |
Can you try using the functions in the |
I'll give it a try |
Cool, thanks! It should be similar to the pushforward one, albeit more intricate |
I'm using the functions from |
It seems my last comment was due to some confusion. In a previous comment you only mention the need to handle |
After a small fixup, the new functionality should be working now. I've tested it and it seems to handle vector-valued (w/ scalar input) functions just fine. |
Yes, derivative is for scalar to scalar or scalar to vector. Thank you for the contribution, I'll allow the workflows and review tomorrow |
Your code is never touched because you did not overload preparation, or provide methods which accept the additional "extras" argument. I'll fix it tomorrow, but if you want to do it yourself, you can take inspiration from the other methods in the extension |
I tried it but had to give up (at least for now), as I'm not familiar at all with how extras and |
ForwardDiff
ForwardDiff
Indeed, the current version of the package gives errors like this one, which are very hard to parse: type NoDerivativeExtras has no field pushforward_extras
Stacktrace:
[1] getproperty(x::DifferentiationInterface.NoDerivativeExtras, f::Symbol)
@ Base ./Base.jl:37
[2] value_and_derivative!(f::DifferentiationInterfaceTest.var"#num_to_arr#13"{StaticArraysCore.SMatrix{2, 3, Int64, 6}}, der::StaticArraysCore.MMatrix{2, 3, Float64, 6}, backend::AutoForwardDiff{2, Symbol}, x::Float64, extras::DifferentiationInterface.NoDerivativeExtras)
@ DifferentiationInterface ~/work/DifferentiationInterface.jl/DifferentiationInterface.jl/DifferentiationInterface/src/first_order/derivative.jl:91 I thus coded #313 which should make errors much more pleasant, let's see how it looks after merging |
And the new error is much more informative! I'll add these indications to the docs, but essentially it tells you that you forgot to implement some necessary methods: MethodError: no method matching derivative(::DifferentiationInterfaceTest.var"#num_to_arr#13"{StaticArraysCore.SMatrix{2, 3, Int64, 6}}, ::AutoForwardDiff{2, Symbol}, ::Float64, ::DifferentiationInterface.NoDerivativeExtras)
Closest candidates are:
derivative(::F, ::Any, ::AutoForwardDiff, ::Any, ::DifferentiationInterfaceForwardDiffExt.ForwardDiffTwoArgDerivativeExtras) where F
@ DifferentiationInterfaceForwardDiffExt ~/work/DifferentiationInterface.jl/DifferentiationInterface.jl/DifferentiationInterface/ext/DifferentiationInterfaceForwardDiffExt/twoarg.jl:95
derivative(::F, ::Any, ::ADTypes.AbstractADType, ::Any, ::DifferentiationInterface.PushforwardDerivativeExtras) where F
@ DifferentiationInterface ~/work/DifferentiationInterface.jl/DifferentiationInterface.jl/DifferentiationInterface/src/first_order/derivative.jl:147
derivative(::F, ::Any, ::ADTypes.AbstractADType, ::Any) where F
@ DifferentiationInterface ~/work/DifferentiationInterface.jl/DifferentiationInterface.jl/DifferentiationInterface/src/first_order/derivative.jl:127
... Whenever you implement a new
Sorry to use you as a bit of a lab rat, but you're the first one to try and contribute something nontrivial to DI (for which I'm grateful!). That means it's a good test for whether our process is clear enough or not (obviously not) |
No problem. Thanks! I've added Not sure about |
You can ask the docstrings: we return a tuple of the value and its derivative, whatever type they have, so no DiffResults. |
Do you want me to finish the PR? I definitely can if you get tired of the DI kinks. But having one more autodiff-interested individual look at the guts of the code is rather instructive for me |
I'll make my attempt to finish it |
All right, I've added the missing methods for |
I know what's going on!
So you actually need to code these, plus a suitable |
(in addition there are test failures due to typos but that's an easy fix) |
Are we sure that doesn't rely on DiffResults at all? |
|
Great. I'm still not sure what the fallback is for each method so I just wanted to check (a point of having |
Whenever it exists, I prefer sticking to the backend's existing API, in particular the public parts. My rationale is that I can't afford to try and optimize 13 backends better than their own individual creators, so I make do with what said creators give me. ForwardDiff might be a bit of an exception because technically the If you want to investigate how Generic Specific Do you think we can get better than this? |
Yes, I know, and appreciate the exception you're making here! In fact, I have an open PR (JuliaDiff/ForwardDiff.jl#678) at ForwardDiff asking for this to be implemented there, but with no luck so far.
Reading through the code, I don't think so. Also, in my benchmark case this PR is now running as fast as |
Spoiler alert, I think (hope) the tests will fail because you didn't implement |
Ouch! I'll add them. |
Is it just me or are the checks running forever? |
Sorry, I had an extra parameter (not sure where it came from) in second_derivative(!). Let's see now. I'm also now running the tests locally to see what happens. |
This file should be enough. Don't run the whole test suite you will need an hour or so |
That's what I did. Caught another bug :/. Running again to see if it's fixed |
When the tests fail, do you usually find it clear what you need to fix? |
Should be fixed now. I'm getting a couple of efficiency test errors with
Yes, you get the stack trace right there as soon as a test fails, which is nice. IDK how it could be made better. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution!
Thank you! Can I ask for a new release with this change? |
Yes, i was just waiting to check that the tests pass on main. |
Not really; I just saw that something is allocating when it shouldn't. FWIW these are the errors:
I'm on 1.10.4 on macOS arm64. |
Very strange! |
I'll be using it in Fronts to replace I should be ready to migrate as soon as the new release is live. I'll make sure to mention the relevant PR in #134. |
Interesting! Since it will also allow you to try other backends, I wonder if symbolic backends might be close to optimal for such a one-dimensional case. What kind of functions do you often end up differentiating? |
Thanks. It's probably worth a try. I should first learn how symbolic differentiation works with DI, but I think it's worth testing to see if I can use it to shave a few milliseconds off common cases.
Functions like this one (plus others also present in the |
For symbolic differentiation, preparation (the extras shenanigans) is very costly but absolutely essential, since it compiles executable versions of your function and its derivatives. At the moment I haven't super optimized that part because hardly anyone uses it. But if you want to use it, I'll definitely squeeze out some more performance |
Thanks. I'll give it a try. Any recommendation for which symbolic backend I can start with? |
As discussed in #305, I'm starting by copying the implementations from JuliaDiff/AbstractDifferentiation.jl#122 almost verbatim.