From 247f8148f012c83b6695f8b75ae61850427e43c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Thu, 24 Sep 2026 22:02:05 +0200 Subject: [PATCH] Pin that `mul!` into a `Float64` buffer throws instead of zeroing the gradient (#171) The MWE in #171 fills a preallocated buffer with 5-arg `mul!` on views, and ReverseDiff returned an all-zero gradient. With a `Float64` buffer, storing the tracked products fell through to `convert(Float64, ::TrackedReal)`, which up to v1.6.0 silently returned the value and severed the tape. #170 (v1.7.0) made that conversion throw, a week before the issue was filed; with a buffer that can hold tracked reals, the gradients were correct. Nothing pinned either behaviour for this pattern. Co-Authored-By: Claude Opus 5.5 (1M context) --- test/api/GradientTests.jl | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/api/GradientTests.jl b/test/api/GradientTests.jl index ec72257f..1f4db4ef 100644 --- a/test/api/GradientTests.jl +++ b/test/api/GradientTests.jl @@ -451,4 +451,35 @@ end [1.0, 2.0] end +############################################################################################ + +@testset "in-place `mul!` into a preallocated buffer (#171)" begin + function tensor_conv!(est, W, H) + L = size(W, 3) + T = size(H, 2) + @. est = 0 + for lag in 0:(L - 1) + @views mul!(est[:, lag+1:T], W[:, :, lag+1]', H[:, 1:T-lag], 1, 1) + end + return est + end + loss(est, W, H) = sum(abs2, tensor_conv!(est, W, H)) + + W = rand(2, 3, 2) + H = rand(2, 5) + f(W, H) = loss(similar(W, promote_type(eltype(W), eltype(H)), 3, 5), W, H) + + gW = ForwardDiff.gradient(W -> f(W, H), W) + gH = ForwardDiff.gradient(H -> f(W, H), H) + test_approx(ReverseDiff.gradient(W -> f(W, H), W), gW) + foreach(test_approx, ReverseDiff.gradient(f, (W, H)), (gW, gH)) + + # tape replay + tape = ReverseDiff.compile(ReverseDiff.GradientTape(f, (W, H))) + foreach(test_approx, ReverseDiff.gradient!(map(similar, (W, H)), tape, (W, H)), (gW, gH)) + + # a `Float64` buffer throws instead of zeroing the gradient + @test_throws ArgumentError ReverseDiff.gradient(W -> loss(zeros(3, 5), W, H), W) +end + end # module