# Autodiffing with Enzyme through a DifferentialEquations ODEproblem

**URL:** <https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257>\
**Category:** General Usage\
**Tags:** question\
**Created:** [February 24, 2025, 5:46pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257 "2025-02-24T17:46:05Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![dameka](https://avatars.discourse-cdn.com/v4/letter/d/77aa72/32.png) [@dameka](https://discourse.julialang.org/u/dameka)\
**Post date:** [February 24, 2025, 5:46pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/1 "2025-02-24T17:46:05Z")

</div>

I’m trying to autodiff an ODE problem that I’m solving with DifferentialEquations.jl. Here’s my code:

```julia
begin
	# Import needed packages
	using DifferentialEquations
	using Enzyme
	#using Zygote
	#using SciMLSensitivity
end

function exp_ode(t)
	f(u,p,t) = 0.98u
	u0 = 1.0
	tspan = (0.0,1.0)
	prob = DifferentialEquations.ODEProblem(f,u0,tspan)
	sol = DifferentialEquations.solve(prob,abstol=1e-8,reltol=1e-8,saveat=t)
	return sol.u
end

times = Vector(LinRange(0, 1, 10))
du_dt = Enzyme.jacobian(set_runtime_activity(Reverse), t -> exp_ode(t), times)

```

The error message:

```julia
Enzyme execution failed.
Enzyme: Non-constant keyword argument found for Tuple{UInt64, typeof(Core.kwcall), EnzymeCore.Duplicated{@NamedTuple{abstol::Float64, reltol::Float64, saveat::Vector{Float64}}}, typeof(EnzymeCore.EnzymeRules.augmented_primal), EnzymeCore.EnzymeRules.RevConfigWidth{1, true, true, (false, true, false, false, false), true}, EnzymeCore.Const{typeof(DiffEqBase.solve_up)}, Type{EnzymeCore.Duplicated{Any}}, EnzymeCore.Duplicated{SciMLBase.ODEProblem{Float64, Tuple{Float64, Float64}, false, SciMLBase.NullParameters, SciMLBase.ODEFunction{false, SciMLBase.AutoSpecialize, Main.var"workspace#102".var"#f#1", LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing, Nothing, Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}, SciMLBase.StandardODEProblem}}, EnzymeCore.Const{Nothing}, EnzymeCore.Active{Float64}, EnzymeCore.Const{SciMLBase.NullParameters}}

```

This is on Julia 1.11.3 and Enzyme v0.13.30

---

<div class="post-metadata">

**Author:** ![jClugstor](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jclugstor/32/32689_2.png) [@jClugstor](https://discourse.julialang.org/u/jClugstor)\
**Post date:** [February 24, 2025, 6:21pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/2 "2025-02-24T18:21:04Z")

</div>

I think the problem might be that you’re trying to differentiate with respect to a keyword argument? I’m not sure but I don’t think you can do that in Enzyme.

---

<div class="post-metadata">

**Author:** ![dameka](https://avatars.discourse-cdn.com/v4/letter/d/77aa72/32.png) [@dameka](https://discourse.julialang.org/u/dameka)\
**Post date:** [February 24, 2025, 6:25pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/3 "2025-02-24T18:25:36Z")

</div>

That would certainly make sense. But in that case, how is it possible to differentiate wrt time with this sort of problem? I don’t see another way to use ODEproblem to make `t` a non-keyword argument.

---

<div class="post-metadata">

**Author:** ![yolhan\_mannes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yolhan_mannes/32/220485_2.png) [@yolhan\_mannes](https://discourse.julialang.org/u/yolhan_mannes)\
**Post date:** [February 24, 2025, 6:26pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/4 "2025-02-24T18:26:24Z")

</div>

You could interpolate the solution and then take its derivative (DataInterpolation.jl for instance), note that f(sol,p,t) should be the derivative though by definition

---

<div class="post-metadata">

**Author:** ![dameka](https://avatars.discourse-cdn.com/v4/letter/d/77aa72/32.png) [@dameka](https://discourse.julialang.org/u/dameka)\
**Post date:** [February 24, 2025, 6:33pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/5 "2025-02-24T18:33:19Z")

</div>

That’s not a bad idea. Good point on the f(…) being the derivative, but the goal was to show “you can do this”, and I suppose I learned you can’t (directly). I will try a more interesting function and taking the derivative wrt parameters instead of time.

---

<div class="post-metadata">

**Author:** ![yolhan\_mannes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yolhan_mannes/32/220485_2.png) [@yolhan\_mannes](https://discourse.julialang.org/u/yolhan_mannes)\
**Post date:** [February 24, 2025, 6:37pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/6 "2025-02-24T18:37:33Z")

</div>

You will need SciMLSensitivity.jl

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [February 25, 2025, 1:33pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/7 "2025-02-25T13:33:40Z")

</div>

> [@jClugstor](#):
>
> I think the problem might be that you’re trying to differentiate with respect to a keyword argument? I’m not sure but I don’t think you can do that in Enzyme.

Yeah you cannot differentiate w.r.t. the save points with Enzyme because it doesn’t handle keyword arguments like that.

For the record, you can do this with ForwardDiff. You would need to do a trick of forcing duals on your state and time though, i.e.:

```julia
	u0 = eltype(t)(1.0)
	tspan = eltype(t).((0.0,1.0))

```

and then ForwardDiff.jl would give you the derivatives w.r.t. the save points `t`. This is used in optimal experimental design. I know

> **[GitHub - mathopt/DynamicOED.jl: Optimal experimental design of ODE and DAE systems...](https://github.com/mathopt/DynamicOED.jl)**
>
> Optimal experimental design of ODE and DAE systems in julia

uses this

---

<div class="post-metadata">

**Author:** ![dameka](https://avatars.discourse-cdn.com/v4/letter/d/77aa72/32.png) [@dameka](https://discourse.julialang.org/u/dameka)\
**Post date:** [February 25, 2025, 8:25pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/8 "2025-02-25T20:25:21Z")

</div>

This is great, thank you Chris. Some quirks about the output I’m getting:

```julia
function exp_ode(times)
	f(u,p,t) = 0.98*u
	u0 = eltype(times)(1.0)
	tspan = eltype(times).((0.0, 1.0))
	prob = DifferentialEquations.ODEProblem(f,u0,tspan)
	sol = DifferentialEquations.solve(prob,abstol=1e-8,reltol=1e-8,saveat=times)
	return sol.u
end

begin
	times = Vector(LinRange(0, 1, 10))
	p = 0.98
	du_dt = ForwardDiff.jacobian(exp_ode, times)
	du_dt_diag = diag(du_dt)
end

```

du\_dt is a 10x10 matrix, the jacobian. I know I can extract the derivative from the diagonal, but it’s frustrating that I can’t just use FD.derivative() since technically it’s a vector values function (in reality, a scalar function evaluated at many point). Is there a more elegant solution than using the jacobian?

The quirk is that du\_dt(0) and du\_dt(1) evaluated this way both come out to 0 (upper left and bottom right):

```julia
10×10 Matrix{Float64}:
 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 0.0 1.09274 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 0.0 0.0 1.21844 0.0 0.0 0.0 0.0 0.0 0.0 0.0
 0.0 0.0 0.0 1.35861 0.0 0.0 0.0 0.0 0.0 0.0
 0.0 0.0 0.0 0.0 1.51491 0.0 0.0 0.0 0.0 0.0
 0.0 0.0 0.0 0.0 0.0 1.68918 0.0 0.0 0.0 0.0
 0.0 0.0 0.0 0.0 0.0 0.0 1.8835 0.0 0.0 0.0
 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.10017 0.0 0.0
 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.34177 0.0
 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

```

Is this a result of our workaround with the keyword argument?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [February 25, 2025, 10:19pm UTC](https://discourse.julialang.org/t/autodiffing-with-enzyme-through-a-differentialequations-odeproblem/126257/9 "2025-02-25T22:19:52Z")

</div>

the easiest thing would be just use the analytical solution to the sparse coloring problem, which would be to just do the `[1,1,1,...,1]` vector as the directional derivative. What this would look like is:

```julia
ForwardDiff.partials(exp_ode(ForwardDiff.Dual.(times, 1)))[1]

```

which should give you that vector. Why that would work… is a much longer story 😅
