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

fix: use Enzyme's native Jacobian in forward mode with constant contexts #710

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DifferentiationInterface/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DifferentiationInterface"
uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
authors = ["Guillaume Dalle", "Adrian Hill"]
version = "0.6.36"
version = "0.6.37"

[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Moreover, each context type is supported by a specific subset of backends:
| `AutoChainRules` | ✅ | ❌ |
| `AutoDiffractor` | ❌ | ❌ |
| `AutoEnzyme` (forward) | ✅ | ✅ |
| `AutoEnzyme` (reverse) | ✅ | |
| `AutoEnzyme` (reverse) | ✅ | ❌ (soon) |
| `AutoFastDifferentiation` | ✅ | ✅ |
| `AutoFiniteDiff` | ✅ | ✅ |
| `AutoFiniteDifferences` | ✅ | ✅ |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,11 @@
end

function DI.prepare_gradient(
f::F, backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}}, x
) where {F}
f::F,
backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}},
x,
contexts::Vararg{DI.Constant,C},
) where {F,C}
valB = to_val(DI.pick_batchsize(backend, x))
shadows = create_shadows(valB, x)
return EnzymeForwardGradientPrep(valB, shadows)
Expand All @@ -131,23 +134,31 @@
prep::EnzymeForwardGradientPrep{B},
backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}},
x,
) where {F,B}
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
mode = forward_noprimal(backend)
f_and_df = get_f_and_df(f, backend, mode)
derivs = gradient(mode, f_and_df, x; chunk=Val(B), shadows=prep.shadows)
return only(derivs)
annotated_contexts = translate(backend, mode, Val(B), contexts...)
derivs = gradient(

Check warning on line 142 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L141-L142

Added lines #L141 - L142 were not covered by tests
mode, f_and_df, x, annotated_contexts...; chunk=Val(B), shadows=prep.shadows
)
return first(derivs)

Check warning on line 145 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L145

Added line #L145 was not covered by tests
end

function DI.value_and_gradient(
f::F,
prep::EnzymeForwardGradientPrep{B},
backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}},
x,
) where {F,B}
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
mode = forward_withprimal(backend)
f_and_df = get_f_and_df(f, backend, mode)
(; derivs, val) = gradient(mode, f_and_df, x; chunk=Val(B), shadows=prep.shadows)
return val, only(derivs)
annotated_contexts = translate(backend, mode, Val(B), contexts...)
(; derivs, val) = gradient(

Check warning on line 158 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L157-L158

Added lines #L157 - L158 were not covered by tests
mode, f_and_df, x, annotated_contexts...; chunk=Val(B), shadows=prep.shadows
)
return val, first(derivs)

Check warning on line 161 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L161

Added line #L161 was not covered by tests
end

function DI.gradient!(
Expand All @@ -156,8 +167,9 @@
prep::EnzymeForwardGradientPrep{B},
backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}},
x,
) where {F,B}
return copyto!(grad, DI.gradient(f, prep, backend, x))
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
return copyto!(grad, DI.gradient(f, prep, backend, x, contexts...))

Check warning on line 172 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L172

Added line #L172 was not covered by tests
end

function DI.value_and_gradient!(
Expand All @@ -166,8 +178,9 @@
prep::EnzymeForwardGradientPrep{B},
backend::AutoEnzyme{<:ForwardMode,<:Union{Nothing,Const}},
x,
) where {F,B}
y, new_grad = DI.value_and_gradient(f, prep, backend, x)
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
y, new_grad = DI.value_and_gradient(f, prep, backend, x, contexts...)

Check warning on line 183 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L183

Added line #L183 was not covered by tests
return y, copyto!(grad, new_grad)
end

Expand All @@ -185,9 +198,12 @@
end

function DI.prepare_jacobian(
f::F, backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}}, x
) where {F}
y = f(x)
f::F,
backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}},
x,
contexts::Vararg{DI.Constant,C},
) where {F,C}
y = f(x, map(DI.unwrap, contexts)...)

Check warning on line 206 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L206

Added line #L206 was not covered by tests
valB = to_val(DI.pick_batchsize(backend, x))
shadows = create_shadows(valB, x)
return EnzymeForwardOneArgJacobianPrep(valB, shadows, length(y))
Expand All @@ -198,11 +214,15 @@
prep::EnzymeForwardOneArgJacobianPrep{B},
backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}},
x,
) where {F,B}
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
mode = forward_noprimal(backend)
f_and_df = get_f_and_df(f, backend, mode)
derivs = jacobian(mode, f_and_df, x; chunk=Val(B), shadows=prep.shadows)
jac_tensor = only(derivs)
annotated_contexts = translate(backend, mode, Val(B), contexts...)
derivs = jacobian(

Check warning on line 222 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L221-L222

Added lines #L221 - L222 were not covered by tests
mode, f_and_df, x, annotated_contexts...; chunk=Val(B), shadows=prep.shadows
)
jac_tensor = first(derivs)

Check warning on line 225 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L225

Added line #L225 was not covered by tests
return maybe_reshape(jac_tensor, prep.output_length, length(x))
end

Expand All @@ -211,11 +231,15 @@
prep::EnzymeForwardOneArgJacobianPrep{B},
backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}},
x,
) where {F,B}
contexts::Vararg{DI.Constant,C},
) where {F,B,C}
mode = forward_withprimal(backend)
f_and_df = get_f_and_df(f, backend, mode)
(; derivs, val) = jacobian(mode, f_and_df, x; chunk=Val(B), shadows=prep.shadows)
jac_tensor = only(derivs)
annotated_contexts = translate(backend, mode, Val(B), contexts...)
(; derivs, val) = jacobian(

Check warning on line 239 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L238-L239

Added lines #L238 - L239 were not covered by tests
mode, f_and_df, x, annotated_contexts...; chunk=Val(B), shadows=prep.shadows
)
jac_tensor = first(derivs)

Check warning on line 242 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L242

Added line #L242 was not covered by tests
return val, maybe_reshape(jac_tensor, prep.output_length, length(x))
end

Expand All @@ -225,8 +249,9 @@
prep::EnzymeForwardOneArgJacobianPrep,
backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}},
x,
) where {F}
return copyto!(jac, DI.jacobian(f, prep, backend, x))
contexts::Vararg{DI.Constant,C},
) where {F,C}
return copyto!(jac, DI.jacobian(f, prep, backend, x, contexts...))

Check warning on line 254 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L254

Added line #L254 was not covered by tests
end

function DI.value_and_jacobian!(
Expand All @@ -235,7 +260,8 @@
prep::EnzymeForwardOneArgJacobianPrep,
backend::AutoEnzyme{<:Union{ForwardMode,Nothing},<:Union{Nothing,Const}},
x,
) where {F}
y, new_jac = DI.value_and_jacobian(f, prep, backend, x)
contexts::Vararg{DI.Constant,C},
) where {F,C}
y, new_jac = DI.value_and_jacobian(f, prep, backend, x, contexts...)

Check warning on line 265 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/forward_onearg.jl#L265

Added line #L265 was not covered by tests
return y, copyto!(jac, new_jac)
end
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@
return Const(DI.unwrap(c))
end

@inline function _translate(

Check warning on line 56 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl#L56

Added line #L56 was not covered by tests
backend::AutoEnzyme, mode::Mode, ::Val{B}, c::DI.Cache
) where {B}
if B == 1
return Duplicated(DI.unwrap(c), make_zero(DI.unwrap(c)))

Check warning on line 60 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl#L59-L60

Added lines #L59 - L60 were not covered by tests
else
return BatchDuplicated(DI.unwrap(c), ntuple(_ -> make_zero(DI.unwrap(c)), Val(B)))

Check warning on line 62 in DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl

View check run for this annotation

Codecov / codecov/patch

DifferentiationInterface/ext/DifferentiationInterfaceEnzymeExt/utils.jl#L62

Added line #L62 was not covered by tests
end
end

@inline function _translate(
backend::AutoEnzyme, mode::Mode, ::Val{B}, c::DI.FunctionContext
) where {B}
Expand Down
7 changes: 7 additions & 0 deletions DifferentiationInterface/test/Back/Enzyme/test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ end;
logging=LOGGING,
)

test_differentiation(
backends[2],
default_scenarios(; include_normal=false, include_cachified=true);
excluded=SECOND_ORDER,
logging=LOGGING,
)

test_differentiation(
duplicated_backends,
default_scenarios(; include_normal=false, include_closurified=true);
Expand Down