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/__init__.py +117 -0
- chumpy/api_compatibility.py +534 -0
- chumpy/ch.py +1367 -0
- chumpy/ch_ops.py +814 -0
- chumpy/ch_random.py +32 -0
- chumpy/extras.py +72 -0
- chumpy/linalg.py +306 -0
- chumpy/logic.py +39 -0
- chumpy/monitor.py +149 -0
- chumpy/np_tensordot.py +228 -0
- chumpy/optimization.py +161 -0
- chumpy/optimization_internal.py +455 -0
- chumpy/reordering.py +454 -0
- chumpy/test_ch.py +621 -0
- chumpy/test_inner_composition.py +80 -0
- chumpy/test_linalg.py +272 -0
- chumpy/test_optimization.py +204 -0
- chumpy/testing.py +21 -0
- chumpy/utils.py +93 -0
- chumpy/version.py +3 -0
- chumpy_fixed-0.71.dist-info/METADATA +31 -0
- chumpy_fixed-0.71.dist-info/RECORD +25 -0
- chumpy_fixed-0.71.dist-info/WHEEL +5 -0
- chumpy_fixed-0.71.dist-info/licenses/LICENSE.txt +22 -0
- chumpy_fixed-0.71.dist-info/top_level.txt +1 -0
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)
|