JuliaDiff / DifferentiationInterface.jl

An interface to various automatic differentiation backends in Julia.
https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface
MIT License
197 stars 15 forks source link

manual "backend" #634

Open tpapp opened 5 days ago

tpapp commented 5 days ago

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 commented 5 days ago

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 commented 5 days ago

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 commented 5 days ago

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.