def test_opfromgraph(self): # as with the scan tests above, insert foreign inputs into the # inner graph. outer = tensor.scalar("outer") shared = aesara.shared(np.array(1.0, dtype=aesara.config.floatX), name="shared") constant = tensor.constant(1.0, name="constant") z = outer * (shared + constant) # construct the inner graph a = tensor.scalar() b = tensor.scalar() r = a + b r.tag.replacement = z * (a - b) # construct the outer graph c = tensor.scalar() d = tensor.scalar() u = aesara.OpFromGraph([a, b], [r])(c, d) t = z * u (v, ) = map_variables(self.replacer, [t]) t2 = z * v f = aesara.function([c, d, outer], [t, t2]) for m, n in itertools.combinations(range(10), 2): assert f(m, n, outer=0.5) == [m + n, m - n] # test that the unsupported case of replacement with a shared # variable with updates crashes shared.update = shared + 1 with pytest.raises(NotImplementedError): map_variables(self.replacer, [t])
def test_reallocation(): x = tensor.scalar("x") y = tensor.scalar("y") z = tensor.tanh(3 * x + y) + tensor.cosh(x + 5 * y) # The functinality is currently implement for non lazy and non c VM only. for linker in [ vm.VM_Linker(allow_gc=False, lazy=False, use_cloop=False), vm.VM_Linker(allow_gc=True, lazy=False, use_cloop=False), ]: m = aesara.compile.get_mode(aesara.Mode(linker=linker)) m = m.excluding("fusion", "inplace") f = aesara.function([x, y], z, name="test_reduce_memory", mode=m) output = f(1, 2) assert output storage_map = f.fn.storage_map def check_storage(storage_map): from aesara.tensor.var import TensorConstant for i in storage_map: if not isinstance(i, TensorConstant): keys_copy = list(storage_map.keys())[:] keys_copy.remove(i) for o in keys_copy: if storage_map[i][ 0] and storage_map[i][0] is storage_map[o][0]: return [True, storage_map[o][0]] return [False, None] assert check_storage(storage_map)[0] assert len({id(v) for v in storage_map.values()}) < len(storage_map)
def test_nested(): notimpl = NotImplementedOp() ifelseifelseif = IfElseIfElseIf() x1 = tt.scalar("x1") x2 = tt.scalar("x2") c1 = tt.scalar("c1") c2 = tt.scalar("c2") t1 = ifelse(c1, x1, notimpl(x2)) t1.name = "t1" t2 = t1 * 10 t2.name = "t2" t3 = ifelse(c2, t2, x1 + t1) t3.name = "t3" t4 = ifelseifelseif(tt.eq(x1, x2), x1, tt.eq(x1, 5), x2, c2, t3, t3 + 0.5) t4.name = "t4" linker = aesara.gof.vm.VM_Linker(lazy=False) f = function([c1, c2, x1, x2], t4, mode=Mode(linker=linker, optimizer="fast_run")) with pytest.raises(NotImplementedOpException): f(1, 0, np.array(10, dtype=x1.dtype), 0) linker = aesara.gof.vm.VM_Linker(lazy=True) f = function([c1, c2, x1, x2], t4, mode=Mode(linker=linker, optimizer="fast_run")) assert f(1, 0, np.array(10, dtype=x1.dtype), 0) == 20.5
def test_compute_test_value(self): x = tt.scalar("x") x.tag.test_value = np.array(1.0, dtype=config.floatX) op = OpFromGraph([x], [x**3]) y = tt.scalar("y") y.tag.test_value = np.array(1.0, dtype=config.floatX) f = op(y) grad_f = tt.grad(f, y) assert grad_f.tag.test_value is not None
def test_input_named_variables(self): # Tests that named variables work when outputs is a dictionary x = tt.scalar("x") y = tt.scalar("y") f = aesara.function([x, y], outputs={"a": x + y, "b": x * y}) assert f(2, 4) == {"a": 6, "b": 8} assert f(2, y=4) == f(2, 4) assert f(x=2, y=4) == f(2, 4)
def test_leaf(self): a = tensor.scalar("a") b = tensor.scalar("b") c = tensor.scalar("c") b.tag.replacement = c u = a + b (v, ) = map_variables(self.replacer, [u]) assert u.owner.inputs == [a, b] assert v.owner.inputs == [a, c]
def test_leaf_inside_scan(self): x = tensor.vector("x") y = tensor.scalar("y") z = tensor.scalar("z") y.tag.replacement = z s, _ = aesara.scan(lambda x: x * y, sequences=x) (s2, ) = map_variables(self.replacer, [s]) f = aesara.function([x, y, z], [s, s2]) rval = f(x=np.array([1, 2, 3], dtype=np.float32), y=1, z=2) assert np.array_equal(rval, [[1, 2, 3], [2, 4, 6]])
def test_shared_state2(self): a = tt.scalar() # the a is for 'anonymous' (un-named). x, s = tt.scalars("xs") f = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x, mutable=False), ], s + a * x, ) g = function( [x, In(a, value=1.0, name="a"), In(s, value=f.container[s])], s + a * x ) f(1, 2) assert f[s] == 2 assert g[s] == 2 f(1, 2) assert f[s] == 4 assert g[s] == 4 g(1, 2) # has no effect on state assert f[s] == 4 assert g[s] == 4
def test_givens_input_var(self): # Ensure error is raised when trying to replace an input variable. x = tt.scalar("x") y = x * 2 with pytest.raises(RuntimeError): function([x], y, givens={x: x + 1})
def test_disconnected_input(self): a = tt.scalar("a") v = tt.vector("v") with pytest.raises(UnusedInputError): function([a, v], v * 2) function([a, v], v * 2, on_unused_input="ignore")
def test_insert_inplace(self): mySymbolicMatricesList = TypedListType( tt.TensorType(aesara.config.floatX, (False, False)))() mySymbolicIndex = tt.scalar(dtype="int64") mySymbolicMatrix = tt.matrix() z = Insert()(mySymbolicMatricesList, mySymbolicIndex, mySymbolicMatrix) m = aesara.compile.mode.get_default_mode().including( "typed_list_inplace_opt") f = aesara.function( [ In(mySymbolicMatricesList, borrow=True, mutable=True), mySymbolicIndex, mySymbolicMatrix, ], z, accept_inplace=True, mode=m, ) assert f.maker.fgraph.toposort()[0].op.inplace x = rand_ranged(-1000, 1000, [100, 101]) y = rand_ranged(-1000, 1000, [100, 101]) assert np.array_equal(f([x], np.asarray(1, dtype="int64"), y), [x, y])
def test_shared_state0(self): a = tt.scalar() # the a is for 'anonymous' (un-named). x, s = tt.scalars("xs") f = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x, mutable=True), ], s + a * x, ) g = function( [ x, In(a, value=1.0, name="a"), In(s, value=f.container[s], update=s - a * x, mutable=True), ], s + a * x, ) f(1, 2) assert f[s] == 2 assert g[s] == 2 g(1, 2) assert f[s] == 0 assert g[s] == 0
def test_copy(self): a = tt.scalar() # the a is for 'anonymous' (un-named). x, s = tt.scalars("xs") f = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x, mutable=True), ], s + a * x, ) g = copy.copy(f) # if they both return, assume that they return equivalent things. assert g.container[x].storage is not f.container[x].storage assert g.container[a].storage is not f.container[a].storage assert g.container[s].storage is not f.container[s].storage assert g.value[a] is f.value[a] # should not have been copied assert ( g.value[s] is not f.value[s] ) # should have been copied because it is mutable. assert not (g.value[s] != f.value[s]).any() # its contents should be identical assert f(2, 1) == g( 2 ) # they should be in sync, default value should be copied. assert f(2, 1) == g( 2 ) # they should be in sync, default value should be copied. f(1, 2) # put them out of sync assert f(1, 2) != g(1, 2) # they should not be equal anymore.
def test_CheckAndRaise_basic_c(linker): exc_msg = "this is the exception" check_and_raise = CheckAndRaise(CustomException, exc_msg) conds = at.scalar() y = check_and_raise(at.as_tensor(1), conds) y_fn = aesara.function([conds], y, mode=Mode(linker)) with pytest.raises(CustomException, match=exc_msg): y_fn(0) x = at.vector() y = check_and_raise(x, conds) y_fn = aesara.function([conds, x], y.shape, mode=Mode(linker, OPT_FAST_RUN)) x_val = np.array([1.0], dtype=aesara.config.floatX) assert np.array_equal(y_fn(0, x_val), x_val) y = check_and_raise(x, at.as_tensor(0)) y_grad = aesara.grad(y.sum(), [x]) y_fn = aesara.function([x], y_grad, mode=Mode(linker, OPT_FAST_RUN)) assert np.array_equal(y_fn(x_val), [x_val])
def test_ifelse(): a = tt.scalar() b = generic() c = generic() notimpl = NotImplementedOp() lazys = [True] # We need lazy to end up being True for this test. if aesara.config.vm.lazy in [True, None]: lazys = [True, None] cloops = [True, False] if aesara.config.cxx == "": cloops = [False] for cloop in cloops: for lazy in lazys: linker = aesara.gof.vm.VM_Linker(use_cloop=cloop, lazy=lazy) f = function( [a, b, c], ifelse(a, notimpl(b), c), mode=Mode(linker=linker, optimizer="fast_run"), ) with pytest.raises(NotImplementedOpException): f(1, "a", "b") assert f(0, "a", "b") == "b"
def __init__(self): a = tt.scalar() # the a is for 'anonymous' (un-named). x, s = tt.scalars("xs") v = tt.vector("v") self.s = s self.x = x self.v = v self.e = a * x + s self.f1 = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x, mutable=True), ], s + a * x, ) self.f2 = function( [ x, In(a, value=1.0, name="a"), In(s, value=self.f1.container[s], update=s + a * x, mutable=True), ], s + a * x, )
def test_scan(self): x = tensor.vector("x") # we will insert a subgraph involving these variables into the inner # graph of scan. since they were not previously in the inner graph, # they are like non_sequences to scan(). scan() infers these and # imports them into the inner graph properly, and map_variables() # should do this as well. outer = tensor.scalar("outer") shared = aesara.shared(np.array(1.0, dtype=aesara.config.floatX), name="shared") constant = tensor.constant(1, name="constant") # z will equal 1 so multiplying by it doesn't change any values z = outer * (shared + constant) def step(x, a): r = a + x r.tag.replacement = z * (a - x) return r s, _ = aesara.scan(step, sequences=x, outputs_info=[np.array(0.0)]) # ensure z is owned by the outer graph so map_variables() will need to # jump through additional hoops to placate FunctionGraph. t = z * s (s2, ) = map_variables(self.replacer, [t]) t2 = z * s2 f = aesara.function([x, outer], [t, t2]) rval = f(x=np.array([1, 2, 3], dtype=np.float32), outer=0.5) assert np.array_equal(rval, [[1, 3, 6], [-1, -3, -6]])
def test_jacobian_disconnected_inputs(): # Test that disconnected inputs are properly handled by jacobian. v1 = tensor.vector() v2 = tensor.vector() jacobian_v = aesara.gradient.jacobian(1 + v1, v2, disconnected_inputs="ignore") func_v = aesara.function([v1, v2], jacobian_v) val = np.arange(4.0).astype(aesara.config.floatX) assert np.allclose(func_v(val, val), np.zeros((4, 4))) s1 = tensor.scalar() s2 = tensor.scalar() jacobian_s = aesara.gradient.jacobian(1 + s1, s2, disconnected_inputs="ignore") func_s = aesara.function([s2], jacobian_s) val = np.array(1.0).astype(aesara.config.floatX) assert np.allclose(func_s(val), np.zeros(1))
def test_draw_value(): npt.assert_equal(_draw_value(np.array([5, 6])), [5, 6]) npt.assert_equal(_draw_value(np.array(5.0)), 5) npt.assert_equal(_draw_value(aet.constant([5.0, 6.0])), [5, 6]) assert _draw_value(aet.constant(5)) == 5 npt.assert_equal(_draw_value(2 * aet.constant([5.0, 6.0])), [10, 12]) val = aesara.shared(np.array([5.0, 6.0])) npt.assert_equal(_draw_value(val), [5, 6]) npt.assert_equal(_draw_value(2 * val), [10, 12]) a = aet.scalar("a") a.tag.test_value = 6 npt.assert_equal(_draw_value(2 * a, givens=[(a, 1)]), 2) assert _draw_value(5) == 5 assert _draw_value(5.0) == 5 assert isinstance(_draw_value(5.0), type(5.0)) assert isinstance(_draw_value(5), type(5)) with pm.Model(): mu = 2 * aet.constant(np.array([5.0, 6.0])) + aesara.shared( np.array(5)) a = pm.Normal("a", mu=mu, sigma=5, shape=2) val1 = _draw_value(a) val2 = _draw_value(a) assert np.all(val1 != val2) with pytest.raises(ValueError) as err: _draw_value([]) err.match("Unexpected type")
def test_make_node_shared(self): """Make sure we can provide `OpFromGraph.make_node` new shared inputs and get a valid `OpFromGraph`.""" x = at.scalar("x") y = shared(1.0, name="y") test_ofg = OpFromGraph([x], [x + y], on_unused_input="ignore") assert test_ofg.shared_inputs == [y] out = test_ofg(x) y_clone = y.clone() assert y_clone != y y_clone.name = "y_clone" out_new = test_ofg.make_node(*(out.owner.inputs[:1] + [y_clone])).outputs[0] assert "on_unused_input" in out_new.owner.op.kwargs assert out_new.owner.op.shared_inputs == [y_clone] out_fn = function([x], out_new) assert np.array_equal(out_fn(1.0), 2.0) y_clone.set_value(2.0) assert np.array_equal(out_fn(1.0), 3.0)
def test_wrong_coefficient_matrix(self): x = tensor.vector() y = tensor.vector() z = tensor.scalar() b = aesara.tensor.nlinalg.lstsq()(x, y, z) f = function([x, y, z], b) with pytest.raises(np.linalg.linalg.LinAlgError): f([2, 1], [2, 1], 1)
def test_get_jaxified_graph(): # Check that jaxifying a graph does not emmit the Supervisor Warning. This test can # be removed once https://github.com/aesara-devs/aesara/issues/637 is sorted. x = at.scalar("x") y = at.exp(x) with pytest.warns(None) as record: fn = get_jaxified_graph(inputs=[x], outputs=[y]) assert not record assert np.isclose(fn(0), 1)
def test_empty_givens_updates(): # Regression test for bug fixed in 8625e03. # Empty givens / updates dictionaries were not properly detected before, # triggering useless crashes at compile time. x = tt.scalar() y = x * 2 function([aesara.In(x)], y, givens={}) function([aesara.In(x)], y, updates={})
def test_repeatOp(self): for ndim in [1, 3]: x = tt.TensorType(config.floatX, [False] * ndim)() a = np.random.random((10, ) * ndim).astype(config.floatX) for axis in self._possible_axis(ndim): for dtype in tt.integer_dtypes: r_var = tt.scalar(dtype=dtype) r = np.asarray(3, dtype=dtype) if dtype == "uint64" or (dtype in self.numpy_unsupported_dtypes and r_var.ndim == 1): with pytest.raises(TypeError): repeat(x, r_var, axis=axis) else: f = aesara.function([x, r_var], repeat(x, r_var, axis=axis)) assert np.allclose(np.repeat(a, r, axis=axis), f(a, r)) r_var = tt.vector(dtype=dtype) if axis is None: r = np.random.randint(1, 6, size=a.size).astype(dtype) else: r = np.random.randint(1, 6, size=(10, )).astype(dtype) if dtype in self.numpy_unsupported_dtypes and r_var.ndim == 1: with pytest.raises(TypeError): repeat(x, r_var, axis=axis) else: f = aesara.function([x, r_var], repeat(x, r_var, axis=axis)) assert np.allclose(np.repeat(a, r, axis=axis), f(a, r)) # check when r is a list of single integer, e.g. [3]. r = np.random.randint(1, 11, size=()).astype(dtype) + 2 f = aesara.function([x], repeat(x, [r], axis=axis)) assert np.allclose(np.repeat(a, r, axis=axis), f(a)) assert not np.any([ isinstance(n.op, RepeatOp) for n in f.maker.fgraph.toposort() ]) # check when r is aesara tensortype that broadcastable is (True,) r_var = aesara.tensor.TensorType( broadcastable=(True, ), dtype=dtype)() r = np.random.randint(1, 6, size=(1, )).astype(dtype) f = aesara.function([x, r_var], repeat(x, r_var, axis=axis)) assert np.allclose(np.repeat(a, r[0], axis=axis), f(a, r)) assert not np.any([ isinstance(n.op, RepeatOp) for n in f.maker.fgraph.toposort() ])
def test4(self): a = tensor.dmatrix() axis = tensor.scalar() l = sort(a, axis, "mergesort") f = aesara.function([a, axis], l) for axis_val in 0, 1: gv = f(self.m_val, axis_val) gt = np.sort(self.m_val, axis_val) utt.assert_allclose(gv, gt)
def check_partial_function(linker_name): x = tensor.scalar("input") y = x**2 f = aesara.function([x], [y + 7, y - 9, y / 14.0], mode=Mode(optimizer=None, linker=linker_name)) assert f(3, output_subset=[0, 1, 2]) == f(3) assert f(4, output_subset=[0, 2]) == [f(4)[0], f(4)[2]] utt.assert_allclose(f(5), np.array([32.0, 16.0, 1.7857142857142858]))
def t(): f = function( [ In(a, name={"adsf", ()}, value=1.0), In(x, name=(), value=2.0), In(s, name=tt.scalar(), value=3.0), ], a + x + s, ) return f
def check_partial_function_output_keys(linker_name): x = tensor.scalar("input") y = 3 * x f = aesara.function([x], { "a": y * 5, "b": y - 7 }, mode=Mode(optimizer=None, linker=linker_name)) assert f(5, output_subset=["a"])["a"] == f(5)["a"]
def test_deepcopy(self): a = tt.scalar() # the a is for 'anonymous' (un-named). x, s = tt.scalars("xs") f = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x, mutable=True), ], s + a * x, ) try: g = copy.deepcopy(f) except NotImplementedError as e: if e[0].startswith("DebugMode is not picklable"): return else: raise # if they both return, assume that they return equivalent things. # print [(k,id(k)) for k in f.finder.keys()] # print [(k,id(k)) for k in g.finder.keys()] assert g.container[0].storage is not f.container[0].storage assert g.container[1].storage is not f.container[1].storage assert g.container[2].storage is not f.container[2].storage assert x not in g.container assert x not in g.value assert len(f.defaults) == len(g.defaults) assert f._check_for_aliased_inputs is g._check_for_aliased_inputs assert f.name == g.name assert f.maker.fgraph.name == g.maker.fgraph.name # print 'f.defaults = %s' % (f.defaults, ) # print 'g.defaults = %s' % (g.defaults, ) for ((f_req, f_feed, f_val), (g_req, g_feed, g_val)) in zip( f.defaults, g.defaults ): assert f_req == g_req and f_feed == g_feed and f_val == g_val assert g.value[1] is not f.value[1] # should not have been copied assert ( g.value[2] is not f.value[2] ) # should have been copied because it is mutable. assert not (g.value[2] != f.value[2]).any() # its contents should be identical assert f(2, 1) == g( 2 ) # they should be in sync, default value should be copied. assert f(2, 1) == g( 2 ) # they should be in sync, default value should be copied. f(1, 2) # put them out of sync assert f(1, 2) != g(1, 2) # they should not be equal anymore. g(1, 2) # put them back in sync assert f(3) == g(3) # They should be in sync again.
def test_compile_pymc_custom_update_op(self): """Test that custom MeasurableVariable Op updates are used by compile_pymc""" class UnmeasurableOp(OpFromGraph): def update(self, node): return {node.inputs[0]: node.inputs[0] + 1} dummy_inputs = [at.scalar(), at.scalar()] dummy_outputs = [at.add(*dummy_inputs)] dummy_x = UnmeasurableOp(dummy_inputs, dummy_outputs)(aesara.shared(1.0), 1.0) # Check that there are no updates at first fn = compile_pymc(inputs=[], outputs=dummy_x) assert fn() == fn() == 2.0 # And they are enabled once the Op is registered as Measurable MeasurableVariable.register(UnmeasurableOp) fn = compile_pymc(inputs=[], outputs=dummy_x) assert fn() == 2.0 assert fn() == 3.0