Skip to content

Commit 28eae15

Browse files
committed
add missing file
1 parent 1ff98e1 commit 28eae15

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

src/operators/enzyme.jl

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using Enzyme
2+
3+
function init_cache(x)
4+
if !Enzyme.Compiler.guaranteed_const(typeof(x))
5+
create_shadow(x)
6+
else
7+
return nothing
8+
end
9+
end
10+
11+
function maybe_duplicated(x::T, x′::Union{Nothing, T}) where {T}
12+
if x′ === nothing
13+
return Const(x)
14+
else
15+
zero_shadow!(x′)
16+
return Duplicated(x, x′)
17+
end
18+
end
19+
20+
"""
21+
EnzymeJacobianOperator
22+
23+
Efficient implementation of `J(f,x,p) * v` and `v * J(f, x,p)'`
24+
"""
25+
struct EnzymeJacobianOperator{F, F′, A, P, P′} <: AbstractJacobianOperator
26+
f::F # F!(res, u, p)
27+
f′::F′ # cache
28+
res::A
29+
u::A
30+
p::P
31+
p′::P′ # cache
32+
end
33+
34+
"""
35+
EnzymeJacobianOperator(f::F, res, u, p; assume_p_const::Bool = false)
36+
37+
Creates a Jacobian operator for `f!(res, u, p)` where `res` is the residual,
38+
`u` is the state variable, and `p` are the parameters.
39+
40+
If `assume_p_const` is `true`, the parameters `p` are assumed to be constant
41+
during the Jacobian computation, which can improve performance by not requiring the
42+
shadow for `p`.
43+
"""
44+
function EnzymeJacobianOperator(f::F, res, u, p; assume_p_const::Bool = false) where {F}
45+
f′ = init_cache(f)
46+
if assume_p_const
47+
p′ = nothing
48+
else
49+
p′ = init_cache(p)
50+
end
51+
return EnzymeJacobianOperator(f, f′, res, u, p, p′)
52+
end
53+
54+
Base.size(J::EnzymeJacobianOperator) = (length(J.res), length(J.u))
55+
Base.eltype(J::EnzymeJacobianOperator) = eltype(J.u)
56+
Base.length(J::EnzymeJacobianOperator) = prod(size(J))
57+
58+
function mul!(out, J::EnzymeJacobianOperator, v)
59+
autodiff(
60+
Forward,
61+
maybe_duplicated(J.f, J.f′), Const,
62+
Duplicated(J.res, reshape(out, size(J.res))),
63+
Duplicated(J.u, reshape(v, size(J.u))),
64+
maybe_duplicated(J.p, J.p′)
65+
)
66+
return nothing
67+
end
68+
69+
LinearAlgebra.adjoint(J::EnzymeJacobianOperator) = Adjoint(J)
70+
LinearAlgebra.transpose(J::EnzymeJacobianOperator) = Transpose(J)
71+
72+
# Jᵀ(y, u) = ForwardDiff.gradient!(y, x -> dot(F(x), u), xk)
73+
# or just reverse mode
74+
75+
function mul!(out, J′::Union{Adjoint{<:Any, <:EnzymeJacobianOperator}, Transpose{<:Any, <:EnzymeJacobianOperator}}, v)
76+
J = parent(J′)
77+
# TODO: provide cache for `copy(v)`
78+
# Enzyme zeros input derivatives and that confuses the solvers.
79+
# If `out` is non-zero we might get spurious gradients
80+
fill!(out, 0)
81+
autodiff(
82+
Reverse,
83+
maybe_duplicated(J.f, J.f′), Const,
84+
Duplicated(J.res, reshape(copy(v), size(J.res))),
85+
Duplicated(J.u, reshape(out, size(J.u))),
86+
maybe_duplicated(J.p, J.p′)
87+
)
88+
return nothing
89+
end

0 commit comments

Comments
 (0)