From 094018e6a89b00edce54b9d15e8a28a3f67c74af Mon Sep 17 00:00:00 2001 From: Bart de Koning <74617371+SouthEndMusic@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:58:55 +0000 Subject: [PATCH 1/6] Fix by GPT-4.1 --- .../jacobian_mixed.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl index 463d5e173..6da405f3a 100644 --- a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl +++ b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl @@ -114,8 +114,9 @@ function _prepare_mixed_sparse_jacobian_aux_aux( seeds_forward = [DI.multibasis(x, eachindex(x)[group]) for group in groups_forward] seeds_reverse = [DI.multibasis(y, eachindex(y)[group]) for group in groups_reverse] - compressed_matrix_forward = stack(_ -> vec(similar(y)), groups_forward; dims=2) - compressed_matrix_reverse = stack(_ -> vec(similar(x)), groups_reverse; dims=1) + # If no groups, create a trivial compressed matrix of correct shape + compressed_matrix_forward = isempty(groups_forward) ? zeros(eltype(y), length(y), 0) : stack(_ -> vec(similar(y)), groups_forward; dims=2) + compressed_matrix_reverse = isempty(groups_reverse) ? zeros(eltype(x), 0, length(x)) : stack(_ -> vec(similar(x)), groups_reverse; dims=1) batched_seeds_forward = [ ntuple(b -> seeds_forward[1 + ((a - 1) * Bf + (b - 1)) % Nf], Val(Bf)) for a in 1:Af From a6fef915a9e51d9c59db417746dbe2ac115c6149 Mon Sep 17 00:00:00 2001 From: Bart de Koning <74617371+SouthEndMusic@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:11:14 +0000 Subject: [PATCH 2/6] Let GPT-4.1 add test --- .../src/scenarios/sparse.jl | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/DifferentiationInterfaceTest/src/scenarios/sparse.jl b/DifferentiationInterfaceTest/src/scenarios/sparse.jl index 3c914c053..b7add4906 100644 --- a/DifferentiationInterfaceTest/src/scenarios/sparse.jl +++ b/DifferentiationInterfaceTest/src/scenarios/sparse.jl @@ -1,3 +1,27 @@ +## Mixed-mode empty coloring scenario + +function mixedmode_empty_coloring_scenario() + sparsity_detector = TracerSparsityDetector() + function f!(y, x) + y .= x + end + backend = AutoSparse( + MixedMode(AutoForwardDiff(), AutoMooncake()); + sparsity_detector, + coloring_algorithm=GreedyColoringAlgorithm(; postprocessing=true) + ) + N = 50 + x = zeros(N) + y = zeros(N) + # No nontrivial Jacobian, but should not error + return Scenario{:jacobian,:in}( + f!, y, x; + prep_args=(; y=zero(y), x=zeros(N), contexts=()), + res1=zeros(N, N), + backend=backend, + name="mixedmode_empty_coloring" + ) +end ## Vector to vector diffsquare(x::AbstractVector)::AbstractVector = diff(x) .^ 2 @@ -397,6 +421,8 @@ function sparse_scenarios(; final_scens = Scenario[] append!(final_scens, scens) + # Add the mixedmode empty coloring scenario + push!(final_scens, mixedmode_empty_coloring_scenario()) include_constantified && append!(final_scens, constantify(scens)) include_cachified && append!(final_scens, cachify(scens; use_tuples)) include_constantorcachified && append!(final_scens, constantorcachify(scens)) From e500fbef042dac235bb9f2d12e670347049fa0a8 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Wed, 1 Oct 2025 22:20:02 +0200 Subject: [PATCH 3/6] Fix formatting --- .../jacobian_mixed.jl | 12 ++++++++++-- DifferentiationInterfaceTest/src/scenarios/sparse.jl | 8 +++++--- README.md | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl index 6da405f3a..139d71e80 100644 --- a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl +++ b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl @@ -115,8 +115,16 @@ function _prepare_mixed_sparse_jacobian_aux_aux( seeds_reverse = [DI.multibasis(y, eachindex(y)[group]) for group in groups_reverse] # If no groups, create a trivial compressed matrix of correct shape - compressed_matrix_forward = isempty(groups_forward) ? zeros(eltype(y), length(y), 0) : stack(_ -> vec(similar(y)), groups_forward; dims=2) - compressed_matrix_reverse = isempty(groups_reverse) ? zeros(eltype(x), 0, length(x)) : stack(_ -> vec(similar(x)), groups_reverse; dims=1) + compressed_matrix_forward = if isempty(groups_forward) + zeros(eltype(y), length(y), 0) + else + stack(_ -> vec(similar(y)), groups_forward; dims=2) + end + compressed_matrix_reverse = if isempty(groups_reverse) + zeros(eltype(x), 0, length(x)) + else + stack(_ -> vec(similar(x)), groups_reverse; dims=1) + end batched_seeds_forward = [ ntuple(b -> seeds_forward[1 + ((a - 1) * Bf + (b - 1)) % Nf], Val(Bf)) for a in 1:Af diff --git a/DifferentiationInterfaceTest/src/scenarios/sparse.jl b/DifferentiationInterfaceTest/src/scenarios/sparse.jl index b7add4906..7af90ed14 100644 --- a/DifferentiationInterfaceTest/src/scenarios/sparse.jl +++ b/DifferentiationInterfaceTest/src/scenarios/sparse.jl @@ -8,18 +8,20 @@ function mixedmode_empty_coloring_scenario() backend = AutoSparse( MixedMode(AutoForwardDiff(), AutoMooncake()); sparsity_detector, - coloring_algorithm=GreedyColoringAlgorithm(; postprocessing=true) + coloring_algorithm=GreedyColoringAlgorithm(; postprocessing=true), ) N = 50 x = zeros(N) y = zeros(N) # No nontrivial Jacobian, but should not error return Scenario{:jacobian,:in}( - f!, y, x; + f!, + y, + x; prep_args=(; y=zero(y), x=zeros(N), contexts=()), res1=zeros(N, N), backend=backend, - name="mixedmode_empty_coloring" + name="mixedmode_empty_coloring", ) end ## Vector to vector diff --git a/README.md b/README.md index 0d1b5bfdc..32c9bcf3b 120000 --- a/README.md +++ b/README.md @@ -1 +1 @@ -DifferentiationInterface/README.md \ No newline at end of file +DifferentiationInterface/README.md From 35621929738c4d63447d365474eb0383cdef9b3e Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Wed, 1 Oct 2025 22:23:04 +0200 Subject: [PATCH 4/6] Remove overly verbose comments --- DifferentiationInterfaceTest/src/scenarios/sparse.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/DifferentiationInterfaceTest/src/scenarios/sparse.jl b/DifferentiationInterfaceTest/src/scenarios/sparse.jl index 7af90ed14..99344ab7b 100644 --- a/DifferentiationInterfaceTest/src/scenarios/sparse.jl +++ b/DifferentiationInterfaceTest/src/scenarios/sparse.jl @@ -1,9 +1,7 @@ -## Mixed-mode empty coloring scenario - function mixedmode_empty_coloring_scenario() sparsity_detector = TracerSparsityDetector() function f!(y, x) - y .= x + return y .= x end backend = AutoSparse( MixedMode(AutoForwardDiff(), AutoMooncake()); @@ -13,7 +11,6 @@ function mixedmode_empty_coloring_scenario() N = 50 x = zeros(N) y = zeros(N) - # No nontrivial Jacobian, but should not error return Scenario{:jacobian,:in}( f!, y, @@ -423,7 +420,6 @@ function sparse_scenarios(; final_scens = Scenario[] append!(final_scens, scens) - # Add the mixedmode empty coloring scenario push!(final_scens, mixedmode_empty_coloring_scenario()) include_constantified && append!(final_scens, constantify(scens)) include_cachified && append!(final_scens, cachify(scens; use_tuples)) From 16b885b63d9396ba6bd57b4e2c0bfcbf477ec00a Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Fri, 3 Oct 2025 11:36:04 +0200 Subject: [PATCH 5/6] Improve fix --- .../jacobian_mixed.jl | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl index 139d71e80..f58682e55 100644 --- a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl +++ b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl @@ -111,41 +111,42 @@ function _prepare_mixed_sparse_jacobian_aux_aux( groups_forward = column_groups(coloring_result) groups_reverse = row_groups(coloring_result) - seeds_forward = [DI.multibasis(x, eachindex(x)[group]) for group in groups_forward] - seeds_reverse = [DI.multibasis(y, eachindex(y)[group]) for group in groups_reverse] - - # If no groups, create a trivial compressed matrix of correct shape - compressed_matrix_forward = if isempty(groups_forward) - zeros(eltype(y), length(y), 0) + # Handle forward direction + if isempty(groups_forward) + seeds_forward = typeof(DI.multibasis(x, Int[]))[] + compressed_matrix_forward = zeros(eltype(y), length(y), 0) + batched_seeds_forward = NTuple{Bf,typeof(DI.multibasis(x, Int[]))}[] + batched_results_forward = NTuple{Bf,typeof(similar(y))}[] + dummy_seeds_forward = ntuple(_ -> DI.multibasis(x, Int[]), Val(Bf)) else - stack(_ -> vec(similar(y)), groups_forward; dims=2) + seeds_forward = [DI.multibasis(x, eachindex(x)[group]) for group in groups_forward] + compressed_matrix_forward = stack(_ -> vec(similar(y)), groups_forward; dims=2) + batched_seeds_forward = [ntuple(b -> seeds_forward[1+((a-1)*Bf+(b-1))%Nf], Val(Bf)) for a in 1:Af] + batched_results_forward = [ntuple(b -> similar(y), Val(Bf)) for _ in batched_seeds_forward] + dummy_seeds_forward = batched_seeds_forward[1] end - compressed_matrix_reverse = if isempty(groups_reverse) - zeros(eltype(x), 0, length(x)) + + # Handle reverse direction + if isempty(groups_reverse) + seeds_reverse = typeof(DI.multibasis(y, Int[]))[] + compressed_matrix_reverse = zeros(eltype(x), 0, length(x)) + batched_seeds_reverse = NTuple{Br,typeof(DI.multibasis(y, Int[]))}[] + batched_results_reverse = NTuple{Br,typeof(similar(x))}[] + dummy_seeds_reverse = ntuple(_ -> DI.multibasis(y, Int[]), Val(Br)) else - stack(_ -> vec(similar(x)), groups_reverse; dims=1) + seeds_reverse = [DI.multibasis(y, eachindex(y)[group]) for group in groups_reverse] + compressed_matrix_reverse = stack(_ -> vec(similar(x)), groups_reverse; dims=1) + batched_seeds_reverse = [ntuple(b -> seeds_reverse[1+((a-1)*Br+(b-1))%Nr], Val(Br)) for a in 1:Ar] + batched_results_reverse = [ntuple(b -> similar(x), Val(Br)) for _ in batched_seeds_reverse] + dummy_seeds_reverse = batched_seeds_reverse[1] end - batched_seeds_forward = [ - ntuple(b -> seeds_forward[1 + ((a - 1) * Bf + (b - 1)) % Nf], Val(Bf)) for a in 1:Af - ] - batched_seeds_reverse = [ - ntuple(b -> seeds_reverse[1 + ((a - 1) * Br + (b - 1)) % Nr], Val(Br)) for a in 1:Ar - ] - - batched_results_forward = [ - ntuple(b -> similar(y), Val(Bf)) for _ in batched_seeds_forward - ] - batched_results_reverse = [ - ntuple(b -> similar(x), Val(Br)) for _ in batched_seeds_reverse - ] - pushforward_prep = DI.prepare_pushforward_nokwarg( strict, f_or_f!y..., DI.forward_backend(dense_backend), x, - batched_seeds_forward[1], + dummy_seeds_forward, contexts...; ) pullback_prep = DI.prepare_pullback_nokwarg( @@ -153,7 +154,7 @@ function _prepare_mixed_sparse_jacobian_aux_aux( f_or_f!y..., DI.reverse_backend(dense_backend), x, - batched_seeds_reverse[1], + dummy_seeds_reverse, contexts...; ) @@ -204,12 +205,25 @@ function _sparse_jacobian_aux!( Nf = batch_size_settings_forward.N Nr = batch_size_settings_reverse.N + # Get dummy seeds based on whether batched seeds are empty + dummy_seeds_forward = if isempty(batched_seeds_forward) + ntuple(_ -> DI.multibasis(x, Int[]), Val(Bf)) + else + batched_seeds_forward[1] + end + + dummy_seeds_reverse = if isempty(batched_seeds_reverse) + ntuple(_ -> DI.multibasis(y, Int[]), Val(Br)) + else + batched_seeds_reverse[1] + end + pushforward_prep_same = DI.prepare_pushforward_same_point( f_or_f!y..., pushforward_prep, DI.forward_backend(dense_backend), x, - batched_seeds_forward[1], + dummy_seeds_forward, contexts..., ) pullback_prep_same = DI.prepare_pullback_same_point( @@ -217,7 +231,7 @@ function _sparse_jacobian_aux!( pullback_prep, DI.reverse_backend(dense_backend), x, - batched_seeds_reverse[1], + dummy_seeds_reverse, contexts..., ) From 33ad75fef5c6ec5177b1652ed72c5c77bf251954 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Fri, 3 Oct 2025 12:21:12 +0200 Subject: [PATCH 6/6] Another fix --- .../jacobian_mixed.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl index f58682e55..730de307e 100644 --- a/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl +++ b/DifferentiationInterface/ext/DifferentiationInterfaceSparseMatrixColoringsExt/jacobian_mixed.jl @@ -213,6 +213,13 @@ function _sparse_jacobian_aux!( end dummy_seeds_reverse = if isempty(batched_seeds_reverse) + # Only evaluate y when needed for reverse mode dummy seeds + y = if length(f_or_f!y) == 1 + f_or_f!y[1](x, map(DI.unwrap, contexts)...) + else + f_or_f!y[1](f_or_f!y[2], x, map(DI.unwrap, contexts)...) + f_or_f!y[2] + end ntuple(_ -> DI.multibasis(y, Int[]), Val(Br)) else batched_seeds_reverse[1]