From b0c18cf45f7cf691ee7c6159538403c9e25a894a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:47:34 +0000 Subject: [PATCH 1/5] Initial plan From eebb0712986bbbad6cb45f93e71242f32d30b07d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:56:38 +0000 Subject: [PATCH 2/5] Implement AutoDI wrapper type with tests Co-authored-by: wsmoses <1260124+wsmoses@users.noreply.github.com> --- src/ADTypes.jl | 5 ++++ src/di.jl | 49 ++++++++++++++++++++++++++++++++++++ test/di.jl | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 4 +++ 4 files changed, 123 insertions(+) create mode 100644 src/di.jl create mode 100644 test/di.jl diff --git a/src/ADTypes.jl b/src/ADTypes.jl index 609087e..9f44c51 100644 --- a/src/ADTypes.jl +++ b/src/ADTypes.jl @@ -23,6 +23,7 @@ include("compat.jl") # @public macro include("mode.jl") include("dense.jl") include("sparse.jl") +include("di.jl") include("legacy.jl") include("symbols.jl") @@ -58,6 +59,10 @@ export AutoChainRules, export AutoSparse @public dense_ad +# DI Automatic Differentiation +export AutoDI +@public inner_ad + # Sparsity detection export AbstractSparsityDetector export jacobian_sparsity, hessian_sparsity diff --git a/src/di.jl b/src/di.jl new file mode 100644 index 0000000..7c601be --- /dev/null +++ b/src/di.jl @@ -0,0 +1,49 @@ +""" + AutoDI{I<:AbstractADType} + +Wraps an ADTypes.jl object to signify that the DifferentiationInterface (DI) implementation of the wrapper function for that AD type should be used, rather than calling the native backend directly. + +This allows packages to distinguish between an intention to directly call a corresponding AD tool vs. the DI wrapper for said tool, enabling the ability to use, test, and validate both approaches. + +# Fields + + - `inner_ad::I`: the underlying AD package, subtyping [`AbstractADType`](@ref) + +# Constructors + + AutoDI(inner_ad) + +# Example + +```jldoctest +julia> using ADTypes + +julia> ad = AutoDI(AutoForwardDiff()) +AutoDI(AutoForwardDiff()) + +julia> inner_ad(ad) +AutoForwardDiff() +``` +""" +struct AutoDI{I <: AbstractADType} <: AbstractADType + inner_ad::I +end + +function Base.show(io::IO, backend::AutoDI) + print(io, AutoDI, "(", repr(backend.inner_ad, context = io), ")") +end + +""" + inner_ad(ad::AutoDI)::AbstractADType + inner_ad(ad::AbstractADType)::AbstractADType + +Return the underlying AD package for a DI AD choice, act as the identity on a non-DI AD choice. + +# See also + + - [`AutoDI`](@ref) +""" +inner_ad(ad::AutoDI) = ad.inner_ad +inner_ad(ad::AbstractADType) = ad + +mode(di_ad::AutoDI) = mode(inner_ad(di_ad)) diff --git a/test/di.jl b/test/di.jl new file mode 100644 index 0000000..11153cb --- /dev/null +++ b/test/di.jl @@ -0,0 +1,65 @@ +@testset "AutoDI" begin + @testset "Subtyping and wrapping $ad_name" for (ad_name, ad) in [ + ("AutoForwardDiff", AutoForwardDiff()), + ("AutoZygote", AutoZygote()), + ("AutoEnzyme", AutoEnzyme()), + ("AutoReverseDiff", AutoReverseDiff()), + ("AutoChainRules", AutoChainRules(; ruleconfig = ForwardOrReverseRuleConfig())), + ] + di_ad = AutoDI(ad) + @test di_ad isa AbstractADType + @test di_ad isa AutoDI + + # Test mode propagation + if mode(ad) isa ForwardMode + @test mode(di_ad) isa ForwardMode + elseif mode(ad) isa ForwardOrReverseMode + @test mode(di_ad) isa ForwardOrReverseMode + elseif mode(ad) isa ReverseMode + @test mode(di_ad) isa ReverseMode + elseif mode(ad) isa SymbolicMode + @test mode(di_ad) isa SymbolicMode + end + + # Test inner_ad accessor + @test inner_ad(ad) == ad + @test inner_ad(di_ad) == ad + end + + @testset "All AD backends" begin + for ad in every_ad() + di_ad = AutoDI(ad) + @test di_ad isa AbstractADType + @test inner_ad(di_ad) == ad + @test mode(di_ad) == mode(ad) + end + end + + @testset "Nested wrapping" begin + # Test that we can wrap AutoDI with AutoSparse and vice versa + ad = AutoForwardDiff() + di_ad = AutoDI(ad) + sparse_di_ad = AutoSparse(di_ad) + + @test sparse_di_ad isa AutoSparse + @test dense_ad(sparse_di_ad) isa AutoDI + @test inner_ad(dense_ad(sparse_di_ad)) == ad + + # Test AutoDI wrapping AutoSparse + sparse_ad = AutoSparse(ad) + di_sparse_ad = AutoDI(sparse_ad) + + @test di_sparse_ad isa AutoDI + @test inner_ad(di_sparse_ad) isa AutoSparse + @test dense_ad(inner_ad(di_sparse_ad)) == ad + end + + @testset "Display" begin + ad = AutoForwardDiff(chunksize = 5) + di_ad = AutoDI(ad) + + str = sprint(show, di_ad) + @test occursin("AutoDI", str) + @test occursin("AutoForwardDiff", str) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index b4a5f5b..0711dbe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,6 +6,7 @@ using ADTypes: AbstractADType, ReverseMode, SymbolicMode using ADTypes: dense_ad, + inner_ad, NoSparsityDetector, KnownJacobianSparsityDetector, KnownHessianSparsityDetector, @@ -101,6 +102,9 @@ end @testset "Sparse" begin include("sparse.jl") end + @testset "DI" begin + include("di.jl") + end @testset "Symbols" begin include("symbols.jl") end From da9c12c46c9ac234468dbe746cda3773637eae4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:59:39 +0000 Subject: [PATCH 3/5] Address code review comments: improve documentation and fix whitespace Co-authored-by: wsmoses <1260124+wsmoses@users.noreply.github.com> --- src/di.jl | 4 ++-- test/di.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/di.jl b/src/di.jl index 7c601be..af4705a 100644 --- a/src/di.jl +++ b/src/di.jl @@ -1,7 +1,7 @@ """ AutoDI{I<:AbstractADType} -Wraps an ADTypes.jl object to signify that the DifferentiationInterface (DI) implementation of the wrapper function for that AD type should be used, rather than calling the native backend directly. +Wraps an AD type to signify that the DifferentiationInterface wrapper should be used instead of calling the backend directly. This allows packages to distinguish between an intention to directly call a corresponding AD tool vs. the DI wrapper for said tool, enabling the ability to use, test, and validate both approaches. @@ -37,7 +37,7 @@ end inner_ad(ad::AutoDI)::AbstractADType inner_ad(ad::AbstractADType)::AbstractADType -Return the underlying AD package for a DI AD choice, act as the identity on a non-DI AD choice. +Return the underlying AD package for a DI AD choice, acts as the identity on a non-DI AD choice. # See also diff --git a/test/di.jl b/test/di.jl index 11153cb..cec90d8 100644 --- a/test/di.jl +++ b/test/di.jl @@ -9,7 +9,7 @@ di_ad = AutoDI(ad) @test di_ad isa AbstractADType @test di_ad isa AutoDI - + # Test mode propagation if mode(ad) isa ForwardMode @test mode(di_ad) isa ForwardMode @@ -20,7 +20,7 @@ elseif mode(ad) isa SymbolicMode @test mode(di_ad) isa SymbolicMode end - + # Test inner_ad accessor @test inner_ad(ad) == ad @test inner_ad(di_ad) == ad From 51801209501ebb93193a130a69ceeaed6b4285be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 15:00:20 +0000 Subject: [PATCH 4/5] Move AutoDI definition into dense.jl and remove separate di.jl file Co-authored-by: wsmoses <1260124+wsmoses@users.noreply.github.com> --- src/ADTypes.jl | 1 - src/dense.jl | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/di.jl | 49 ------------------------------------------------- 3 files changed, 50 insertions(+), 50 deletions(-) delete mode 100644 src/di.jl diff --git a/src/ADTypes.jl b/src/ADTypes.jl index 9f44c51..f5b8500 100644 --- a/src/ADTypes.jl +++ b/src/ADTypes.jl @@ -23,7 +23,6 @@ include("compat.jl") # @public macro include("mode.jl") include("dense.jl") include("sparse.jl") -include("di.jl") include("legacy.jl") include("symbols.jl") diff --git a/src/dense.jl b/src/dense.jl index c75ceb9..7613b2d 100644 --- a/src/dense.jl +++ b/src/dense.jl @@ -586,3 +586,53 @@ NoAutoDiffSelectedError() = NoAutoDiffSelectedError("Automatic differentiation c function mode(::NoAutoDiff) throw(NoAutoDiffSelectedError()) end + +""" + AutoDI{I<:AbstractADType} + +Wraps an AD type to signify that the DifferentiationInterface wrapper should be used instead of calling the backend directly. + +This allows packages to distinguish between an intention to directly call a corresponding AD tool vs. the DI wrapper for said tool, enabling the ability to use, test, and validate both approaches. + +# Fields + + - `inner_ad::I`: the underlying AD package, subtyping [`AbstractADType`](@ref) + +# Constructors + + AutoDI(inner_ad) + +# Example + +```jldoctest +julia> using ADTypes + +julia> ad = AutoDI(AutoForwardDiff()) +AutoDI(AutoForwardDiff()) + +julia> inner_ad(ad) +AutoForwardDiff() +``` +""" +struct AutoDI{I <: AbstractADType} <: AbstractADType + inner_ad::I +end + +function Base.show(io::IO, backend::AutoDI) + print(io, AutoDI, "(", repr(backend.inner_ad, context = io), ")") +end + +""" + inner_ad(ad::AutoDI)::AbstractADType + inner_ad(ad::AbstractADType)::AbstractADType + +Return the underlying AD package for a DI AD choice, acts as the identity on a non-DI AD choice. + +# See also + + - [`AutoDI`](@ref) +""" +inner_ad(ad::AutoDI) = ad.inner_ad +inner_ad(ad::AbstractADType) = ad + +mode(di_ad::AutoDI) = mode(inner_ad(di_ad)) diff --git a/src/di.jl b/src/di.jl deleted file mode 100644 index af4705a..0000000 --- a/src/di.jl +++ /dev/null @@ -1,49 +0,0 @@ -""" - AutoDI{I<:AbstractADType} - -Wraps an AD type to signify that the DifferentiationInterface wrapper should be used instead of calling the backend directly. - -This allows packages to distinguish between an intention to directly call a corresponding AD tool vs. the DI wrapper for said tool, enabling the ability to use, test, and validate both approaches. - -# Fields - - - `inner_ad::I`: the underlying AD package, subtyping [`AbstractADType`](@ref) - -# Constructors - - AutoDI(inner_ad) - -# Example - -```jldoctest -julia> using ADTypes - -julia> ad = AutoDI(AutoForwardDiff()) -AutoDI(AutoForwardDiff()) - -julia> inner_ad(ad) -AutoForwardDiff() -``` -""" -struct AutoDI{I <: AbstractADType} <: AbstractADType - inner_ad::I -end - -function Base.show(io::IO, backend::AutoDI) - print(io, AutoDI, "(", repr(backend.inner_ad, context = io), ")") -end - -""" - inner_ad(ad::AutoDI)::AbstractADType - inner_ad(ad::AbstractADType)::AbstractADType - -Return the underlying AD package for a DI AD choice, acts as the identity on a non-DI AD choice. - -# See also - - - [`AutoDI`](@ref) -""" -inner_ad(ad::AutoDI) = ad.inner_ad -inner_ad(ad::AbstractADType) = ad - -mode(di_ad::AutoDI) = mode(inner_ad(di_ad)) From 540c6d50889ea593ef10179582f88025e00877fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:19:20 +0000 Subject: [PATCH 5/5] Fix merge conflict: update formatting to match Runic style from main branch Co-authored-by: wsmoses <1260124+wsmoses@users.noreply.github.com> --- src/ADTypes.jl | 40 ++++++++++++++++++++-------------------- src/dense.jl | 6 ++++-- test/runtests.jl | 38 +++++++++++++++++++------------------- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/ADTypes.jl b/src/ADTypes.jl index f5b8500..622c8c2 100644 --- a/src/ADTypes.jl +++ b/src/ADTypes.jl @@ -29,26 +29,26 @@ include("symbols.jl") # Automatic Differentiation export AbstractADType export AutoChainRules, - AutoDiffractor, - AutoEnzyme, - AutoFastDifferentiation, - AutoFiniteDiff, - AutoFiniteDifferences, - AutoForwardDiff, - AutoGTPSA, - AutoModelingToolkit, - AutoMooncake, - AutoMooncakeForward, - AutoPolyesterForwardDiff, - AutoReverseDiff, - AutoSymbolics, - AutoTapir, - AutoTaylorDiff, - AutoTracker, - AutoZygote, - NoAutoDiff, - NoAutoDiffSelectedError, - AutoReactant + AutoDiffractor, + AutoEnzyme, + AutoFastDifferentiation, + AutoFiniteDiff, + AutoFiniteDifferences, + AutoForwardDiff, + AutoGTPSA, + AutoModelingToolkit, + AutoMooncake, + AutoMooncakeForward, + AutoPolyesterForwardDiff, + AutoReverseDiff, + AutoSymbolics, + AutoTapir, + AutoTaylorDiff, + AutoTracker, + AutoZygote, + NoAutoDiff, + NoAutoDiffSelectedError, + AutoReactant @public AbstractMode @public ForwardMode, ReverseMode, ForwardOrReverseMode, SymbolicMode @public mode diff --git a/src/dense.jl b/src/dense.jl index 7613b2d..5400f6c 100644 --- a/src/dense.jl +++ b/src/dense.jl @@ -68,7 +68,8 @@ struct AutoEnzyme{M, A} <: AbstractADType end function AutoEnzyme(; - mode::M = nothing, function_annotation::Type{A} = Nothing) where {M, A} + mode::M = nothing, function_annotation::Type{A} = Nothing + ) where {M, A} return AutoEnzyme{M, A}(mode) end @@ -106,7 +107,8 @@ struct AutoReactant{M<:AutoEnzyme} <: AbstractADType end function AutoReactant(; - mode::Union{AutoEnzyme,Nothing} = nothing) + mode::Union{AutoEnzyme, Nothing} = nothing + ) if mode === nothing mode = AutoEnzyme() end diff --git a/test/runtests.jl b/test/runtests.jl index 0711dbe..3fc3ab7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,27 +1,27 @@ using ADTypes using ADTypes: AbstractADType, - mode, - ForwardMode, - ForwardOrReverseMode, - ReverseMode, - SymbolicMode + mode, + ForwardMode, + ForwardOrReverseMode, + ReverseMode, + SymbolicMode using ADTypes: dense_ad, - inner_ad, - NoSparsityDetector, - KnownJacobianSparsityDetector, - KnownHessianSparsityDetector, - sparsity_detector, - jacobian_sparsity, - hessian_sparsity, - NoColoringAlgorithm, - coloring_algorithm, - column_coloring, - row_coloring, - symmetric_coloring + inner_ad, + NoSparsityDetector, + KnownJacobianSparsityDetector, + KnownHessianSparsityDetector, + sparsity_detector, + jacobian_sparsity, + hessian_sparsity, + NoColoringAlgorithm, + coloring_algorithm, + column_coloring, + row_coloring, + symmetric_coloring using Aqua: Aqua using ChainRulesCore: ChainRulesCore, RuleConfig, - HasForwardsMode, HasReverseMode, - NoForwardsMode, NoReverseMode + HasForwardsMode, HasReverseMode, + NoForwardsMode, NoReverseMode using EnzymeCore: EnzymeCore using JET: JET using Test