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

manual "backend" #634

Open
tpapp opened this issue Nov 20, 2024 · 3 comments
Open

manual "backend" #634

tpapp opened this issue Nov 20, 2024 · 3 comments
Labels
documentation Improvements or additions to documentation

Comments

@tpapp
Copy link

tpapp commented Nov 20, 2024

I am transitioning my packages to use DifferentiationInterface. I would like allow the user to specify derivatives manually though: either for testing purposes, or for simple problems where it makes sense.

Can this be done with DI in its current state? (sorry if I missed something obvious, I read the docs and part of the source)

@gdalle
Copy link
Member

gdalle commented Nov 20, 2024

It can definitely be done but it's a bit off the beaten path.
Essentially you would have to create a new backend type, the procedure is explained in https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/dev/dev_guide/. The thing is, semantically, it does not make sense to store derivative information for a specific function inside the backend itself. So I think the Julianic way would be to use multiple dispatch on the function type.
Here's a skeleton to give you the idea:

struct ManualBackend <: ADTypes.AbstractADType end

function myfunc(x)
    # stuff
end

function DI.derivative(::typeof(myfunc), ::ManualBackend, x)
    # explicit formula
end

function DI.value_and_derivative(::typeof(myfunc), ::ManualBackend, x)
    # explicit formula
end

# other variants or other operators

Does that make sense to you?
I have never tried it so maybe you would hit method ambiguities because this is a very unorthodox way of using DI. But if you do try it I can help out.

@tpapp
Copy link
Author

tpapp commented Nov 20, 2024

Yes, it makes sense, I was thinking of something like this, but using a wrapper

struct ManualBackendDifferentiable{TV,TD,TVD,...}
    value::TV
    derivative::TD
    value_and_derivative::TVD
    ...
end

function DI.value_and_derivative(mbd::ManualBackendDifferentiable, ::ManualBackend, x)
    (; value_and_derivative, value, derivative) = mbd
    if value_and_derivative === nothing
        value(x), derivative(x)
    else
        value_and_derivative(x)
    end
end

and similar, allowing to user to supply the required parts separately or together.

this is a very unorthodox way of using DI

I fully agree, the advantage would be exposing just one interface (that of DI) while still allowing the user to do this.

That said, in case the need arises this can be done for specific derivative combinations very easily in user code on an ad-hoc basis. So maybe a simple example in the manual would be enough for a start.

@gdalle
Copy link
Member

gdalle commented Nov 20, 2024

I had the wrapper idea too at first, but why ask the user to provide their own derivative when they can just override the DI.derivative function itself?
Maybe I could define ManualBackend in DI to save some headaches.

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

No branches or pull requests

2 participants