From 9cbe0e8e19c82e6e4e4ae227a1446c39eda34da7 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Wed, 13 Aug 2025 19:11:24 -0400 Subject: [PATCH 1/5] fix: regression in non-fast scalar indexing support fix: project toml for julia pre 1.9 fix: support gradient + more test coverage chore: relax version chore: remove 1.6 support and bump min version to 1.10 fix: apply suggestions from code review Co-authored-by: David Widmann fix: use a struct instead of closure fix: sizecheck chore: remove GPUArraysCore Co-authored-by: David Widmann fix: revert _take chore: remove 1.8 checks chore: remove 0.1 Co-authored-by: David Widmann --- Project.toml | 8 +++- ext/ForwardDiffGPUArraysCoreExt.jl | 65 ++++++++++++++++++++++++++++++ src/dual.jl | 2 - test/DualTest.jl | 1 - test/GradientTest.jl | 22 ++++++++++ test/JacobianTest.jl | 14 +++++++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 ext/ForwardDiffGPUArraysCoreExt.jl diff --git a/Project.toml b/Project.toml index 24885b11..984fcd84 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ForwardDiff" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "1.2.0" +version = "1.2.1" [deps] CommonSubexpressions = "bbf7d656-a473-5ed7-a52c-81e309532950" @@ -15,9 +15,11 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" [weakdeps] +GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [extensions] +ForwardDiffGPUArraysCoreExt = "GPUArraysCore" ForwardDiffStaticArraysExt = "StaticArrays" [compat] @@ -26,6 +28,7 @@ CommonSubexpressions = "0.3" DiffResults = "1.1" DiffRules = "1.4" DiffTests = "0.1" +GPUArraysCore = "0.2" IrrationalConstants = "0.1, 0.2" LogExpFunctions = "0.3" NaNMath = "1" @@ -39,9 +42,10 @@ Calculus = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" DiffTests = "de460e47-3fe3-5279-bb4a-814414816d5d" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" +JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Calculus", "DiffTests", "IrrationalConstants", "SparseArrays", "StaticArrays", "Test", "InteractiveUtils"] +test = ["Calculus", "DiffTests", "IrrationalConstants", "SparseArrays", "StaticArrays", "Test", "InteractiveUtils", "JLArrays"] diff --git a/ext/ForwardDiffGPUArraysCoreExt.jl b/ext/ForwardDiffGPUArraysCoreExt.jl new file mode 100644 index 00000000..a881dc4d --- /dev/null +++ b/ext/ForwardDiffGPUArraysCoreExt.jl @@ -0,0 +1,65 @@ +module ForwardDiffGPUArraysCoreExt + +using GPUArraysCore: AbstractGPUArray +using ForwardDiff: ForwardDiff, Dual, Partials, npartials, partials + +struct PartialsFn{T,D<:Dual} + dual::D +end +PartialsFn{T}(dual::Dual) where {T} = PartialsFn{T,typeof(dual)}(dual) + +(f::PartialsFn{T})(i) where {T} = partials(T, f.dual, i) + +function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, + seed::Partials{N,V}) where {T,V,N} + idxs = collect(ForwardDiff.structural_eachindex(duals, x)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) + return duals +end + +function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, + seeds::NTuple{N,Partials{N,V}}) where {T,V,N} + idxs = collect(Iterators.take(ForwardDiff.structural_eachindex(duals, x), N)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) + return duals +end + +function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, index, + seed::Partials{N,V}) where {T,V,N} + offset = index - 1 + idxs = collect(Iterators.drop(ForwardDiff.structural_eachindex(duals, x), offset)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) + return duals +end + +function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, index, + seeds::NTuple{N,Partials{N,V}}, chunksize) where {T,V,N} + offset = index - 1 + idxs = collect( + Iterators.take(Iterators.drop(ForwardDiff.structural_eachindex(duals, x), offset), chunksize) + ) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) + return duals +end + +# gradient +function ForwardDiff.extract_gradient!(::Type{T}, result::AbstractGPUArray, + dual::Dual) where {T} + fn = PartialsFn{T}(dual) + idxs = collect(Iterators.take(ForwardDiff.structural_eachindex(result), npartials(dual))) + result[idxs] .= fn.(1:length(idxs)) + return result +end + +function ForwardDiff.extract_gradient_chunk!(::Type{T}, result::AbstractGPUArray, dual, + index, chunksize) where {T} + fn = PartialsFn{T}(dual) + offset = index - 1 + idxs = collect( + Iterators.take(Iterators.drop(ForwardDiff.structural_eachindex(result), offset), chunksize) + ) + result[idxs] .= fn.(1:length(idxs)) + return result +end + +end diff --git a/src/dual.jl b/src/dual.jl index 69a36760..66c20924 100644 --- a/src/dual.jl +++ b/src/dual.jl @@ -298,8 +298,6 @@ Base.copy(d::Dual) = d Base.eps(d::Dual) = eps(value(d)) Base.eps(::Type{D}) where {D<:Dual} = eps(valtype(D)) -# The `base` keyword was added in Julia 1.8: -# https://github.com/JuliaLang/julia/pull/42428 Base.precision(d::Dual; base::Integer=2) = precision(value(d); base=base) function Base.precision(::Type{D}; base::Integer=2) where {D<:Dual} precision(valtype(D); base=base) diff --git a/test/DualTest.jl b/test/DualTest.jl index b64094ea..fcd52ea7 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -118,7 +118,6 @@ ForwardDiff.:≺(::Type{OuterTestTag}, ::Type{TestTag}) = false @test precision(typeof(FDNUM)) === precision(V) @test precision(NESTED_FDNUM) === precision(PRIMAL) @test precision(typeof(NESTED_FDNUM)) === precision(V) - @test precision(FDNUM; base=10) === precision(PRIMAL; base=10) @test precision(typeof(FDNUM); base=10) === precision(V; base=10) @test precision(NESTED_FDNUM; base=10) === precision(PRIMAL; base=10) diff --git a/test/GradientTest.jl b/test/GradientTest.jl index 4f46c167..5c2c0938 100644 --- a/test/GradientTest.jl +++ b/test/GradientTest.jl @@ -9,6 +9,7 @@ using ForwardDiff using ForwardDiff: Dual, Tag using StaticArrays using DiffTests +using JLArrays include(joinpath(dirname(@__FILE__), "utils.jl")) @@ -255,4 +256,25 @@ end end end +@testset "GPUArraysCore" begin + fn(x) = sum(x .^ 2 ./ 2) + + x = [1.0, 2.0, 3.0] + x_jl = JLArray(x) + + grad = ForwardDiff.gradient(fn, x) + grad_jl = ForwardDiff.gradient(fn, x_jl) + + @test grad_jl isa JLArray + @test Array(grad_jl) ≈ grad + + cfg = ForwardDiff.GradientConfig( + fn, x_jl, ForwardDiff.Chunk{2}(), ForwardDiff.Tag(fn, eltype(x)) + ) + grad_jl = ForwardDiff.gradient(fn, x_jl, cfg) + + @test grad_jl isa JLArray + @test Array(grad_jl) ≈ grad +end + end # module diff --git a/test/JacobianTest.jl b/test/JacobianTest.jl index b010078f..689c61dc 100644 --- a/test/JacobianTest.jl +++ b/test/JacobianTest.jl @@ -8,6 +8,7 @@ using ForwardDiff: Dual, Tag, JacobianConfig using StaticArrays using DiffTests using LinearAlgebra +using JLArrays include(joinpath(dirname(@__FILE__), "utils.jl")) @@ -308,4 +309,17 @@ end end end +@testset "GPUArraysCore" begin + f(x) = x .^ 2 ./ 2 + + x = [1.0, 2.0, 3.0] + x_jl = JLArray(x) + + jac = ForwardDiff.jacobian(f, x) + jac_jl = ForwardDiff.jacobian(f, x_jl) + + @test jac_jl isa JLArray + @test Array(jac_jl) ≈ jac +end + end # module From 095a9d195b582b3f1e7ada7d84569be943fec842 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Mon, 1 Sep 2025 16:27:36 -0400 Subject: [PATCH 2/5] feat: support wrapper types as well --- Project.toml | 3 - ext/ForwardDiffGPUArraysCoreExt.jl | 65 --------------------- src/ForwardDiff.jl | 1 + src/apiutils.jl | 92 ++++++++++++++++++------------ src/gradient.jl | 24 ++++++-- src/utils.jl | 15 +++++ test/DualTest.jl | 1 + test/GradientTest.jl | 5 ++ 8 files changed, 96 insertions(+), 110 deletions(-) delete mode 100644 ext/ForwardDiffGPUArraysCoreExt.jl create mode 100644 src/utils.jl diff --git a/Project.toml b/Project.toml index 984fcd84..c162d595 100644 --- a/Project.toml +++ b/Project.toml @@ -15,11 +15,9 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" [weakdeps] -GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [extensions] -ForwardDiffGPUArraysCoreExt = "GPUArraysCore" ForwardDiffStaticArraysExt = "StaticArrays" [compat] @@ -28,7 +26,6 @@ CommonSubexpressions = "0.3" DiffResults = "1.1" DiffRules = "1.4" DiffTests = "0.1" -GPUArraysCore = "0.2" IrrationalConstants = "0.1, 0.2" LogExpFunctions = "0.3" NaNMath = "1" diff --git a/ext/ForwardDiffGPUArraysCoreExt.jl b/ext/ForwardDiffGPUArraysCoreExt.jl deleted file mode 100644 index a881dc4d..00000000 --- a/ext/ForwardDiffGPUArraysCoreExt.jl +++ /dev/null @@ -1,65 +0,0 @@ -module ForwardDiffGPUArraysCoreExt - -using GPUArraysCore: AbstractGPUArray -using ForwardDiff: ForwardDiff, Dual, Partials, npartials, partials - -struct PartialsFn{T,D<:Dual} - dual::D -end -PartialsFn{T}(dual::Dual) where {T} = PartialsFn{T,typeof(dual)}(dual) - -(f::PartialsFn{T})(i) where {T} = partials(T, f.dual, i) - -function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, - seed::Partials{N,V}) where {T,V,N} - idxs = collect(ForwardDiff.structural_eachindex(duals, x)) - duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) - return duals -end - -function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, - seeds::NTuple{N,Partials{N,V}}) where {T,V,N} - idxs = collect(Iterators.take(ForwardDiff.structural_eachindex(duals, x), N)) - duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) - return duals -end - -function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, index, - seed::Partials{N,V}) where {T,V,N} - offset = index - 1 - idxs = collect(Iterators.drop(ForwardDiff.structural_eachindex(duals, x), offset)) - duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) - return duals -end - -function ForwardDiff.seed!(duals::AbstractGPUArray{Dual{T,V,N}}, x, index, - seeds::NTuple{N,Partials{N,V}}, chunksize) where {T,V,N} - offset = index - 1 - idxs = collect( - Iterators.take(Iterators.drop(ForwardDiff.structural_eachindex(duals, x), offset), chunksize) - ) - duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) - return duals -end - -# gradient -function ForwardDiff.extract_gradient!(::Type{T}, result::AbstractGPUArray, - dual::Dual) where {T} - fn = PartialsFn{T}(dual) - idxs = collect(Iterators.take(ForwardDiff.structural_eachindex(result), npartials(dual))) - result[idxs] .= fn.(1:length(idxs)) - return result -end - -function ForwardDiff.extract_gradient_chunk!(::Type{T}, result::AbstractGPUArray, dual, - index, chunksize) where {T} - fn = PartialsFn{T}(dual) - offset = index - 1 - idxs = collect( - Iterators.take(Iterators.drop(ForwardDiff.structural_eachindex(result), offset), chunksize) - ) - result[idxs] .= fn.(1:length(idxs)) - return result -end - -end diff --git a/src/ForwardDiff.jl b/src/ForwardDiff.jl index b16b986b..dc6ab0e6 100644 --- a/src/ForwardDiff.jl +++ b/src/ForwardDiff.jl @@ -21,6 +21,7 @@ include("derivative.jl") include("gradient.jl") include("jacobian.jl") include("hessian.jl") +include("utils.jl") export DiffResults diff --git a/src/apiutils.jl b/src/apiutils.jl index f401a3fc..73c4a4ec 100644 --- a/src/apiutils.jl +++ b/src/apiutils.jl @@ -72,36 +72,46 @@ end function seed!(duals::AbstractArray{Dual{T,V,N}}, x, seed::Partials{N,V} = zero(Partials{N,V})) where {T,V,N} - if isbitstype(V) - for idx in structural_eachindex(duals, x) - duals[idx] = Dual{T,V,N}(x[idx], seed) - end - else - for idx in structural_eachindex(duals, x) - if isassigned(x, idx) + if supports_fast_scalar_indexing(duals) + if isbitstype(V) + for idx in structural_eachindex(duals, x) duals[idx] = Dual{T,V,N}(x[idx], seed) - else - Base._unsetindex!(duals, idx) + end + else + for idx in structural_eachindex(duals, x) + if isassigned(x, idx) + duals[idx] = Dual{T,V,N}(x[idx], seed) + else + Base._unsetindex!(duals, idx) + end end end + else + idxs = collect(structural_eachindex(duals, x)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) end return duals end function seed!(duals::AbstractArray{Dual{T,V,N}}, x, seeds::NTuple{N,Partials{N,V}}) where {T,V,N} - if isbitstype(V) - for (i, idx) in zip(1:N, structural_eachindex(duals, x)) - duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) - end - else - for (i, idx) in zip(1:N, structural_eachindex(duals, x)) - if isassigned(x, idx) + if supports_fast_scalar_indexing(duals) + if isbitstype(V) + for (i, idx) in zip(1:N, structural_eachindex(duals, x)) duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) - else - Base._unsetindex!(duals, idx) + end + else + for (i, idx) in zip(1:N, structural_eachindex(duals, x)) + if isassigned(x, idx) + duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + else + Base._unsetindex!(duals, idx) + end end end + else + idxs = collect(Iterators.take(structural_eachindex(duals, x), N)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) end return duals end @@ -110,18 +120,23 @@ function seed!(duals::AbstractArray{Dual{T,V,N}}, x, index, seed::Partials{N,V} = zero(Partials{N,V})) where {T,V,N} offset = index - 1 idxs = Iterators.drop(structural_eachindex(duals, x), offset) - if isbitstype(V) - for idx in idxs - duals[idx] = Dual{T,V,N}(x[idx], seed) - end - else - for idx in idxs - if isassigned(x, idx) + if supports_fast_scalar_indexing(duals) + if isbitstype(V) + for idx in idxs duals[idx] = Dual{T,V,N}(x[idx], seed) - else - Base._unsetindex!(duals, idx) + end + else + for idx in idxs + if isassigned(x, idx) + duals[idx] = Dual{T,V,N}(x[idx], seed) + else + Base._unsetindex!(duals, idx) + end end end + else + idxs = collect(idxs) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), Ref(seed)) end return duals end @@ -130,18 +145,23 @@ function seed!(duals::AbstractArray{Dual{T,V,N}}, x, index, seeds::NTuple{N,Partials{N,V}}, chunksize = N) where {T,V,N} offset = index - 1 idxs = Iterators.drop(structural_eachindex(duals, x), offset) - if isbitstype(V) - for (i, idx) in zip(1:chunksize, idxs) - duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) - end - else - for (i, idx) in zip(1:chunksize, idxs) - if isassigned(x, idx) + if supports_fast_scalar_indexing(duals) + if isbitstype(V) + for (i, idx) in zip(1:chunksize, idxs) duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) - else - Base._unsetindex!(duals, idx) + end + else + for (i, idx) in zip(1:chunksize, idxs) + if isassigned(x, idx) + duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + else + Base._unsetindex!(duals, idx) + end end end + else + idxs = collect(Iterators.take(idxs, chunksize)) + duals[idxs] .= Dual{T,V,N}.(view(x, idxs), getindex.(Ref(seeds), 1:length(idxs))) end return duals end diff --git a/src/gradient.jl b/src/gradient.jl index 98bdb091..90233ab9 100644 --- a/src/gradient.jl +++ b/src/gradient.jl @@ -65,17 +65,29 @@ end extract_gradient!(::Type{T}, result::AbstractArray, y::Real) where {T} = fill!(result, zero(y)) function extract_gradient!(::Type{T}, result::AbstractArray, dual::Dual) where {T} idxs = structural_eachindex(result) - for (i, idx) in zip(1:npartials(dual), idxs) - result[idx] = partials(T, dual, i) + if supports_fast_scalar_indexing(result) + for (i, idx) in zip(1:npartials(dual), idxs) + result[idx] = partials(T, dual, i) + end + else + fn = PartialsFn{T}(dual) + idxs = collect(Iterators.take(idxs, npartials(dual))) + result[idxs] .= fn.(1:length(idxs)) + return result end return result end function extract_gradient_chunk!(::Type{T}, result, dual, index, chunksize) where {T} - offset = index - 1 - idxs = Iterators.drop(structural_eachindex(result), offset) - for (i, idx) in zip(1:chunksize, idxs) - result[idx] = partials(T, dual, i) + idxs = Iterators.drop(structural_eachindex(result), index - 1) + if supports_fast_scalar_indexing(result) + for (i, idx) in zip(1:chunksize, idxs) + result[idx] = partials(T, dual, i) + end + else + fn = PartialsFn{T}(dual) + idxs = collect(Iterators.take(idxs, chunksize)) + result[idxs] .= fn.(1:length(idxs)) end return result end diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 00000000..b4e7298c --- /dev/null +++ b/src/utils.jl @@ -0,0 +1,15 @@ +# overload for array types that +@inline supports_fast_scalar_indexing(::Array) = true + +@inline function supports_fast_scalar_indexing(x::AbstractArray) + parent(x) === x && return false + return supports_fast_scalar_indexing(parent(x)) +end + +# Helper function for broadcasting +struct PartialsFn{T,D<:Dual} + dual::D +end +PartialsFn{T}(dual::Dual) where {T} = PartialsFn{T,typeof(dual)}(dual) + +(f::PartialsFn{T})(i) where {T} = partials(T, f.dual, i) diff --git a/test/DualTest.jl b/test/DualTest.jl index fcd52ea7..b64094ea 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -118,6 +118,7 @@ ForwardDiff.:≺(::Type{OuterTestTag}, ::Type{TestTag}) = false @test precision(typeof(FDNUM)) === precision(V) @test precision(NESTED_FDNUM) === precision(PRIMAL) @test precision(typeof(NESTED_FDNUM)) === precision(V) + @test precision(FDNUM; base=10) === precision(PRIMAL; base=10) @test precision(typeof(FDNUM); base=10) === precision(V; base=10) @test precision(NESTED_FDNUM; base=10) === precision(PRIMAL; base=10) diff --git a/test/GradientTest.jl b/test/GradientTest.jl index 5c2c0938..47f4f377 100644 --- a/test/GradientTest.jl +++ b/test/GradientTest.jl @@ -277,4 +277,9 @@ end @test Array(grad_jl) ≈ grad end +@testset "Scalar Indexing Checks" begin + @test ForwardDiff.supports_fast_scalar_indexing(UnitLowerTriangular(view(rand(6, 6), 1:3, 1:3))) + @test !ForwardDiff.supports_fast_scalar_indexing(UnitLowerTriangular(view(JLArray(rand(6, 6)), 1:3, 1:3))) +end + end # module From 0ed3a8259082ba5664b0084efb81c703093534f3 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Mon, 1 Sep 2025 16:56:50 -0400 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: David Widmann --- src/utils.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index b4e7298c..1f19a3fe 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,9 +1,8 @@ # overload for array types that -@inline supports_fast_scalar_indexing(::Array) = true +@inline supports_fast_scalar_indexing(::StridedArray) = true @inline function supports_fast_scalar_indexing(x::AbstractArray) - parent(x) === x && return false - return supports_fast_scalar_indexing(parent(x)) + return parent(x) !== x && supports_fast_scalar_indexing(parent(x)) end # Helper function for broadcasting From cbc1661a1d1ba4ae4aada79054529faa36e5c32c Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Mon, 1 Sep 2025 17:06:30 -0400 Subject: [PATCH 4/5] fix: revert StridedArray change --- ext/ForwardDiffStaticArraysExt.jl | 2 ++ src/utils.jl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/ForwardDiffStaticArraysExt.jl b/ext/ForwardDiffStaticArraysExt.jl index e97da9d5..6f9372e7 100644 --- a/ext/ForwardDiffStaticArraysExt.jl +++ b/ext/ForwardDiffStaticArraysExt.jl @@ -10,6 +10,8 @@ using ForwardDiff: Dual, partials, npartials, Partials, GradientConfig, Jacobian vector_mode_jacobian, vector_mode_jacobian!, valtype, value using DiffResults: DiffResult, ImmutableDiffResult, MutableDiffResult +@inline ForwardDiff.supports_fast_scalar_indexing(::StaticArray) = true + @generated function dualize(::Type{T}, x::StaticArray) where T N = length(x) dx = Expr(:tuple, [:(Dual{T}(x[$i], chunk, Val{$i}())) for i in 1:N]...) diff --git a/src/utils.jl b/src/utils.jl index 1f19a3fe..478d9731 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,5 +1,5 @@ # overload for array types that -@inline supports_fast_scalar_indexing(::StridedArray) = true +@inline supports_fast_scalar_indexing(::Array) = true @inline function supports_fast_scalar_indexing(x::AbstractArray) return parent(x) !== x && supports_fast_scalar_indexing(parent(x)) From 2c9732389d9d736929bb3f5149e3ad9a4757164a Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Mon, 1 Sep 2025 17:07:00 -0400 Subject: [PATCH 5/5] fix: remove inline --- ext/ForwardDiffStaticArraysExt.jl | 2 +- src/utils.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/ForwardDiffStaticArraysExt.jl b/ext/ForwardDiffStaticArraysExt.jl index 6f9372e7..e0319714 100644 --- a/ext/ForwardDiffStaticArraysExt.jl +++ b/ext/ForwardDiffStaticArraysExt.jl @@ -10,7 +10,7 @@ using ForwardDiff: Dual, partials, npartials, Partials, GradientConfig, Jacobian vector_mode_jacobian, vector_mode_jacobian!, valtype, value using DiffResults: DiffResult, ImmutableDiffResult, MutableDiffResult -@inline ForwardDiff.supports_fast_scalar_indexing(::StaticArray) = true +ForwardDiff.supports_fast_scalar_indexing(::StaticArray) = true @generated function dualize(::Type{T}, x::StaticArray) where T N = length(x) diff --git a/src/utils.jl b/src/utils.jl index 478d9731..d4ae24cc 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,7 +1,7 @@ # overload for array types that -@inline supports_fast_scalar_indexing(::Array) = true +supports_fast_scalar_indexing(::Array) = true -@inline function supports_fast_scalar_indexing(x::AbstractArray) +function supports_fast_scalar_indexing(x::AbstractArray) return parent(x) !== x && supports_fast_scalar_indexing(parent(x)) end