def test_debugprint(): x, y, z = matrices("xyz") e = x + y * z op = OpFromGraph([x, y, z], [e]) out = op(x, y, z) output_str = debugprint(out, file="str") lines = output_str.split("\n") exp_res = """OpFromGraph{inline=False} [id A] |x [id B] |y [id C] |z [id D] Inner graphs: OpFromGraph{inline=False} [id A] >Elemwise{add,no_inplace} [id E] > |*0-<TensorType(float64, (None, None))> [id F] > |Elemwise{mul,no_inplace} [id G] > |*1-<TensorType(float64, (None, None))> [id H] > |*2-<TensorType(float64, (None, None))> [id I] """ for truth, out in zip(exp_res.split("\n"), lines): assert truth.strip() == out.strip()
def test_clone(self): x, y, z = matrices("xyz") ofg = OpFromGraph([x], [2 * x]) ofg_clone = ofg.clone() assert ofg_clone.fgraph is not ofg.fgraph assert ofg_clone.fgraph.outputs != ofg.fgraph.outputs assert equal_computations(ofg_clone.fgraph.outputs, ofg.fgraph.outputs)
def test_grad(self, cls_ofg): x, y, z = matrices("xyz") e = x + y * z op = cls_ofg([x, y, z], [e]) f = op(x, y, z) f = f - grad(tt_sum(f), y) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 assert np.all(11.0 == fn(xv, yv, zv))
def test_grad_grad(self, cls_ofg): x, y, z = matrices("xyz") e = x + y * z op = cls_ofg([x, y, z], [e]) f = op(x, y, z) f = f - grad(at_sum(f), y) f = f - grad(at_sum(f), y) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 np.testing.assert_array_almost_equal(6.0, fn(xv, yv, zv), 4)
def test_connection_pattern(self, cls_ofg): # Basic case x, y, z = matrices("xyz") out1 = x * y out2 = y * z op1 = cls_ofg([x, y, z], [out1, out2]) results = op1.connection_pattern(None) expect_result = [[True, False], [True, True], [False, True]] assert results == expect_result # Graph with ops that don't have a 'full' connection pattern # and with ops that have multiple outputs m, n, p, q = matrices("mnpq") o1, o2 = op1(m, n, p) out1, out2 = op1(o1, q, o2) op2 = cls_ofg([m, n, p, q], [out1, out2]) results = op2.connection_pattern(None) expect_result = [[True, False], [True, True], [False, True], [True, True]] assert results == expect_result # Inner graph where some computation doesn't rely on explicit inputs srng = RandomStream(seed=234) rv_u = srng.uniform((2, 2)) x, y = matrices("xy") out1 = x + rv_u out2 = y + 3 out3 = 3 + rv_u op3 = cls_ofg([x, y], [out1, out2, out3]) results = op3.connection_pattern(None) expect_result = [ [True, False, False], [False, True, False], [True, False, True], ] assert results == expect_result
def test_straightforward(self, cls_ofg): x, y, z = matrices("xyz") e = x + y * z op = cls_ofg([x, y, z], [e]) # (1+3*5=array of 16) - (3+1*5=array of 8) f = op(x, y, z) - op(y, z, x) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 assert np.all(8.0 == fn(xv, yv, zv)) assert np.all(8.0 == fn(xv, yv, zv))
def test_valid_input(self): x, y, z = matrices("xyz") with pytest.raises(TypeError): OpFromGraph((x,), (x,)) with pytest.raises(TypeError): OpFromGraph([1], [1]) with pytest.raises(TypeError): OpFromGraph([x, as_tensor(1)], [x]) with pytest.raises(NotImplementedError): OpFromGraph([x], [x], updates={})
def test_size_changes(self, cls_ofg): x, y, z = matrices("xyz") e = dot(x, y) op = cls_ofg([x, y], [e]) f = op(x, op(y, z)) fn = function([x, y, z], f) xv = np.ones((2, 3), dtype=config.floatX) yv = np.ones((3, 4), dtype=config.floatX) * 3 zv = np.ones((4, 5), dtype=config.floatX) * 5 res = fn(xv, yv, zv) assert res.shape == (2, 5) assert np.all(180.0 == res) res = fn(xv, yv, zv) assert res.shape == (2, 5) assert np.all(180.0 == res)
def test_shared(self, cls_ofg): x, y, z = matrices("xyz") s = shared(np.random.rand(2, 2).astype(config.floatX)) e = x + y * z + s op = cls_ofg([x, y, z], [e]) # (1+3*5=array of 16) - (3+1*5=array of 8) f = op(x, y, z) - op(y, z, x) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 # print function, function.__module__ # print fn.maker.fgraph.toposort() assert np.allclose(8.0, fn(xv, yv, zv)) assert np.allclose(8.0, fn(xv, yv, zv))
def test_shared_grad(self, cls_ofg): x, y, z = matrices("xyz") s = shared(np.random.rand(2, 2).astype(config.floatX)) e = x + y * z + s op = cls_ofg([x, y, z], [e]) f = op(x, y, z) f = f - grad(tt_sum(f), y) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 assert np.allclose(11.0 + s.get_value(), fn(xv, yv, zv)) # grad again the shared variable f = op(x, y, z) f = f - grad(tt_sum(f), s) fn = function([x, y, z], f) assert np.allclose(15.0 + s.get_value(), fn(xv, yv, zv))
def test_shared_grad(self, cls_ofg): x, y, z = matrices("xyz") s = shared(np.random.random((2, 2)).astype(config.floatX)) e = x + y * z + s op = cls_ofg([x, y, z], [e]) f = op(x, y, z) f = f - grad(at_sum(f), y) fn = function([x, y, z], f) xv = np.ones((2, 2), dtype=config.floatX) yv = np.ones((2, 2), dtype=config.floatX) * 3 zv = np.ones((2, 2), dtype=config.floatX) * 5 np.testing.assert_array_almost_equal(11.0 + s.get_value(), fn(xv, yv, zv), 4) # grad again the shared variable f = op(x, y, z) f = f - grad(at_sum(f), s) fn = function([x, y, z], f) np.testing.assert_array_almost_equal(15.0 + s.get_value(), fn(xv, yv, zv), 4)
def test_matrix_perform_and_opt(self): m = config.mode m = aesara.compile.get_mode(m) m.check_isfinite = False x, y = matrices("xy") # regular softmax and crossentropy sm = softmax(x) cm = categorical_crossentropy(sm, y) # numerically stable log-softmax with crossentropy logsm = logsoftmax(x) sm2 = exp(logsm) # just used to show equivalence with sm cm2 = -aet_sum(y * logsm, axis=1) grad_node = grad(cm2.mean(), x) # create some inputs into a softmax that are large and labels a = np.exp(10 * np.random.random((5, 10)).astype(config.floatX)) # create some one-hot coded labels b = np.eye(5, 10).astype(config.floatX) # show equivalence of softmax and exponentiated numerically stable # log-softmax f1 = aesara.function([x], [sm, sm2]) sm_, sm2_ = f1(a) utt.assert_allclose(sm_, sm2_) # now show that the two versions result in the same crossentropy cost # this indicates that the forward function does provide some numerical # stability f2 = aesara.function([x, y], [cm, cm2], mode=m) cm_, cm2_ = f2(a, b) utt.assert_allclose(cm_, cm2_) # now, show that in the standard softmax case the gradients blow up # while in the log-softmax case they don't f3 = aesara.function([x, y], [grad_node]) grad_ = f3(a, b) assert not np.any(np.isnan(grad_))
def test_valid_input(self): x, y, z = matrices("xyz") with pytest.raises(ValueError, match="Expected at least.*"): OpFromGraph([x], [x])() with pytest.raises(ValueError, match=r"Expected 1 input\(s\)"): OpFromGraph([x], [x]).make_node() with pytest.raises(TypeError): OpFromGraph((x, ), (x, )) with pytest.raises(TypeError): OpFromGraph([1], [1]) with pytest.raises(TypeError): OpFromGraph([x, as_tensor(1)], [x]) with pytest.raises(TypeError): OpFromGraph([shared(1)], [1]) with pytest.raises(NotImplementedError): OpFromGraph([x], [x], updates={})