Skip to content

Commit 11ca009

Browse files
committed
more tests
1 parent fc6b7fc commit 11ca009

File tree

2 files changed

+255
-15
lines changed

2 files changed

+255
-15
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
3636
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3737

3838
[targets]
39-
test = ["ForwardDiff", "LinearAlgebra", "Random", "Symbolics", "Test"]
39+
test = ["ForwardDiff", "LinearAlgebra", "Random", "Symbolics", "Test", "FiniteDifferences"]

test/gradcheck.jl

Lines changed: 254 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ jacobicheck(f, dims...) = jacobicheck(f, randn.(Float64, dims)...)
4242
@test jacobicheck(identity, (4,5)) # one random matrix
4343
@test jacobicheck(+, 3, 3) # two random vectors
4444

45+
# Zygote's misnamed hobbit function:
46+
function pullback(f, x...)
47+
y, b = Diffractor.∂⃖{1}()(f, x...)
48+
back(dy) = map(unthunk, Base.tail(b(dy)))
49+
y, back
50+
end
4551

4652
#####
4753
##### Zygote/test/gradcheck.jl : Base
@@ -77,8 +83,7 @@ end
7783
@test jacobicheck((w, x) -> Transpose(w)*x, randn(5,5), randn(5,5))
7884

7985

80-
# FIXME: fail with:
81-
# MethodError: no method matching isapprox(::Tangent{Adjoint{Float64, Matrix{Float64}}, @NamedTuple{parent::Matrix{Float64}}}, ::Adjoint{Float64, Matrix{Float64}}; rtol::Float64, atol::Float64)
86+
# MethodError: no method matching isapprox(::Tangent{Adjoint{Float64, Matrix{Float64}}, @NamedTuple{parent::Matrix{Float64}}}, ::Adjoint{Float64, Matrix{Float64}}; rtol::Float64, atol::Float64)
8287
@test_broken jacobicheck((w, x) -> parent(w)*x, randn(5,5)', randn(5,5))
8388
@test_broken jacobicheck((w, x) -> parent(w)*x, transpose(randn(5,5)), randn(5,5))
8489
end
@@ -90,8 +95,7 @@ end
9095
@test gradcheck(x -> sum((i->x[i]).(1:length(x))), randn(10))
9196
@test gradcheck(X -> sum(x -> x^2, X), randn(10))
9297

93-
# FIXME: fail with
94-
# MethodError: no method matching copy(::Nothing)
98+
# MethodError: no method matching copy(::Nothing)
9599
@test_broken jacobicheck(x -> sum(x, dims = (2, 3)), (3,4,5))
96100
@test_broken jacobicheck(x -> sum(abs2, x; dims=1), randn(4, 3, 2))
97101
@test_broken gradcheck(X -> sum(sum(x -> x^2, X; dims=1)), randn(10)) # issue #681
@@ -104,40 +108,276 @@ end
104108
@test gradient((x,y) -> sum(yi -> yi*x, y), 1, [1,1]) == (2, [1, 1])
105109
@test gradient((x,y) -> prod(yi -> yi*x, y), 1, [1,1]) == (2, [1, 1])
106110

107-
# FIXME: fail with
108-
# AssertionError: Base.issingletontype(typeof(f))
111+
# AssertionError: Base.issingletontype(typeof(f))
109112
@test_broken gradient((x,y) -> sum(map(yi -> yi*x, y)), 1, [1,1]) == (2, [1, 1])
110113
@test_broken gradient((x,y) -> prod(map(yi -> yi*x, y)), 1, [1,1]) == (2, [1, 1])
111114

112115
@test gradcheck(x -> prod(x), (3,4))
113116
@test gradient(x -> prod(x), (1,2,3))[1] == (6,3,2)
114117

115-
# FIXME: fail with
116-
# MethodError: no method matching copy(::Nothing)
118+
# MethodError: no method matching copy(::Nothing)
117119
@test_broken jacobicheck(x -> prod(x, dims = (2, 3)), (3,4,5))
118120
end
119121

120122
@testset "cumsum" begin
121123
@test jacobicheck(x -> cumsum(x), (4,))
122124

123-
# FIXME: fail with
124-
# TypeError: in typeassert, expected Int64, got a value of type Nothing
125+
# TypeError: in typeassert, expected Int64, got a value of type Nothing
125126
@test_broken jacobicheck(x -> cumsum(x, dims=2), (3,4,5))
126127
@test_broken jacobicheck(x -> cumsum(x, dims=3), (3,4)) # trivial
127128

128-
# FIXME: fail with
129-
# MethodError: no method matching copy(::Nothing)
129+
# MethodError: no method matching copy(::Nothing)
130130
@test_broken jacobicheck(x -> cumsum(x, dims=1), (3,))
131131

132-
# FIXME: fail with
133-
# Rewrite reached intrinsic function bitcast. Missing rule?
132+
# Rewrite reached intrinsic function bitcast. Missing rule?
134133
@test_broken jacobicheck(x -> cumsum(x, dims=3), (5,)) # trivial
135134
end
136135

136+
@testset "getindex" begin
137+
@test jacobicheck(x -> x[:, 2, :], (3, 4, 5))
138+
@test jacobicheck(x -> x[1:2, 3:4], (3, 4))
139+
140+
imat = [1 2; 3 4]
141+
@test jacobicheck(x -> x[:, imat], (3, 4))
142+
143+
# incorrect gradient
144+
# julia> gradient(sum ∘ x->x[:,[1,2,2]], x)[1]
145+
# 3×4 Matrix{Float64}:
146+
# 1.0 1.0 0.0 0.0
147+
# 1.0 1.0 0.0 0.0
148+
# 1.0 1.0 0.0 0.0
149+
# while it should be
150+
# 3×4 Matrix{Float64}:
151+
# 1.0 2.0 0.0 0.0
152+
# 1.0 2.0 0.0 0.0
153+
# 1.0 2.0 0.0 0.0
154+
@test_broken jacobicheck(x -> x[:, [1, 2, 2]], (3, 4))
155+
# same here
156+
irep = [1 2; 2 2]
157+
@test_broken jacobicheck(x -> x[1, irep], (3, 4))
158+
159+
# https://github.com/invenia/Nabla.jl/issues/139
160+
x = rand(3)
161+
z = [1, 2, 3, 3]
162+
y139(x, z) = dot(ones(4), x[z])
163+
# Evaluated: ([1.0 0.0 0.0; 1.0 0.0 0.0; 2.0 0.0 0.0], NoTangent()) == ([1, 1, 2], NoTangent())
164+
@test_broken gradient(y139, x, z) == ([1, 1, 2], NoTangent())
165+
166+
# https://github.com/FluxML/Zygote.jl/issues/376
167+
168+
_, back = pullback(x->x[1]*im, randn(2))
169+
@test back(1.0)[1] == real([-im, 0]) == [0, 0]
170+
171+
# _droplike
172+
@test gradient(x -> sum(inv, x[1, :]'), ones(2, 2)) == ([-1 -1; 0 0],)
173+
@test gradient(x -> sum(inv, transpose(x[1, :])), ones(2, 2)) == ([-1 -1; 0 0],) # same with transpose, in case ' overloaded!
174+
@test gradient(x -> sum(inv, x[1:1, :]'), ones(2, 2)) == ([-1 -1; 0 0],)
175+
@test gradient(x -> sum(inv, transpose(x[1:1, :])), ones(2, 2)) == ([-1 -1; 0 0],)
176+
@test gradient(x -> sum(inv, transpose(view(x, 1, :))), ones(2, 2)) == ([-1 -1; 0 0],)
177+
178+
# https://github.com/FluxML/Zygote.jl/issues/513
179+
# MethodError: no method matching copy(::Nothing)
180+
@test_broken gradient(p -> sum(Float32[1, 0] - p), [2, 3]) == ([-1, -1],)
181+
@test_broken gradient(x -> sum(Float32[1, x] .+ x), 4) == (3.0f0,)
182+
183+
# Ensure that nothings work with numeric types.
184+
_, back = pullback(getindex, randn(4), [1])
185+
@test back([ZeroTangent()]) == (zeros(4), NoTangent())
186+
# Ensure that nothings work with non-numeric types.
187+
_, back = pullback(getindex, [randn(2) for _ in 1:3], [1])
188+
@test back([ZeroTangent()]) == (NoTangent(), NoTangent())
189+
end
190+
191+
@testset "view" begin
192+
@test jacobicheck(x -> view(x,:,2,:), (3,4,5))
193+
@test jacobicheck(x -> view(x,1:2,3:4), (3,4))
194+
@test jacobicheck(x -> view(x,:,[1,2,2]), (3,4))
195+
196+
# https://github.com/FluxML/Zygote.jl/issues/272
197+
g272(x) = view(x,1:2)[1]
198+
@test gradient(g272, ones(3)) == ([1,0,0],)
199+
end
200+
201+
# 194
202+
@testset "eachcol" begin
203+
# MethodError: no method matching one(::SubArray{Float64, 1, Matrix{Float64}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, true})
204+
@test_broken jacobicheck(x -> map(sum, eachcol(x)), (3,4))
205+
@test_broken jacobicheck(x -> map(sum, eachcol(transpose(x))), (3,4))
206+
@test_broken jacobicheck(x -> map(norm, eachcol(x)), (3,4))
207+
@test_broken jacobicheck(x -> map(norm, eachrow(x)), (3,4))
208+
@test_broken jacobicheck(x -> map(norm, eachslice(x, dims=3)), (3,4,5))
209+
210+
# some slices may have gradient nothing
211+
@test gradient(x -> sum(y -> rand()>0.5 ? 0 : first(y), eachcol(x)), rand(3,10))[1] isa Matrix
212+
213+
# strange errors (on Zygote)
214+
@test gradient(x -> sum(norm, eachcol(x)), [1 2 3; 4 5 6])[1] isa Matrix
215+
@test gradient(x -> sum(norm, eachcol(x)), rand(3,400))[1] isa Matrix
216+
end
217+
218+
@testset "collect" begin
219+
# MethodError: no method matching copy(::Nothing)
220+
@test_broken gradient(x -> sum(inv, collect(x)), (1,2)) === ((-1.0, -1/4),)
221+
@test_broken gradient(xs -> sum(inv, [x^2 for x in xs]), ones(2)) == ([-2, -2],)
222+
223+
# Rewrite reached intrinsic function bitcast. Missing rule?
224+
@test_broken gradient(x -> sum(collect(view(x, 1:1))), rand(2)) == ([1,0],)
225+
@test_broken gradient(x -> sum(inv, collect(view(x', 1,:))), ones(2,2)) == ([-1 0; -1 0],)
226+
end
227+
228+
229+
@testset "reverse" begin
230+
@test jacobicheck(x -> reverse(x), rand(17))
231+
@test jacobicheck(x -> reverse(x, 8), rand(17))
232+
@test jacobicheck(x -> reverse(x, 8, 13), rand(17))
233+
# Rewrite reached intrinsic function bitcast. Missing rule?
234+
@test_broken jacobicheck(x -> reverse(x, dims=2), rand(17, 42))
235+
end
236+
237+
@testset "permutedims" begin
238+
@test jacobicheck(x -> permutedims(x), rand(2))
239+
@test jacobicheck(x -> permutedims(x), rand(2,3))
240+
@test jacobicheck(x -> permutedims(x, [3,1,2]), rand(4,5,6))
241+
@test jacobicheck(x -> PermutedDimsArray(x, (3,1,2)), rand(4,5,6))
242+
let
243+
y, back = pullback(permutedims, randn(3))
244+
@test first(back(randn(1, 3))) isa Vector
245+
end
246+
end
247+
248+
@testset "repeat" begin
249+
# MethodError: no method matching copy(::Nothing)
250+
@test_broken jacobicheck(x -> repeat(x; inner=2), rand(5))
251+
@test_broken jacobicheck(x -> repeat(x; inner=2, outer=3), rand(5))
252+
@test_broken jacobicheck(x -> repeat(x; inner=(2,2,1), outer=(1,1,3)), rand(5,4,3))
253+
254+
@test jacobicheck(x -> repeat(x, 3), rand(5))
255+
@test jacobicheck(x -> repeat(x, 2, 3), rand(5))
256+
@test jacobicheck(x -> repeat(x, 5), rand(5,7))
257+
@test jacobicheck(x -> repeat(x, 3, 2), rand(5,3))
258+
end
259+
260+
@testset "fill" begin
261+
@test jacobicheck(x->fill(x[1], 3), rand(1))
262+
@test jacobicheck(x->fill(x[1], 3, 4, 2), rand(1))
263+
264+
# fill(struct, ...) handled by ChainRules after
265+
# https://github.com/FluxML/Zygote.jl/pull/1051
266+
# FIXME: marking as broken, because not sure what the tangent should contain.
267+
# in the first we have a ZeroTangent, and the second we dont.
268+
@test_broken gradient(x -> fill(x, 3)[1][1], (1,2)) == (Tangent{Tuple{Int,Int}}(1.0,),)
269+
@test_broken gradient(x -> fill(x, 3)[1].a, (a=1, b=2)) == (Tangent{@NamedTuple{a::Int64, b::Int64}}(a = 1.0, b=nothing),) # 1 not 1.0
270+
end
271+
272+
@testset "circshift" begin
273+
for D in 1:5
274+
x0 = zeros(ntuple(d->5, D))
275+
g = gradient(x -> x[1], x0)[1]
276+
shift = ntuple(_ -> rand(-5:5), D)
277+
@test gradient(x -> circshift(x, shift)[1], x0)[1] == circshift(g, map(-, shift))
278+
end
279+
end
280+
281+
@testset "map" begin
282+
@testset "bascis" begin
283+
@test jacobicheck(xs -> map(x -> x^2, xs), rand(2,3))
284+
285+
# MethodError: no method matching copy(::Nothing)
286+
@test_broken jacobicheck((xss...) -> sum(map((xs...) -> sqrt(sum(xs.^2)), xss...)), [rand(5) for _ in 1:6]...)
287+
288+
# MethodError: no method matching copy(::Nothing)
289+
@test_broken gradcheck(y -> map(x -> x*y, 1:5), 3)
290+
@test_broken gradient(v -> sum([x for x in v]), [1.1,2.2,3.3]) == ([1, 1, 1],)
291+
end
292+
293+
@test_skip @testset "bascis, pmap" begin
294+
@test jacobicheck(xs -> sum(pmap(x -> x^2, xs)), rand(2,3))
295+
@test jacobicheck((xss...) -> sum(pmap((xs...) -> sqrt(sum(xs.^2)), xss...)), [rand(5) for _ in 1:6]...)
296+
297+
function foo(y)
298+
bar = (x) -> x*y
299+
sum(pmap(bar, 1:5))
300+
end
301+
@test gradtest(foo, 3)
302+
@test gradient(v -> sum([x for x in v]), [1.1,2.2,3.3]) == ([1, 1, 1],)
303+
end
304+
305+
@testset "Tuple adjoint" begin
306+
x = randn(3)
307+
_, pb = pullback(x -> map(abs2, x), x)
308+
Δy = randn(3)
309+
@test first(pb((Δy..., ))) first(pb(Δy))
310+
end
311+
312+
@testset "empty tuples" begin
313+
out, pb = pullback(map, -, ())
314+
@test pb(out) === (NoTangent(), NoTangent())
315+
316+
out, pb = pullback(map, +, (), ())
317+
# MethodError: reducing over an empty collection is not allowed, ChainRules.var"#map_pullback#1234"{typeof(+), Tuple{Tuple{}, Tuple{}},
318+
@test_broken pb(()) === (ZeroTangent(), ZeroTangent(), ZeroTangent())
319+
320+
function build_foo(z)
321+
foo(x) = x * z
322+
return foo
323+
end
324+
out, pb = pullback(map, build_foo(5.0), ())
325+
@test pb(()) === (NoTangent(), NoTangent())
326+
end
327+
328+
@testset "Vector{Nothing} cotangent" begin
329+
Δ = fill(ZeroTangent(), 5)
330+
331+
# Unary stateless
332+
out, pb = pullback(map, -, randn(5))
333+
@test pb(Δ)[2] isa Vector{ZeroTangent}
334+
335+
# Binary stateless
336+
out, pb = pullback(map, +, randn(5), randn(5))
337+
@test pb(Δ)[2] isa Vector{ZeroTangent}
338+
@test pb(Δ)[3] isa Vector{ZeroTangent}
339+
340+
# Stateful
341+
function build_foo(z)
342+
foo(x) = x * z
343+
return foo
344+
end
345+
# AssertionError: Base.issingletontype(typeof(f))
346+
@test_broken out, pb = pullback(map, build_foo(5.0), randn(5))
347+
@test_skip pb(Δ)[2] isa Vector{ZeroTangent}
348+
end
349+
end
350+
351+
352+
353+
354+
@testset "LinearAlgebra.det" begin
355+
@test jacobicheck(det, (4, 4))
356+
@test jacobicheck(logdet, map(x -> x*x'+I, (randn(4, 4),))[1])
357+
@test jacobicheck(x -> logabsdet(x)[1], (4, 4))
358+
@test gradient(det, 2.0)[1] == 1
359+
@test gradient(logdet, 2.0)[1] == 0.5
360+
end
361+
362+
@testset "kron" begin
363+
# FIXME: fail with
364+
# TypeError: in typeassert, expected Int64, got a value of type Nothing
365+
@test_broken jacobicheck(kron, 5, 3)
366+
@test_broken jacobicheck(kron, rand(5), rand(3), rand(8))
367+
@test_broken jacobicheck(kron, rand(5,1), rand(3,1))
368+
@test_broken jacobicheck(kron, rand(5,1), rand(3,1), rand(8,1))
369+
@test_broken jacobicheck(kron, rand(5,2), rand(3,2), rand(8,2))
370+
end
371+
372+
137373

138374
# FIXME: complex numbers; put somewhere
139375
@test gradcheck((a,b)->sum(reim(acosh(complex(a[1], b[1])))), [-2.0], [1.0])
140376

377+
# FIXME: misc tests
378+
@test jacobicheck(x -> x', rand(5))
379+
380+
141381
# FIXME: include those?
142382
# @testset "println, show, string, etc" begin
143383
# function foo(x)

0 commit comments

Comments
 (0)