Skip to content
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
15 changes: 15 additions & 0 deletions ext/TensorKitEnzymeExt/TensorKitEnzymeExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ using MatrixAlgebraKit
using TupleTools
using Random: AbstractRNG

"""
is_inactive(config, x::Annotation) -> Bool

Check whether the annotated argument `x` should be treated as inactive, i.e. whether no tangent should be read from or accumulated into `x.dval`.
Under runtime activity, Enzyme may pass an argument that is inactive at run time as a `Duplicated` whose shadow aliases the primal (`x.dval === x.val`);
writing into that shadow would then corrupt the primal.
See https://enzymead.github.io/Enzyme.jl/dev/faq/#faq-runtime-activity.

!!! note
This is a stopgap until an equivalent helper is available in `EnzymeRules`
(see https://github.com/EnzymeAD/Enzyme.jl/pull/3597#discussion_r4014313289).
"""
@inline is_inactive(config, ::Const) = true
@inline is_inactive(config, x::Annotation) = EnzymeRules.runtime_activity(config) && x.dval === x.val

include("utility.jl")
include("linalg.jl")
include("indexmanipulations.jl")
Expand Down
12 changes: 6 additions & 6 deletions ext/TensorKitEnzymeExt/tensoroperations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ function EnzymeRules.augmented_primal(
backend::Const,
allocator::Const
) where {RT}
Ccache = isa(β, Const) ? nothing : copy(C.val)
A_needs_cache = EnzymeRules.overwritten(config)[3] && !(typeof(B) <: Const) && !(typeof(C) <: Const)
Ccache = isa(β, Const) || is_inactive(config, C) ? nothing : copy(C.val)
A_needs_cache = EnzymeRules.overwritten(config)[3] && !(typeof(B) <: Const) && !(typeof(C) <: Const) && !is_inactive(config, A)
Acache = A_needs_cache ? copy(A.val) : nothing
B_needs_cache = EnzymeRules.overwritten(config)[5] && !(typeof(A) <: Const) && !(typeof(C) <: Const)
B_needs_cache = EnzymeRules.overwritten(config)[5] && !(typeof(A) <: Const) && !(typeof(C) <: Const) && !is_inactive(config, B)
Bcache = B_needs_cache ? copy(B.val) : nothing
AB = if !isa(α, Const)
AB = TO.tensorcontract(A.val, pA.val, false, B.val, pB.val, false, pAB.val, One(), backend.val, allocator.val)
Expand Down Expand Up @@ -65,17 +65,17 @@ function EnzymeRules.reverse(
Δα = pullback_dα(α, C, AB)
Δβ = pullback_dβ(β, C, Cval)

if !isa(A, Const)
if !is_inactive(config, A) && !is_inactive(config, C)
TensorKit.blas_contract_pullback_ΔA!(
A.dval, C.dval, Aval, pA.val, Bval, pB.val, pAB.val, α.val, backend.val, allocator.val
) # this typically returns nothing
end
if !isa(B, Const)
if !is_inactive(config, B) && !is_inactive(config, C)
TensorKit.blas_contract_pullback_ΔB!(
B.dval, C.dval, Aval, pA.val, Bval, pB.val, pAB.val, α.val, backend.val, allocator.val
) # this typically returns nothing
end
!isa(C, Const) && pullback_dC!(C.dval, β.val) # this typically returns nothing
!is_inactive(config, C) && pullback_dC!(C.dval, β.val) # this typically returns nothing
return nothing, nothing, nothing, nothing, nothing, nothing, Δα, Δβ, nothing, nothing
end

Expand Down
Loading