@@ -42,6 +42,12 @@ jacobicheck(f, dims...) = jacobicheck(f, randn.(Float64, dims)...)
42
42
@test jacobicheck (identity, (4 ,5 )) # one random matrix
43
43
@test jacobicheck (+ , 3 , 3 ) # two random vectors
44
44
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
45
51
46
52
# ####
47
53
# #### Zygote/test/gradcheck.jl : Base
77
83
@test jacobicheck ((w, x) -> Transpose (w)* x, randn (5 ,5 ), randn (5 ,5 ))
78
84
79
85
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)
82
87
@test_broken jacobicheck ((w, x) -> parent (w)* x, randn (5 ,5 )' , randn (5 ,5 ))
83
88
@test_broken jacobicheck ((w, x) -> parent (w)* x, transpose (randn (5 ,5 )), randn (5 ,5 ))
84
89
end
90
95
@test gradcheck (x -> sum ((i-> x[i]). (1 : length (x))), randn (10 ))
91
96
@test gradcheck (X -> sum (x -> x^ 2 , X), randn (10 ))
92
97
93
- # FIXME : fail with
94
- # MethodError: no method matching copy(::Nothing)
98
+ # MethodError: no method matching copy(::Nothing)
95
99
@test_broken jacobicheck (x -> sum (x, dims = (2 , 3 )), (3 ,4 ,5 ))
96
100
@test_broken jacobicheck (x -> sum (abs2, x; dims= 1 ), randn (4 , 3 , 2 ))
97
101
@test_broken gradcheck (X -> sum (sum (x -> x^ 2 , X; dims= 1 )), randn (10 )) # issue #681
@@ -104,40 +108,276 @@ end
104
108
@test gradient ((x,y) -> sum (yi -> yi* x, y), 1 , [1 ,1 ]) == (2 , [1 , 1 ])
105
109
@test gradient ((x,y) -> prod (yi -> yi* x, y), 1 , [1 ,1 ]) == (2 , [1 , 1 ])
106
110
107
- # FIXME : fail with
108
- # AssertionError: Base.issingletontype(typeof(f))
111
+ # AssertionError: Base.issingletontype(typeof(f))
109
112
@test_broken gradient ((x,y) -> sum (map (yi -> yi* x, y)), 1 , [1 ,1 ]) == (2 , [1 , 1 ])
110
113
@test_broken gradient ((x,y) -> prod (map (yi -> yi* x, y)), 1 , [1 ,1 ]) == (2 , [1 , 1 ])
111
114
112
115
@test gradcheck (x -> prod (x), (3 ,4 ))
113
116
@test gradient (x -> prod (x), (1 ,2 ,3 ))[1 ] == (6 ,3 ,2 )
114
117
115
- # FIXME : fail with
116
- # MethodError: no method matching copy(::Nothing)
118
+ # MethodError: no method matching copy(::Nothing)
117
119
@test_broken jacobicheck (x -> prod (x, dims = (2 , 3 )), (3 ,4 ,5 ))
118
120
end
119
121
120
122
@testset " cumsum" begin
121
123
@test jacobicheck (x -> cumsum (x), (4 ,))
122
124
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
125
126
@test_broken jacobicheck (x -> cumsum (x, dims= 2 ), (3 ,4 ,5 ))
126
127
@test_broken jacobicheck (x -> cumsum (x, dims= 3 ), (3 ,4 )) # trivial
127
128
128
- # FIXME : fail with
129
- # MethodError: no method matching copy(::Nothing)
129
+ # MethodError: no method matching copy(::Nothing)
130
130
@test_broken jacobicheck (x -> cumsum (x, dims= 1 ), (3 ,))
131
131
132
- # FIXME : fail with
133
- # Rewrite reached intrinsic function bitcast. Missing rule?
132
+ # Rewrite reached intrinsic function bitcast. Missing rule?
134
133
@test_broken jacobicheck (x -> cumsum (x, dims= 3 ), (5 ,)) # trivial
135
134
end
136
135
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
+
137
373
138
374
# FIXME : complex numbers; put somewhere
139
375
@test gradcheck ((a,b)-> sum (reim (acosh (complex (a[1 ], b[1 ])))), [- 2.0 ], [1.0 ])
140
376
377
+ # FIXME : misc tests
378
+ @test jacobicheck (x -> x' , rand (5 ))
379
+
380
+
141
381
# FIXME : include those?
142
382
# @testset "println, show, string, etc" begin
143
383
# function foo(x)
0 commit comments