From 009dce884f5b971f5c8b3e81457d175d85e5a9a2 Mon Sep 17 00:00:00 2001 From: David Widmann Date: Mon, 7 Apr 2025 22:46:36 +0200 Subject: [PATCH 1/3] Fix seeding with undefined elements --- src/apiutils.jl | 56 +++++++++++++++++++++++++++++++++++++------- test/JacobianTest.jl | 14 +++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/apiutils.jl b/src/apiutils.jl index 39608d6c..f401a3fc 100644 --- a/src/apiutils.jl +++ b/src/apiutils.jl @@ -72,16 +72,36 @@ end function seed!(duals::AbstractArray{Dual{T,V,N}}, x, seed::Partials{N,V} = zero(Partials{N,V})) where {T,V,N} - for idx in structural_eachindex(duals, x) - duals[idx] = Dual{T,V,N}(x[idx], seed) + 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) + duals[idx] = Dual{T,V,N}(x[idx], seed) + else + Base._unsetindex!(duals, idx) + end + end end return duals end function seed!(duals::AbstractArray{Dual{T,V,N}}, x, seeds::NTuple{N,Partials{N,V}}) where {T,V,N} - for (i, idx) in zip(1:N, structural_eachindex(duals, x)) - duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + 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) + duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + else + Base._unsetindex!(duals, idx) + end + end end return duals end @@ -90,8 +110,18 @@ 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) - for idx in idxs - duals[idx] = Dual{T,V,N}(x[idx], seed) + 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) + duals[idx] = Dual{T,V,N}(x[idx], seed) + else + Base._unsetindex!(duals, idx) + end + end end return duals end @@ -100,8 +130,18 @@ 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) - for (i, idx) in zip(1:chunksize, idxs) - duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + 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) + duals[idx] = Dual{T,V,N}(x[idx], seeds[i]) + else + Base._unsetindex!(duals, idx) + end + end end return duals end diff --git a/test/JacobianTest.jl b/test/JacobianTest.jl index 1e52f7fa..76fa93ed 100644 --- a/test/JacobianTest.jl +++ b/test/JacobianTest.jl @@ -279,4 +279,18 @@ end end end +@testset "BigFloat" begin + # issues #436, #740 + for n in (2, 20) + x = BigFloat.(1:n) + @test x isa Vector{BigFloat} + y = similar(x) + @test !isassigned(y, 1) + res = ForwardDiff.jacobian(copyto!, y, x) + @test y == x + @test res isa Matrix{BigFloat} + @test res == I + end +end + end # module From ec3e483747159b46e0db5328c60c3543c13478a6 Mon Sep 17 00:00:00 2001 From: David Widmann Date: Mon, 25 Aug 2025 21:13:10 +0200 Subject: [PATCH 2/3] Extend tests to cover all branches --- test/JacobianTest.jl | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/test/JacobianTest.jl b/test/JacobianTest.jl index 76fa93ed..b010078f 100644 --- a/test/JacobianTest.jl +++ b/test/JacobianTest.jl @@ -279,18 +279,33 @@ end end end +# issues #436, #740 @testset "BigFloat" begin - # issues #436, #740 - for n in (2, 20) - x = BigFloat.(1:n) - @test x isa Vector{BigFloat} + # Unassigned entries in the output + x = BigFloat.(1:9) + for chunksize in (1, 2, 9) y = similar(x) - @test !isassigned(y, 1) - res = ForwardDiff.jacobian(copyto!, y, x) + @test all(i -> !isassigned(y, i), eachindex(y)) + cfg = ForwardDiff.JacobianConfig(copyto!, y, x, ForwardDiff.Chunk{chunksize}()) + res = ForwardDiff.jacobian(copyto!, y, x, cfg) @test y == x @test res isa Matrix{BigFloat} @test res == I end + + # Unassigned (but unused) entry in the input and unassigned entries in the output + resize!(x, 10) + f = (y, x) -> copyto!(y, 1, x, 1, 9) + for chunksize in (1, 2, 10) + y = similar(x, 9) + @test all(i -> !isassigned(y, i), eachindex(y)) + cfg = ForwardDiff.JacobianConfig(f, y, x, ForwardDiff.Chunk{chunksize}()) + res = ForwardDiff.jacobian(f, y, x, cfg) + @test y == x[1:(end-1)] + @test res isa Matrix{BigFloat} + @test res[:, 1:(end-1)] == I + @test all(iszero, res[:, end]) + end end end # module From 0fe979769e66c5fec340fa7db9a3d5615f4a2876 Mon Sep 17 00:00:00 2001 From: David Widmann Date: Mon, 25 Aug 2025 21:22:42 +0200 Subject: [PATCH 3/3] Bump version to 1.0.2 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9eb2914c..2712d857 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ForwardDiff" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "1.0.1" +version = "1.0.2" [deps] CommonSubexpressions = "bbf7d656-a473-5ed7-a52c-81e309532950"