DifferentiationInterface.jl icon indicating copy to clipboard operation
DifferentiationInterface.jl copied to clipboard

convert active argument in case of a preparation mismatch

Open tpapp opened this issue 3 months ago • 5 comments

I understand that the argument type has to match the prepared backend. However, instead of erroring, in some situations it would make sense to just convert to the right type. Eg in the example below, the last line could work, in accordance with the robustness principle (with the understanding that it allocates, is suboptimal, etc).

julia> import ForwardDiff

julia> import DifferentiationInterface as DI

julia> backend = DI.AutoForwardDiff()
AutoForwardDiff()

julia> test(x) = x .+ 1
test (generic function with 2 methods)

julia> x = ones(3);

julia> prep = prepare_jacobian(test, backend, x);

julia> DI.value_and_jacobian(test, prep, backend, x)
([2.0, 2.0, 2.0], [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0])

julia> DI.value_and_jacobian(test, prep, backend, Float32.(x))
ERROR: PreparationMismatchError (inconsistent types between preparation and execution):
  - f: ✅
  - backend: ✅
  - x: ❌
    - prep: Vector{Float64}
    - exec: Vector{Float32}
  - contexts: ✅
If you are confident that this check is superfluous, you can disable it by running preparation with the keyword argument `strict=Val(false)` inside DifferentiationInterface.

I would allow this always, but I understand that some users would just want to catch this performance issue as an error. So maybe an option, or a wrapper-like API

DI.value_and_jacobian(test, prep, backend, AutoConvert(Float32.(x)))

would make sense.

tpapp avatar Oct 01 '25 10:10 tpapp

I'm conflicted about this. I see the point but I also see the people who will shoot themselves in the foot. Note that it would probably happen through a third possible value for the strict kwarg during preparation: true / false / ... :convert maybe? @adrhill thoughts?

gdalle avatar Oct 08 '25 11:10 gdalle

also see the people who will shoot themselves in the foot

Can you please explain how? At worst, you get a bit of allocations. For arguments of nontrivial length, the cost of these is dominated by AD.

tpapp avatar Oct 08 '25 11:10 tpapp

Because this "strict" preparation mode is a good way to spot code smells. If you're preparing on one type but executing on another type, more often that not it reveals a bug in your program rather than something DI should handle more gracefully

gdalle avatar Oct 08 '25 11:10 gdalle

One of DI's early design decisions was to make the interface as explicit as possible. A convert flag seems reasonable, but then again it just takes Float32.(x) for the user to convert it themselves. In comparison, strict=Val(:convert) seems less user friendly.

If both Vector{Float64} and Vector{Float32} are needed, you could prepare both a prep64 and a prep32.

adrhill avatar Oct 08 '25 11:10 adrhill

One thing we could easily do without added complexity is expose a function to query the types with which a prep object was made. Would that be helpful in your case?

gdalle avatar Oct 08 '25 15:10 gdalle