def test_reallocation(): x = scalar("x") y = scalar("y") z = tanh(3 * x + y) + cosh(x + 5 * y) # The functinality is currently implement for non lazy and non c VM only. for linker in [ VMLinker(allow_gc=False, lazy=False, use_cloop=False), VMLinker(allow_gc=True, lazy=False, use_cloop=False), ]: m = get_mode(Mode(linker=linker)) m = m.excluding("fusion", "inplace") f = 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): 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_no_leak_many_call_nonlazy(): # Verify no memory leaks when calling a function a lot of times # This isn't really a unit test, you have to run it and look at top to # see if there's a leak. def build_graph(x, depth=5): z = x for d in range(depth): z = sin(-z + 1) return z def time_linker(name, linker): steps_a = 10 x = dvector() a = build_graph(x, steps_a) f_a = function([x], a, mode=Mode(optimizer=None, linker=linker())) inp = np.random.rand(1000000) for i in range(500): f_a(inp) print(1) time_linker("vmLinker_C", lambda: VMLinker(allow_gc=False, use_cloop=True)) print(2) time_linker("vmLinker", lambda: VMLinker(allow_gc=False, use_cloop=False))
def test_no_recycling(): x = vector() for lnk in [ VMLinker(use_cloop=True), VMLinker(use_cloop=False, lazy=True), VMLinker(use_cloop=False, lazy=False, allow_gc=True), VMLinker(use_cloop=False, lazy=False, allow_gc=False), ]: mode = Mode(optimizer="fast_compile", linker=lnk) f = function([x], x + 1, mode=mode) f2 = function([x], (x + 1) * 2, mode=mode) m1 = f.fn.thunks[0].thunk.module m2 = f2.fn.thunks[0].thunk.module assert m1 is m2
def test_partial_function_with_updates(): def check_updates(linker_name): x = lscalar("input") y = shared(np.asarray(1, "int64"), name="global") f = function( [x], [x, x + 34], updates=[(y, x + 1)], mode=Mode(optimizer=None, linker=linker_name), ) g = function( [x], [x - 6], updates=[(y, y + 3)], mode=Mode(optimizer=None, linker=linker_name), ) assert f(3, output_subset=[]) == [] assert y.get_value() == 4 assert g(30, output_subset=[0]) == [24] assert g(40, output_subset=[]) == [] assert y.get_value() == 10 check_updates(VMLinker(allow_partial_eval=True, use_cloop=False)) check_updates("cvm")
def test_VMLinker_make_vm_cvm(): # We don't want this at module level, since CXX might not be present from aesara.link.c.cvm import CVM a = scalar() linker = VMLinker(allow_gc=False, use_cloop=True) f = function([a], a, mode=Mode(optimizer=None, linker=linker)) assert isinstance(f.fn, CVM)
def test_callback_with_ifelse(self): a, b, c = scalars("abc") f = function( [a, b, c], ifelse(a, 2 * b, 2 * c), mode=Mode(optimizer=None, linker=VMLinker(callback=self.callback)), ) f(1, 2, 3) assert self.n_callbacks["IfElse"] == 2
def test_vm_gc(): x = vector() p = RunOnce()(x) mode = Mode(linker=VMLinker(lazy=True)) f = function([In(x, mutable=True)], [p + 1, p + 2], mode=mode) f([1, 2, 3]) p = RunOnce()(x) pp = p + p f = function([x], [pp + pp], mode=mode) f([1, 2, 3])
def test_speed_lazy(): # TODO FIXME: This isn't a real test. def build_graph(x, depth=5): z = x for d in range(depth): z = ifelse(z[0] > 0, -z, z) return z def time_linker(name, linker): steps_a = 10 steps_b = 100 x = vector() a = build_graph(x, steps_a) b = build_graph(x, steps_b) f_a = function([x], a, mode=Mode(optimizer=None, linker=linker())) f_b = function([x], b, mode=Mode(optimizer=None, linker=linker())) f_a([2.0]) t0 = time.time() f_a([2.0]) t1 = time.time() f_b([2.0]) t2 = time.time() f_b([2.0]) t3 = time.time() t_a = t1 - t0 t_b = t3 - t2 print( f"{name} takes {1000 * (t_b - t_a) / (steps_b - steps_a):f} s/Kop") time_linker("vmLinker", VMLinker) time_linker("vmLinker_nogc", lambda: VMLinker(allow_gc=False)) if config.cxx: time_linker("vmLinker_C", lambda: VMLinker(allow_gc=False, use_cloop=True))
def test_callback(self): a, b, c = scalars("abc") f = function( [a, b, c], (a + b) + c, mode=Mode(optimizer=None, linker=VMLinker(callback=self.callback)), ) f(1, 2, 3) assert sum(self.n_callbacks.values()) == len(f.maker.fgraph.toposort()) f(1, 2, 3) assert sum( self.n_callbacks.values()) == len(f.maker.fgraph.toposort()) * 2
def test_partial_function_with_output_keys(): def check_partial_function_output_keys(linker_name): x = scalar("input") y = 3 * x f = 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"] check_partial_function_output_keys( VMLinker(allow_partial_eval=True, use_cloop=False)) check_partial_function_output_keys("cvm")
def test_no_leak_many_call_lazy(): # Verify no memory leaks when calling a function a lot of times # This isn't really a unit test, you have to run it and look at top to # see if there's a leak def build_graph(x, depth=5): z = x for d in range(depth): z = ifelse(z.mean() > 0.5, -z, z) return z def time_linker(name, linker): steps_a = 10 x = dvector() a = build_graph(x, steps_a) f_a = function([x], a, mode=Mode(optimizer=None, linker=linker())) inp = np.random.rand(1000000) for i in range(100): f_a(inp) # this doesn't seem to work, prints 0 for everything # import resource # # pre = resource.getrusage(resource.RUSAGE_SELF) # post = resource.getrusage(resource.RUSAGE_SELF) # print(pre.ru_ixrss, post.ru_ixrss) # print(pre.ru_idrss, post.ru_idrss) # print(pre.ru_maxrss, post.ru_maxrss) print(1) time_linker("vmLinker_C", lambda: VMLinker(allow_gc=False, use_cloop=True)) print(2) time_linker("vmLinker", lambda: VMLinker(allow_gc=False, use_cloop=False))
def test_vm_gc(): # This already caused a bug in the trunk of Aesara. # # The bug was introduced in the trunk on July 5th, 2012 and fixed on # July 30th. x = vector() p = RunOnce()(x) mode = Mode(linker=VMLinker(lazy=True)) f = function([In(x, mutable=True)], [p + 1, p + 2], mode=mode) f([1, 2, 3]) p = RunOnce()(x) pp = p + p f = function([x], [pp + pp], mode=mode) f([1, 2, 3])
def test_partial_function(): from tests import unittest_tools as utt def check_partial_function(linker_name): x = scalar("input") y = x**2 f = 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])) check_partial_function(VMLinker(allow_partial_eval=True, use_cloop=False)) if not config.cxx: pytest.skip("Need cxx for this test") check_partial_function("cvm")
def test_VMLinker_exception(): class BadOp(Op): def perform(self, node, inputs, outputs): pass def make_node(self, x): return Apply(self, [x], [x.type()]) def make_thunk(self, *args, **kwargs): raise Exception("bad Op") a = scalar() linker = VMLinker(allow_gc=False, use_cloop=True) z = BadOp()(a) with pytest.raises(Exception, match=".*Apply node that caused the error.*"): function([a], z, mode=Mode(optimizer=None, linker=linker))
def test_use_c_thunks(): a_at = scalars("a") b_at = vectors("b") a = np.array(0.0).astype(config.floatX) b = np.array([2.0]).astype(config.floatX) cases = [False] if config.cxx: cases.append(True) for use_c_thunks in cases: f = function( [a_at, b_at], a_at * b_at, mode=Mode(optimizer=None, linker=VMLinker(c_thunks=use_c_thunks, use_cloop=False)), ) assert np.array_equal(a * b, f(a, b)) assert any([hasattr(t, "cthunk") for t in f.fn.thunks]) == use_c_thunks
def test_VMLinker_make_vm_no_cvm(): from importlib import reload from unittest.mock import patch with config.change_flags(cxx=""): # Make sure that GXX isn't present with pytest.raises(MissingGXX): import aesara.link.c.cvm reload(aesara.link.c.cvm) # Make sure that `cvm` module is missing with patch.dict("sys.modules", {"aesara.link.c.cvm": None}): a = scalar() linker = VMLinker(allow_gc=False, use_cloop=True) with pytest.raises(ModuleNotFoundError): import aesara.link.c.cvm f = function([a], a, mode=Mode(optimizer=None, linker=linker)) assert isinstance(f.fn, Loop)
class TestFunction: @pytest.mark.xfail() def test_none(self): fn = function([], None) # ok rval = fn() assert ( rval != [] ), "See #254: Using None as function output leads to [] return value" assert rval is None def test_empty(self): fn = function([], []) # ok assert fn() == [] def test_extra_inputs(self): x, s = scalars("xs") fn = function([x], [x]) with pytest.raises(TypeError): fn(1, 2) def test_missing_inputs(self): def fn(): x, s = scalars("xs") function([], [x]) with pytest.raises(MissingInputError): fn() def fn(): x, s = scalars("xs") # Ignore unused input s, as it hides the other error function([s], [x], on_unused_input="ignore") with pytest.raises(MissingInputError): fn() def fn(): x, s = scalars("xs") function([s], [x]) with pytest.raises(UnusedInputError): fn() def fn(): x, s = scalars("xs") # Ignore unused input s, as it hides the other error function([s], x, on_unused_input="ignore") with pytest.raises(MissingInputError): fn() def fn(): x, s = scalars("xs") function([s], x) with pytest.raises(UnusedInputError): fn() def fn(): x, s = scalars("xs") # Ignore unused input s, as it hides the other error function([s], Out(x), on_unused_input="ignore") with pytest.raises(MissingInputError): fn() def fn(): x, s = scalars("xs") function([s], Out(x)) with pytest.raises(UnusedInputError): fn() def fn(): x, s = scalars("xs") function([In(x, update=s + x)], x) with pytest.raises(MissingInputError): fn() def fn(): x, s = scalars("xs") function([In(x, update=((s * s) + x))], x) with pytest.raises(MissingInputError): fn() def test_input_anon_singleton(self): x, s = scalars("xs") fn = function([s, x], [x + s]) assert fn(2, 3) == [5] # no state assert fn(2, 3) == [5] def test_input_anon_unpack(self): x, s = scalars("xs") fn = function([s, x], x + s) assert fn(2, 3) == 5 def test_naming_rule0(self): x, s = scalars("xs") f = function([x, s], x / s) assert f(1, 2) == 0.5 assert f(2, 1) == 2.0 assert f(s=2, x=1) == 0.5 assert f(x=2, s=1) == 2.0 assert f(2, s=1) == 2.0 with pytest.raises(TypeError): # got multiple values for keyword argument 'x' f(2, x=2.0) with pytest.raises(TypeError): # takes exactly 2 non-keyword arguments (1 given) f(x=1) with pytest.raises(TypeError): # takes exactly 2 non-keyword arguments (0 given) f(s=1) def test_naming_rule1(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = scalars("xs") f = function([a, s], a / s) assert f(1, 2) == 0.5 assert f(2, 1) == 2.0 assert f(2, s=1) == 2.0 with pytest.raises(TypeError): # got unexpected keyword argument 'q' f(q=2, s=1) with pytest.raises(TypeError): # got unexpected keyword argument 'a' f(a=2, s=1) def test_naming_rule2(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = scalars("xs") # x's name is ignored because it is followed by anonymous parameter a. # Ignore unused input x, as it hides the other error f = function([x, a, s], a / s, on_unused_input="ignore") assert f(9, 1, 2) == 0.5 assert f(9, 2, 1) == 2.0 assert f(9, 2, s=1) == 2.0 with pytest.raises(TypeError): # got unexpected keyword argument 'x' f(x=9, a=2, s=1) with pytest.raises(TypeError): # got unexpected keyword argument 'x' f(5.0, x=9) def test_naming_rule3(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = scalars("xs") # x's name is not ignored (as in test_naming_rule2) because a has a default value. f = function([x, In(a, value=1.0), s], a / s + x) assert f(9, 2, 4) == 9.5 # can specify all args in order assert f(9, 2, s=4) == 9.5 # can give s as kwarg assert f(9, s=4) == 9.25 # can give s as kwarg, get default a assert f(x=9, s=4) == 9.25 # can give s as kwarg, omit a, x as kw with pytest.raises(TypeError): # got unexpected keyword argument 'a' f(x=9, a=2, s=4) with pytest.raises(TypeError): # takes exactly 3 non-keyword arguments (0 given) f() with pytest.raises(TypeError): # takes exactly 3 non-keyword arguments (1 given) f(x=9) def test_naming_rule4(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = scalars("xs") f = function([x, In(a, value=1.0, name="a"), s], a / s + x) assert f(9, 2, 4) == 9.5 # can specify all args in order assert f(9, 2, s=4) == 9.5 # can give s as kwarg assert f(9, s=4) == 9.25 # can give s as kwarg, get default a assert f(9, a=2, s=4) == 9.5 # can give s as kwarg, a as kwarg assert f(x=9, a=2, s=4) == 9.5 # can give all kwargs assert f(x=9, s=4) == 9.25 # can give all kwargs with pytest.raises(TypeError): # takes exactly 3 non-keyword arguments (0 given) f() with pytest.raises(TypeError): # got multiple values for keyword argument 'x' f(5.0, x=9) @pytest.mark.parametrize( "mode", [ Mode( linker=VMLinker(allow_gc=True, use_cloop=False, c_thunks=False), optimizer="fast_compile", ), Mode( linker=VMLinker(allow_gc=True, use_cloop=False, c_thunks=False), optimizer="fast_run", ), Mode(linker="cvm", optimizer="fast_compile"), Mode(linker="cvm", optimizer="fast_run"), ], ) def test_state_access(self, mode): a = scalar() x, s = scalars("xs") f = function( [ x, In(a, value=1.0, name="a"), In(s, value=0.0, update=s + a * x) ], s + a * x, mode=mode, ) assert f[a] == 1.0 assert f[s] == 0.0 assert f(3.0) == 3.0 assert f[s] == 3.0 assert f(3.0, a=2.0) == 9.0 # 3.0 + 2*3.0 assert ( f[a] == 1.0 ) # state hasn't changed permanently, we just overrode it last line assert f[s] == 9.0 f[a] = 5.0 assert f[a] == 5.0 assert f(3.0) == 24.0 # 9 + 3*5 assert f[s] == 24.0 def test_same_names(self): a, x, s = scalars("xxx") # implicit names would cause error. What do we do? f = function([a, x, s], a + x + s) assert f(1, 2, 3) == 6 with pytest.raises(TypeError): f(1, 2, x=3) def test_weird_names(self): a, x, s = scalars("xxx") with pytest.raises(TypeError): function([In(a, name=[])], []) def t(): f = function( [ In(a, name={"adsf", ()}, value=1.0), In(x, name=(), value=2.0), In(s, name=scalar(), value=3.0), ], a + x + s, ) return f with pytest.raises(TypeError): t() def test_copy(self): a = scalar() x, s = 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) assert f.unpack_single == g.unpack_single assert f.trust_input == g.trust_input 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 # Should not have been copied assert g.value[a] is f.value[a] # Should have been copied because it is mutable assert g.value[s] is not f.value[s] # Their contents should be equal, though assert np.array_equal(g.value[s], f.value[s]) # They should be in sync, default value should be copied assert np.array_equal(f(2, 1), g(2)) # They should be in sync, default value should be copied assert np.array_equal(f(2, 1), g(2)) # Put them out of sync f(1, 2) # They should not be equal anymore assert not np.array_equal(f(1, 2), g(1, 2)) def test_copy_share_memory(self): x = fscalar("x") # SharedVariable for tests, one of them has update y = shared(value=1) z = shared(value=2) out = tanh((x + y + 2) / (x + z - 0.2)**2) # Test for different linkers for mode in ("FAST_RUN", "FAST_COMPILE"): ori = function([x], [out], mode=mode, updates={z: z + 1}) cpy = ori.copy(share_memory=True) # Test if memories shared storage_map_ori = ori.vm.storage_map storage_map_cpy = cpy.vm.storage_map fgraph_cpy = cpy.maker.fgraph # Assert intermediate and Constants storages are shared. # and output stoarges are not shared i_o_variables = fgraph_cpy.inputs + fgraph_cpy.outputs ori_storages = storage_map_ori.values() l = [ val for key, val in storage_map_cpy.items() if key not in i_o_variables or isinstance(key, Constant) ] for storage in l: assert any(storage is s for s in ori_storages) # Assert storages of SharedVariable without updates are shared for (input, _1, _2), here, there in zip(ori.indices, ori.input_storage, cpy.input_storage): assert here.data is there.data def test_swap_SharedVariable(self): i = iscalar() x_list = shared(value=np.random.random((10, )).astype(config.floatX)) x = scalar("x") # SharedVariable for tests, one of them has update y = shared(value=1, name="y") z = shared(value=2, name="z") m = shared(value=0, name="m") # SharedVariable to replace y_rpl = shared(value=3, name="y_rpl") z_rpl = shared(value=4, name="z_rpl") swap = {y: y_rpl, z: z_rpl} map_SV = {"y_rpl": y_rpl, "z_rpl": z_rpl} out = x + y + z + m # Test for different linkers # for mode in ["FAST_RUN","FAST_COMPILE"]: second_time = False for mode in ("FAST_RUN", "FAST_COMPILE"): ori = function( [i], [out], mode=mode, updates=[(z, z + 1), (m, m + 2)], givens={x: x_list[i]}, ) cpy = ori.copy(swap=swap) # run function several times ori(1), cpy(1), cpy(2) # assert same SharedVariable are update in different function if not second_time: # m should be updated 3 times assert m.get_value() == 6 # z should be updated once assert z.get_value() == 3 # z_rpl should be updated twice assert z_rpl.get_value() == 6 # y and y_rpl should not be updated assert y_rpl.get_value() == 3 assert y.get_value() == 1 elif second_time: # doule update for sharedvariable assert m.get_value() == 12 assert z.get_value() == 4 assert z_rpl.get_value() == 8 assert y_rpl.get_value() == 3 # test cpy function: # 2. SharedVariable is updatable -> values did update(z == 5) # 1. sharedvariable is swap -> Rpl sharedvariables share storage names = map_SV.keys() for key in cpy.vm.storage_map: if key.name in names: assert (map_SV[key.name].container.storage[0] == cpy.vm.storage_map[key][0]) second_time = True def test_swap_SharedVariable_with_given(self): # A special testcase for logistic_sgd.py in Deep Learning Tutorial # This test assert that SharedVariable in different function have same storage train_x = shared(value=np.random.random((10, 10)).astype(config.floatX)) test_x = shared(value=np.random.random((10, 10)).astype(config.floatX)) train_y = shared(value=np.random.random((10, 1)).astype(config.floatX)) test_y = shared(value=np.random.random((10, 1)).astype(config.floatX)) i = iscalar("index") x = vector("x") y = vector("y") # this formular has no sense but for a test out = (at_sum(x) - y)**2 train = function( [i], out, givens={ x: train_x[i], y: train_y[i] }, updates={train_x: train_x + 0.1}, ) test_def = function([i], out, givens={x: test_x[i], y: test_y[i]}) test_cpy = train.copy(swap={ train_x: test_x, train_y: test_y }, delete_updates=True) for in1, in2 in zip(test_def.maker.inputs, test_cpy.maker.inputs): assert in1.value is in2.value def test_copy_delete_updates(self): w = iscalar("w") x = fscalar("x") # SharedVariable for tests, one of them has update y = shared(value=1, name="y") z = shared(value=2, name="z") out = x + y + z # Test for different linkers # for mode in ["FAST_RUN","FAST_COMPILE"]: # second_time = False for mode in ("FAST_RUN", "FAST_COMPILE"): ori = function([x], out, mode=mode, updates={z: z * 2}) cpy = ori.copy(delete_updates=True) assert cpy(1) == 4 assert cpy(1) == 4 assert cpy(1) == 4 # Test if unused implicit and explicit inputs from delete_updates # are ignored as intended. for mode in ("FAST_RUN", "FAST_COMPILE"): ori = function([x], x, mode=mode, updates={z: z * 2}) cpy = ori.copy(delete_updates=True) ori = function([x, w], x, mode=mode, updates={z: z + w}) cpy = ori.copy(delete_updates=True) def test_shared_state0(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = 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_shared_state1(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = 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])], s + a * x) f(1, 2) assert f[s] == 2 assert g[s] == 2 f(1, 2) g(1, 2) assert f[s] == 4 assert g[s] == 4 def test_shared_state2(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = 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_shared_state_not_implicit(self): # This test is taken from the documentation in # doc/topics/function.txt. If it does not pass anymore and yet the # behavior is still intended the doc and the test should both be # updated accordingly. x, s = scalars("xs") inc = function([x, In(s, update=(s + x), value=10.0)], []) dec = function( [x, In(s, update=(s - x), value=inc.container[s], implicit=False)], []) assert dec[s] is inc[s] inc[s] = 2 assert dec[s] == 2 dec(1) assert inc[s] == 1 dec(1, 0) assert inc[s] == -1 assert dec[s] == -1 def test_constant_output(self): # Test that if the output is a constant, we respect the aesara memory interface f = function([], at.constant([4])) # print f.maker.fgraph.toposort() out = f() assert (out == 4).all() out[0] = 3 out2 = f() # If the following 2 asserts fail it mean Aesara broke it's memory contract. assert out2 is not out assert (out2 == 4).all() # Test that if the output is a constant and borrow, we respect the aesara memory interface f = function([], Out(at.constant([4]), borrow=True)) # print f.maker.fgraph.toposort() out = f() assert (out == 4).all() out[0] = 3 out2 = f() if isinstance(get_default_mode(), DebugMode): # In DebugMode, we don't implement optimization based on borrow on the output. assert (out2 == 4).all() else: assert out2 is out assert (out2 == 3).all() def test_borrow_input(self): # Tests that the contract for io.In is respected. When borrow=False, it should be # impossible for outputs to be aliased to the input variables provided by the user, # either through a view-map or a destroy map. New tests should be added in the future # when borrow=True is implemented. a = dmatrix() aval = np.random.random((3, 3)) # when borrow=False, test that a destroy map cannot alias output to input f = function([In(a, borrow=False)], Out(a + 1, borrow=True)) assert np.all(f(aval) == aval + 1) assert not np.may_share_memory(aval, f(aval)) # when borrow=False, test that a viewmap cannot alias output to input f = function([In(a, borrow=False)], Out(a[0, :], borrow=True)) assert np.all(f(aval) == aval[0, :]) assert not np.may_share_memory(aval, f(aval)) def test_borrow_output(self): a = dmatrix() f = function([a], Out(a, borrow=False)) o = np.ones((3, 3)) assert o is not f( o) # function no longer permits aliasing outputs to inputs f = function([a], Out(a * 4, borrow=False)) o = np.ones((3, 3)) four = f(o) assert np.all(four == 4) f(o + 0.1) # should not clobber the memory used to store four assert np.all(four == 4) f = function([a], Out(a * 4, borrow=True), mode=Mode("c|py_nogc", "fast_run")) o = np.ones((3, 3)) four = f(o) assert np.all(four == 4) f(o + 0.1) # should clobber the memory used to store four if config.cxx: assert not np.all(four == 4) else: # The Elemwise.perform method don't reuse memory # as some numpy version don't support that correctly. assert np.all(four == 4) def test_disconnected_input(self): a = scalar("a") v = vector("v") with pytest.raises(UnusedInputError): function([a, v], v * 2) function([a, v], v * 2, on_unused_input="ignore") def test_masked_input(self): m = matrix("m") mt = m.T mt.name = "m.T" with pytest.raises(UnusedInputError): function([m, mt], mt * 2) function([m, mt], mt * 2, on_unused_input="ignore") def test_givens_input_var(self): # Ensure error is raised when trying to replace an input variable. x = scalar("x") y = x * 2 with pytest.raises(RuntimeError): function([x], y, givens={x: x + 1}) def test_free(self): # Make test on free() function x = vector("x") func = function([x], x + 1) func.vm.allow_gc = False func([1]) check_list = [] for key, val in func.vm.storage_map.items(): if not isinstance(key, Constant): check_list.append(val) assert any(val[0] for val in check_list) func.free() for key, val in func.vm.storage_map.items(): if not isinstance(key, Constant): assert val[0] is None def test_default_values(self): # Check that default values are restored # when an exception occurs in interactive mode. a, b = dscalars("a", "b") c = a + b funct = function([In(a, name="first"), In(b, value=1, name="second")], c) x = funct(first=1) try: funct(second=2) except TypeError: assert funct(first=1) == x def test_check_for_aliased_inputs(self): b = np.random.random((5, 4)) s1 = shared(b) s2 = shared(b) x1 = vector() # Assert cases we should not check for aliased inputs for d in [ dict(outputs=[s1 + 1]), dict(outputs=[s1 + 1, s2 + 3]), dict(outputs=[s1 + 1], updates=[(s2, s2 + 3)]), dict(inputs=[x1], outputs=[x1 + 1], updates=[(s2, s2 + 3)]), ]: if "inputs" not in d: d["inputs"] = [] f = function(**d) assert not f._check_for_aliased_inputs, d # Assert cases we should check for aliased inputs for d in [ dict( inputs=[In(x1, borrow=True)], outputs=[x1 + 1], updates=[(s2, s2 + 3)], ), dict( inputs=[In(x1, borrow=True, mutable=True)], outputs=[x1 + 1], updates=[(s2, s2 + 3)], ), dict( inputs=[In(x1, mutable=True)], outputs=[x1 + 1], updates=[(s2, s2 + 3)], ), ]: if "inputs" not in d: d["inputs"] = [] f = function(**d) assert f._check_for_aliased_inputs, d
from aesara.link.numba.linker import NumbaLinker from aesara.link.vm import VMLinker _logger = logging.getLogger("aesara.compile.mode") # If a string is passed as the linker argument in the constructor for # Mode, it will be used as the key to retrieve the real linker in this # dictionary predefined_linkers = { "py": PerformLinker(), # Use allow_gc Aesara flag "c": CLinker(), # Don't support gc. so don't check allow_gc "c|py": OpWiseCLinker(), # Use allow_gc Aesara flag "c|py_nogc": OpWiseCLinker(allow_gc=False), "vm": VMLinker(use_cloop=False), # Use allow_gc Aesara flag "cvm": VMLinker(use_cloop=True), # Use allow_gc Aesara flag "vm_nogc": VMLinker(allow_gc=False, use_cloop=False), "cvm_nogc": VMLinker(allow_gc=False, use_cloop=True), "jax": JAXLinker(), "numba": NumbaLinker(), } def register_linker(name, linker): """Add a `Linker` which can be referred to by `name` in `Mode`.""" if name in predefined_linkers: raise ValueError(f"Linker name already taken: {name}") predefined_linkers[name] = linker
print( f"{name} takes {1000 * (t_b - t_a) / (steps_b - steps_a):f} s/Kop") time_linker("c|py", OpWiseCLinker) time_linker("vmLinker", VMLinker) time_linker("vmLinker_nogc", lambda: VMLinker(allow_gc=False)) if config.cxx: time_linker("vmLinker_CLOOP", lambda: VMLinker(allow_gc=False, use_cloop=True)) time_numpy() @pytest.mark.parametrize( "linker", [ VMLinker(), VMLinker(allow_gc=False), VMLinker(allow_gc=False, use_cloop=True), ], ) def test_speed_lazy(linker): # TODO FIXME: This isn't a real test. def build_graph(x, depth=5): z = x for d in range(depth): z = ifelse(z[0] > 0, -z, z) return z steps_a = 10 steps_b = 100