chumpy-fixed 0.71__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
chumpy/test_ch.py ADDED
@@ -0,0 +1,621 @@
1
+ #!/usr/bin/env python
2
+ # encoding: utf-8
3
+ """
4
+ Author(s): Matthew Loper
5
+
6
+ See LICENCE.txt for licensing and contact information.
7
+ """
8
+
9
+ import time
10
+
11
+ import unittest
12
+ import numpy as np
13
+ import scipy.sparse as sp
14
+
15
+ from . import ch
16
+
17
+ class TestCh(unittest.TestCase):
18
+
19
+
20
+ def test_cachehits(self):
21
+ """Test how many nodes are visited when cache is cleared.
22
+ If the number of hits changes, it has to be carefully
23
+ looked at to make sure that correctness and performance
24
+ don't get messed up by a change."""
25
+
26
+ a = ch.array(1)
27
+ b = ch.array(2)
28
+ c = a
29
+ for i in range(10):
30
+ c = a + c + b
31
+
32
+ c.dr_wrt(a)
33
+ c.dr_wrt(b)
34
+ self.assertEqual(a.clear_cache() + b.clear_cache(), 59)
35
+ c.dr_wrt(a)
36
+ c.dr_wrt(b)
37
+ self.assertEqual(a.clear_cache(123) + b.clear_cache(123), 41)
38
+
39
+ def test_nested_concatenate(self):
40
+ aa = ch.arange(3)
41
+ bb = ch.arange(4)
42
+ cc = ch.arange(5)
43
+
44
+ result = ch.concatenate((ch.concatenate((aa,bb)),cc))
45
+ self.assertTrue(result.m0 is aa)
46
+ self.assertTrue(result.m1 is bb)
47
+ self.assertTrue(result.m2 is cc)
48
+
49
+ self.assertTrue(result.dr_wrt(aa).nnz > 0)
50
+ self.assertTrue(result.dr_wrt(bb).nnz > 0)
51
+ self.assertTrue(result.dr_wrt(cc).nnz > 0)
52
+
53
+ def test_nandivide(self):
54
+ foo = ch.array(np.random.randn(16).reshape((4,4)))
55
+ bar = ch.array(np.random.randn(16).reshape((4,4)))
56
+ bar[2,2] = 0
57
+ self.assertEqual(ch.NanDivide(foo,bar)[2,2].r, 0.)
58
+ foo[2,2] = 0
59
+ self.assertEqual(ch.NanDivide(foo,bar)[2,2].r, 0.)
60
+
61
+ def test_casting(self):
62
+ for fn in float, int:
63
+ self.assertEqual(fn(np.array(5)), fn(ch.array(5)))
64
+ self.assertEqual(fn(np.array([[5]])), fn(ch.array([[5]])))
65
+
66
+ def test_tensordot(self):
67
+ an = np.arange(60.).reshape(3,4,5)
68
+ bn = np.arange(24.).reshape(4,3,2)
69
+ cn = np.tensordot(an,bn, axes=([1,0],[0,1]))
70
+
71
+ ac = ch.arange(60.).reshape(3,4,5)
72
+ bc = ch.arange(24.).reshape(4,3,2)
73
+ cc = ch.tensordot(ac,bc, axes=([1,0],[0,1]))
74
+
75
+ cc.r
76
+ cc.dr_wrt(ac)
77
+ cc.dr_wrt(bc)
78
+ #print cn
79
+
80
+ def test_make_sure_is_double(self):
81
+ x = ch.array([0])
82
+ self.assertTrue(isinstance(x.r[0], np.float64))
83
+
84
+ def test_cross(self):
85
+ aa = ch.random.randn(30).reshape((10,3))
86
+ bb = ch.random.randn(30).reshape((10,3))
87
+
88
+ cross_ch = ch.cross(aa, bb)
89
+ cross_np = np.cross(aa.r, bb.r)
90
+
91
+ # print cross_ch.r
92
+ # print cross_np
93
+
94
+ eps = 1.0
95
+ step = (np.random.rand(30) - .5).reshape((10,3)) * eps
96
+
97
+ gt_diff = np.cross(aa.r, bb.r+step) - cross_np
98
+ pr_diff = cross_ch.dr_wrt(bb).dot(step.ravel())
99
+ # print gt_diff
100
+ # print pr_diff
101
+ # print np.max(np.abs(gt_diff.ravel()-pr_diff.ravel()))
102
+ self.assertTrue(1e-14 > np.max(np.abs(gt_diff.ravel()-pr_diff.ravel())))
103
+
104
+ gt_diff = np.cross(aa.r+step, bb.r) - cross_np
105
+ pr_diff = cross_ch.dr_wrt(aa).dot(step.ravel())
106
+ #print gt_diff
107
+ # print pr_diff
108
+ # print np.max(np.abs(gt_diff.ravel()-pr_diff.ravel()))
109
+ self.assertTrue(1e-14 > np.max(np.abs(gt_diff.ravel()-pr_diff.ravel())))
110
+
111
+ def test_dr_wrt_selection(self):
112
+ aa = ch.arange(10,20)
113
+ bb = ch.arange(1,11)
114
+ cc = aa * bb + aa + bb +2
115
+
116
+ dr0 = cc.dr_wrt(aa[4:6])
117
+ dr1 = cc.dr_wrt(aa)[:,4:6]
118
+ self.assertTrue((dr0 - dr1).nnz == 0)
119
+
120
+ dr0 = cc.dr_wrt(bb[5:8])
121
+ dr1 = cc.dr_wrt(bb)[:,5:8]
122
+ self.assertTrue((dr0 - dr1).nnz == 0)
123
+
124
+
125
+ def test_sum_mean_std_var(self):
126
+ for fn in [ch.sum, ch.mean, ch.var, ch.std]:
127
+
128
+ # Create fake input and differences in input space
129
+ data1 = ch.ones((3,4,7,2))
130
+ data2 = ch.array(data1.r + .1 * np.random.rand(data1.size).reshape(data1.shape))
131
+ diff = data2.r - data1.r
132
+
133
+ # Compute outputs
134
+ result1 = fn(data1, axis=2)
135
+ result2 = fn(data2, axis=2)
136
+
137
+ # Empirical and predicted derivatives
138
+ gt = result2.r - result1.r
139
+ pred = result1.dr_wrt(data1).dot(diff.ravel()).reshape(gt.shape)
140
+
141
+ #print np.max(np.abs(gt - pred))
142
+
143
+ if fn in [ch.std, ch.var]:
144
+ self.assertTrue(1e-2 > np.max(np.abs(gt - pred)))
145
+ else:
146
+ self.assertTrue(1e-14 > np.max(np.abs(gt - pred)))
147
+ # test caching
148
+ dr0 = result1.dr_wrt(data1)
149
+ data1[:] = np.random.randn(data1.size).reshape(data1.shape)
150
+ self.assertTrue(result1.dr_wrt(data1) is dr0) # changing values shouldn't force recompute
151
+ result1.axis=1
152
+ self.assertTrue(result1.dr_wrt(data1) is not dr0)
153
+
154
+ self.assertEqual(ch.mean(ch.eye(3),axis=1).ndim, np.mean(np.eye(3),axis=1).ndim)
155
+ self.assertEqual(ch.mean(ch.eye(3),axis=0).ndim, np.mean(np.eye(3),axis=0).ndim)
156
+ self.assertEqual(ch.sum(ch.eye(3),axis=1).ndim, np.sum(np.eye(3),axis=1).ndim)
157
+ self.assertEqual(ch.sum(ch.eye(3),axis=0).ndim, np.sum(np.eye(3),axis=0).ndim)
158
+
159
+
160
+
161
+ def test_cumsum(self):
162
+ a = ch.array([1.,5.,3.,7.])
163
+ cs = ch.cumsum(a)
164
+ r1 = cs.r
165
+ dr = cs.dr_wrt(a)
166
+ diff = (ch.random.rand(4)-.5)*.1
167
+ a.x += diff.r
168
+ pred = dr.dot(diff.r)
169
+ gt = cs.r - r1
170
+ self.assertTrue(1e-13 > np.max(np.abs(gt - pred)))
171
+
172
+
173
+ def test_iteration_cache(self):
174
+ """ Each time you set an attribute, the cache (of r's and dr's) of
175
+ ancestors is cleared. Because children share ancestors, this means
176
+ these can be cleared multiple times unnecessarily; in some cases,
177
+ where lots of objects exist, this cache clearing can actually be a bottleneck.
178
+
179
+ Therefore, the concept of an iteration was added; intended to be used in
180
+ an optimization setting (see optimization.py) and in the set() method, it
181
+ avoids such redundant clearing of cache."""
182
+
183
+ a, b, c = ch.Ch(1), ch.Ch(2), ch.Ch(3)
184
+ x = a+b
185
+ y = x+c
186
+ self.assertTrue(y.r[0]==6)
187
+
188
+ a.__setattr__('x', 10, 1)
189
+ self.assertTrue(y.r == 15)
190
+ a.__setattr__('x', 100, 1)
191
+ self.assertTrue(y.r == 15)
192
+ a.__setattr__('x', 100, 2)
193
+ self.assertTrue(y.r == 105)
194
+
195
+ a, b, c = ch.array([1]), ch.array([2]), ch.array([3])
196
+ x = a+b
197
+ y = x+c
198
+ self.assertTrue(y.r[0]==6)
199
+
200
+ a.__setattr__('x', np.array([10]), 1)
201
+ self.assertTrue(y.r[0] == 15)
202
+ a.__setattr__('x', np.array(100), 1)
203
+ self.assertTrue(y.r[0] == 15)
204
+ a.__setattr__('x', np.array(100), 2)
205
+ self.assertTrue(y.r[0] == 105)
206
+ a.__setitem__(list(range(0,1)), np.array(200), 2)
207
+ self.assertTrue(y.r[0] == 105)
208
+ a.__setitem__(list(range(0,1)), np.array(200), 3)
209
+ self.assertTrue(y.r[0] == 205)
210
+
211
+
212
+
213
+ def test_stacking(self):
214
+
215
+ a1 = ch.Ch(np.arange(10).reshape(2,5))
216
+ b1 = ch.Ch(np.arange(20).reshape(4,5))
217
+ c1 = ch.vstack((a1,b1))
218
+ c1_check = np.vstack((a1.r, b1.r))
219
+ residuals1 = (c1_check - c1.r).ravel()
220
+
221
+
222
+ a2 = ch.Ch(np.arange(10).reshape(5,2))
223
+ b2 = ch.Ch(np.arange(20).reshape(5,4))
224
+ c2 = ch.hstack((a2,b2))
225
+ c2_check = np.hstack((a2.r, b2.r))
226
+ residuals2 = (c2_check - c2.r).ravel()
227
+
228
+ self.assertFalse(np.any(residuals1))
229
+ self.assertFalse(np.any(residuals2))
230
+
231
+ d0 = ch.array(np.arange(60).reshape((10,6)))
232
+ d1 = ch.vstack((d0[:4], d0[4:]))
233
+ d2 = ch.hstack((d1[:,:3], d1[:,3:]))
234
+ tmp = d2.dr_wrt(d0).todense()
235
+ diff = tmp - np.eye(tmp.shape[0])
236
+ self.assertFalse(np.any(diff.ravel()))
237
+
238
+
239
+
240
+ #def test_drs(self):
241
+ # a = ch.Ch(2)
242
+ # b = ch.Ch(3)
243
+ # c = a * b
244
+ # print c.dr_wrt(a)
245
+ # print c.compute_drs_wrt(a).r
246
+
247
+ @unittest.skip('We are using LinearOperator for this for now. Might change back though.')
248
+ def test_reorder_caching(self):
249
+ a = ch.Ch(np.zeros(8).reshape((4,2)))
250
+ b = a.T
251
+ dr0 = b.dr_wrt(a)
252
+ a.x = a.x + 1.
253
+ dr1 = b.dr_wrt(a)
254
+ self.assertTrue(dr0 is dr1)
255
+ a.x = np.zeros(4).reshape((2,2))
256
+ dr2 = b.dr_wrt(a)
257
+ self.assertTrue(dr2 is not dr1)
258
+
259
+ def test_transpose(self):
260
+ from .utils import row, col
261
+ from copy import deepcopy
262
+ for which in ('C', 'F'): # test in fortran and contiguous mode
263
+ a = ch.Ch(np.require(np.zeros(8).reshape((4,2)), requirements=which))
264
+ b = a.T
265
+
266
+ b1 = b.r.copy()
267
+ #dr = b.dr_wrt(a).copy()
268
+ dr = deepcopy(b.dr_wrt(a))
269
+
270
+ diff = np.arange(a.size).reshape(a.shape)
271
+ a.x = np.require(a.r + diff, requirements=which)
272
+ b2 = b.r.copy()
273
+
274
+ diff_pred = dr.dot(col(diff)).ravel()
275
+ diff_emp = (b2 - b1).ravel()
276
+ np.testing.assert_array_equal(diff_pred, diff_emp)
277
+
278
+
279
+ def test_unary(self):
280
+ fns = [ch.exp, ch.log, ch.sin, ch.arcsin, ch.cos, ch.arccos, ch.tan, ch.arctan, ch.negative, ch.square, ch.sqrt, ch.abs, ch.reciprocal]
281
+
282
+ eps = 1e-8
283
+ for f in fns:
284
+
285
+ x0 = ch.Ch(.25)
286
+ x1 = ch.Ch(x0.r+eps)
287
+
288
+ pred = f(x0).dr_wrt(x0)
289
+ empr = (f(x1).r - f(x0).r) / eps
290
+
291
+ # print pred
292
+ # print empr
293
+ if f is ch.reciprocal:
294
+ self.assertTrue(1e-6 > np.abs(pred.ravel()[0] - empr.ravel()[0]))
295
+ else:
296
+ self.assertTrue(1e-7 > np.abs(pred.ravel()[0] - empr.ravel()[0]))
297
+
298
+
299
+ def test_serialization(self):
300
+ # The main challenge with serialization is the "_parents"
301
+ # attribute, which is a nonserializable WeakKeyDictionary.
302
+ # So we pickle/unpickle, change a child and verify the value
303
+ # at root, and verify that both children have parentage.
304
+ from six.moves import cPickle as pickle
305
+ tmp = ch.Ch(10) + ch.Ch(20)
306
+ tmp = pickle.loads(pickle.dumps(tmp))
307
+ tmp.b.x = 30
308
+ self.assertTrue(tmp.r[0] == 40)
309
+ self.assertTrue(list(tmp.a._parents.keys())[0] == tmp)
310
+ self.assertTrue(list(tmp.a._parents.keys())[0] == list(tmp.b._parents.keys())[0])
311
+
312
+ def test_chlambda1(self):
313
+ c1, c2, c3 = ch.Ch(1), ch.Ch(2), ch.Ch(3)
314
+ adder = ch.ChLambda(lambda x, y: x+y)
315
+ adder.x = c1
316
+ adder.y = c2
317
+ self.assertTrue(adder.r == 3)
318
+ adder.x = c2
319
+ self.assertTrue(adder.r == 4)
320
+ adder.x = c1
321
+ self.assertTrue(adder.r == 3)
322
+
323
+
324
+ def test_chlambda2(self):
325
+ passthrough = ch.ChLambda( lambda x : x)
326
+ self.assertTrue(passthrough.dr_wrt(passthrough.x) is not None)
327
+ passthrough.x = ch.Ch(123)
328
+ self.assertTrue(passthrough.dr_wrt(passthrough.x) is not None)
329
+
330
+ # It's probably not reasonable to expect this
331
+ # to work for ChLambda
332
+ #def test_chlambda3(self):
333
+ # c1, c2, c3 = ch.Ch(1), ch.Ch(2), ch.Ch(3)
334
+ # triple = ch.ChLambda( lambda x, y, z : x(y, z))
335
+ # triple.x = Add
336
+ # triple.y = c2
337
+ # triple.z = c3
338
+
339
+
340
+
341
+
342
+
343
+ def test_amax(self):
344
+ from .ch import amax
345
+ import numpy as np
346
+ arr = np.empty((5,2,3,7))
347
+ arr.flat[:] = np.sin(np.arange(arr.size)*1000.)
348
+ #arr = np.array(np.sin(np.arange(24)*10000.).reshape(2,3,4))
349
+
350
+ for axis in range(len(arr.shape)):
351
+ a = amax(a=arr, axis=axis)
352
+ pred = a.dr_wrt(a.a).dot(arr.ravel())
353
+ real = np.amax(arr, axis=axis).ravel()
354
+ self.assertTrue(np.max(np.abs(pred-real)) < 1e-10)
355
+
356
+ def test_maximum(self):
357
+ from .utils import row, col
358
+ from .ch import maximum
359
+
360
+ # Make sure that when we compare the max of two *identical* numbers,
361
+ # we get the right derivatives wrt both
362
+ the_max = maximum(ch.Ch(1), ch.Ch(1))
363
+ self.assertTrue(the_max.r.ravel()[0] == 1.)
364
+ self.assertTrue(the_max.dr_wrt(the_max.a)[0,0] == 1.)
365
+ self.assertTrue(the_max.dr_wrt(the_max.b)[0,0] == 1.)
366
+
367
+ # Now test given that all numbers are different, by allocating from
368
+ # a pool of randomly permuted numbers.
369
+ # We test combinations of scalars and 2d arrays.
370
+ rnd = np.asarray(np.random.permutation(np.arange(20)), np.float64)
371
+ c1 = ch.Ch(rnd[:6].reshape((2,3)))
372
+ c2 = ch.Ch(rnd[6:12].reshape((2,3)))
373
+ s1 = ch.Ch(rnd[12])
374
+ s2 = ch.Ch(rnd[13])
375
+
376
+ eps = .1
377
+ for first in [c1, s1]:
378
+ for second in [c2, s2]:
379
+ the_max = maximum(first, second)
380
+
381
+ for which_to_change in [first, second]:
382
+
383
+
384
+ max_r0 = the_max.r.copy()
385
+ max_r_diff = np.max(np.abs(max_r0 - np.maximum(first.r, second.r)))
386
+ self.assertTrue(max_r_diff == 0)
387
+ max_dr = the_max.dr_wrt(which_to_change).copy()
388
+ which_to_change.x = which_to_change.x + eps
389
+ max_r1 = the_max.r.copy()
390
+
391
+ emp_diff = (the_max.r - max_r0).ravel()
392
+ pred_diff = max_dr.dot(col(eps*np.ones(max_dr.shape[1]))).ravel()
393
+
394
+ #print 'comparing the following numbers/vectors:'
395
+ #print first.r
396
+ #print second.r
397
+ #print 'empirical vs predicted difference:'
398
+ #print emp_diff
399
+ #print pred_diff
400
+ #print '-----'
401
+
402
+ max_dr_diff = np.max(np.abs(emp_diff-pred_diff))
403
+ #print 'max dr diff: %.2e' % (max_dr_diff,)
404
+ self.assertTrue(max_dr_diff < 1e-14)
405
+
406
+
407
+ def test_shared(self):
408
+
409
+ chs = [ch.Ch(i) for i in range(10)]
410
+ vrs = [float(i) for i in range(10)]
411
+
412
+ func = lambda a : a[0]*a[1] + (a[2]*a[3])/a[4]
413
+
414
+ chained_result = func(chs).r
415
+ regular_result = func(vrs)
416
+
417
+ self.assertTrue(chained_result == regular_result)
418
+ #print chained_result
419
+ #print regular_result
420
+
421
+ chained_func = func(chs)
422
+ chained_func.replace(chs[0], ch.Ch(50))
423
+ vrs[0] = 50
424
+
425
+ chained_result = chained_func.r
426
+ regular_result = func(vrs)
427
+
428
+ self.assertTrue(chained_result == regular_result)
429
+ #print chained_result
430
+ #print regular_result
431
+
432
+
433
+ def test_matmatmult(self):
434
+ from .ch import dot
435
+ mtx1 = ch.Ch(np.arange(6).reshape((3,2)))
436
+ mtx2 = ch.Ch(np.arange(8).reshape((2,4))*10)
437
+
438
+ mtx3 = dot(mtx1, mtx2)
439
+ #print mtx1.r
440
+ #print mtx2.r
441
+ #print mtx3.r
442
+ #print mtx3.dr_wrt(mtx1).todense()
443
+ #print mtx3.dr_wrt(mtx2).todense()
444
+
445
+ for mtx in [mtx1, mtx2]:
446
+ oldval = mtx3.r.copy()
447
+ mtxd = mtx3.dr_wrt(mtx).copy()
448
+ mtx_diff = np.random.rand(mtx.r.size).reshape(mtx.r.shape)
449
+ mtx.x = mtx.r + mtx_diff
450
+ mtx_emp = mtx3.r - oldval
451
+ mtx_pred = mtxd.dot(mtx_diff.ravel()).reshape(mtx_emp.shape)
452
+
453
+ self.assertTrue(np.max(np.abs(mtx_emp - mtx_pred)) < 1e-11)
454
+
455
+
456
+ def test_ndim(self):
457
+ vs = [ch.Ch(np.random.randn(6).reshape(2,3)) for i in range(6)]
458
+ res = vs[0] + vs[1] - vs[2] * vs[3] / (vs[4] ** 2) ** vs[5]
459
+ self.assertTrue(res.shape[0]==2 and res.shape[1]==3)
460
+ res = (vs[0] + 1) + (vs[1] - 2) - (vs[2] * 3) * (vs[3] / 4) / (vs[4] ** 2) ** vs[5]
461
+ self.assertTrue(res.shape[0]==2 and res.shape[1]==3)
462
+ drs = [res.dr_wrt(v) for v in vs]
463
+
464
+
465
+ def test_indexing(self):
466
+ big = ch.Ch(np.arange(60).reshape((10,6)))
467
+ little = big[1:3, 3:6]
468
+ self.assertTrue(np.max(np.abs(little.r - np.array([[9,10,11],[15,16,17]]))) == 0)
469
+
470
+ little = big[5]
471
+ self.assertTrue(np.max(np.abs(little.r - np.arange(30, 36))) == 0)
472
+ self.assertTrue(np.max(np.abs(sp.coo_matrix(little.dr_wrt(big)).col - np.arange(30,36))) == 0)
473
+
474
+ little = big[2, 3]
475
+ self.assertTrue(little.r[0] == 15.0)
476
+
477
+ little = big[2, 3:5]
478
+ self.assertTrue(np.max(np.abs(little.r - np.array([15, 16]))) == 0.)
479
+ _ = little.dr_wrt(big)
480
+
481
+ # Tests assignment through reorderings
482
+ aa = ch.arange(4*4*4).reshape((4,4,4))[:3,:3,:3]
483
+ aa[0,1,2] = 100
484
+ self.assertTrue(aa[0,1,2].r[0] == 100)
485
+
486
+ # Tests assignment through reorderings (NaN's are a special case)
487
+ aa = ch.arange(9).reshape((3,3))
488
+ aa[1,1] = np.nan
489
+ self.assertTrue(np.isnan(aa.r[1,1]))
490
+ self.assertFalse(np.isnan(aa.r[0,0]))
491
+
492
+
493
+ def test_redundancy_removal(self):
494
+
495
+ for MT in [False, True]:
496
+ x1, x2 = ch.Ch(10), ch.Ch(20)
497
+ x1_plus_x2_1 = x1 + x2
498
+ x1_plus_x2_2 = x1 + x2
499
+ redundant_sum = (x1_plus_x2_1 + x1_plus_x2_2) * 2
500
+ redundant_sum.MT = MT
501
+
502
+ self.assertTrue(redundant_sum.a.a is not redundant_sum.a.b)
503
+ redundant_sum.remove_redundancy()
504
+ self.assertTrue(redundant_sum.a.a is redundant_sum.a.b)
505
+
506
+ def test_caching(self):
507
+
508
+ vals = [10, 20, 30, 40, 50]
509
+ f = lambda a, b, c, d, e : a + (b * c) - d ** e
510
+
511
+ # Set up our objects
512
+ Cs = [ch.Ch(v) for v in vals]
513
+ C_result = f(*Cs)
514
+
515
+ # Sometimes residuals should be cached
516
+ r1 = C_result.r
517
+ r2 = C_result.r
518
+ self.assertTrue(r1 is r2)
519
+
520
+ # Other times residuals need refreshing
521
+ Cs[0].set(x=5)
522
+ r3 = C_result.r
523
+ self.assertTrue(r3 is not r2)
524
+
525
+ # Sometimes derivatives should be cached
526
+ dr1 = C_result.dr_wrt(Cs[1])
527
+ dr2 = C_result.dr_wrt(Cs[1])
528
+ self.assertTrue(dr1 is dr2)
529
+
530
+ # Other times derivatives need refreshing
531
+ Cs[2].set(x=5)
532
+ dr3 = C_result.dr_wrt(Cs[1])
533
+ self.assertTrue(dr3 is not dr2)
534
+
535
+
536
+ def test_scalars(self):
537
+
538
+ try:
539
+ import theano.tensor as T
540
+ from theano import function
541
+ except:
542
+ return
543
+
544
+ # Set up variables and function
545
+ vals = [1, 2, 3, 4, 5]
546
+ f = lambda a, b, c, d, e : a + (b * c) - d ** e
547
+
548
+ # Set up our objects
549
+ Cs = [ch.Ch(v) for v in vals]
550
+ C_result = f(*Cs)
551
+
552
+ # Set up Theano's equivalents
553
+ Ts = T.dscalars('T1', 'T2', 'T3', 'T4', 'T5')
554
+ TF = f(*Ts)
555
+ T_result = function(Ts, TF)
556
+
557
+ # Make sure values and derivatives are equal
558
+ self.assertEqual(C_result.r, T_result(*vals))
559
+ for k in range(len(vals)):
560
+ theano_derivative = function(Ts, T.grad(TF, Ts[k]))(*vals)
561
+ #print C_result.dr_wrt(Cs[k])
562
+ our_derivative = C_result.dr_wrt(Cs[k])[0,0]
563
+ #print theano_derivative, our_derivative
564
+ self.assertEqual(theano_derivative, our_derivative)
565
+
566
+
567
+ def test_vectors(self):
568
+
569
+ try:
570
+ import theano.tensor as T
571
+ from theano import function
572
+ except:
573
+ return
574
+
575
+ for MT in [False, True]:
576
+
577
+ # Set up variables and function
578
+ vals = [np.random.randn(20) for i in range(5)]
579
+ f = lambda a, b, c, d, e : a + (b * c) - d ** e
580
+
581
+ # Set up our objects
582
+ Cs = [ch.Ch(v) for v in vals]
583
+ C_result = f(*Cs)
584
+ C_result.MT = MT
585
+
586
+ # Set up Theano equivalents
587
+ Ts = T.dvectors('T1', 'T2', 'T3', 'T4', 'T5')
588
+ TF = f(*Ts)
589
+ T_result = function(Ts, TF)
590
+
591
+ if False:
592
+ import theano.gradient
593
+ which = 1
594
+ theano_sse = (TF**2.).sum()
595
+ theano_grad = theano.gradient.grad(theano_sse, Ts[which])
596
+ theano_fn = function(Ts, theano_grad)
597
+ print(theano_fn(*vals))
598
+ C_result_grad = ch.SumOfSquares(C_result).dr_wrt(Cs[which])
599
+ print(C_result_grad)
600
+
601
+ # if True:
602
+ # aaa = np.linalg.solve(C_result_grad.T.dot(C_result_grad), C_result_grad.dot(np.zeros(C_result_grad.shape[1])))
603
+ # theano_hes = theano.R_obbb = theano.R_op()
604
+
605
+ import pdb; pdb.set_trace()
606
+
607
+ # Make sure values and derivatives are equal
608
+ np.testing.assert_array_equal(C_result.r, T_result(*vals))
609
+ for k in range(len(vals)):
610
+ theano_derivative = function(Ts, T.jacobian(TF, Ts[k]))(*vals)
611
+ our_derivative = np.array(C_result.dr_wrt(Cs[k]).todense())
612
+ #print theano_derivative, our_derivative
613
+
614
+ # Theano produces has more nans than we do during exponentiation.
615
+ # So we test only on entries where Theano is without NaN's
616
+ without_nans = np.nonzero(np.logical_not(np.isnan(theano_derivative.flatten())))[0]
617
+ np.testing.assert_array_equal(theano_derivative.flatten()[without_nans], our_derivative.flatten()[without_nans])
618
+
619
+
620
+ if __name__ == '__main__':
621
+ unittest.main()
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env python
2
+ # encoding: utf-8
3
+ """
4
+ Author(s): Matthew Loper
5
+
6
+ See LICENCE.txt for licensing and contact information.
7
+ """
8
+
9
+ import unittest
10
+ from .ch import Ch, depends_on
11
+
12
+ class TestInnerComposition(unittest.TestCase):
13
+
14
+ def test_ic(self):
15
+ child = Child(a=Ch(10))
16
+ parent = Parent(child=child, aliased=Ch(50))
17
+
18
+ junk = [parent.aliased_dependency for k in range(3)]
19
+ self.assertTrue(parent.dcount == 1)
20
+ self.assertTrue(parent.ocount == 0)
21
+ self.assertTrue(parent.rcount == 0)
22
+
23
+ junk = [parent.r for k in range(3)]
24
+ self.assertTrue(parent.dcount == 1)
25
+ self.assertTrue(parent.ocount == 1)
26
+ self.assertTrue(parent.rcount == 1)
27
+
28
+ parent.aliased = Ch(20)
29
+ junk = [parent.aliased_dependency for k in range(3)]
30
+ self.assertTrue(parent.dcount == 2)
31
+ self.assertTrue(parent.ocount == 1)
32
+ self.assertTrue(parent.rcount == 1)
33
+
34
+ junk = [parent.r for k in range(3)]
35
+ self.assertTrue(parent.dcount == 2)
36
+ self.assertTrue(parent.ocount == 2)
37
+ self.assertTrue(parent.rcount == 2)
38
+
39
+ class Parent(Ch):
40
+ dterms = ('aliased', 'child')
41
+
42
+ def __init__(self, *args, **kwargs):
43
+ self.dcount = 0
44
+ self.ocount = 0
45
+ self.rcount = 0
46
+
47
+
48
+ def on_changed(self, which):
49
+ assert('aliased' in which and 'child' in which)
50
+ if 'aliased' in which:
51
+ self.ocount += 1
52
+
53
+ @depends_on('aliased')
54
+ def aliased_dependency(self):
55
+ self.dcount += 1
56
+
57
+ @property
58
+ def aliased(self):
59
+ return self.child.a
60
+
61
+ @aliased.setter
62
+ def aliased(self, val):
63
+ self.child.a = val
64
+
65
+ def compute_r(self):
66
+ self.rcount += 1
67
+ return 0
68
+
69
+ def compute_dr_wrt(self, wrt):
70
+ pass
71
+
72
+
73
+ class Child(Ch):
74
+ dterms = ('a',)
75
+
76
+
77
+
78
+ if __name__ == '__main__':
79
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestInnerComposition)
80
+ unittest.TextTestRunner(verbosity=2).run(suite)