def test_param_allow_downcast_floatX(self): a = fscalar("a") b = fscalar("b") c = fscalar("c") f = pfunc( [ In(a, allow_downcast=True), In(b, allow_downcast=False), In(c, allow_downcast=None), ], (a + b + c), ) # If the values can be accurately represented, everything is OK assert np.all(f(0, 0, 0) == 0) # If allow_downcast is True, idem assert np.allclose(f(0.1, 0, 0), 0.1) # If allow_downcast is False, nope with pytest.raises(TypeError): f(0, 0.1, 0) # If allow_downcast is None, it should work iff floatX=float32 if config.floatX == "float32": assert np.allclose(f(0, 0, 0.1), 0.1) else: with pytest.raises(TypeError): f(0, 0, 0.1)
def test_param_allow_downcast_vector_floatX(self): a = fvector("a") b = fvector("b") c = fvector("c") f = pfunc( [ In(a, allow_downcast=True), In(b, allow_downcast=False), In(c, allow_downcast=None), ], (a + b + c), ) # If the values can be accurately represented, everything is OK z = [0] assert np.all(f(z, z, z) == 0) # If allow_downcast is True, idem assert np.allclose(f([0.1], z, z), 0.1) # If allow_downcast is False, nope with pytest.raises(TypeError): f(z, [0.1], z) # If allow_downcast is None, like False with pytest.raises(TypeError): f(z, z, [0.1])
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_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 __init__(self): a = scalar() # the a is for 'anonymous' (un-named). x, s = scalars("xs") v = 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_copy(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 = 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_state_access(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) ], s + a * x, ) assert f[a] == 1.0 assert f[s] == 0.0 assert f(3.0) == 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_deepcopy_trust_input(self): a = dscalar() # the a is for 'anonymous' (un-named). x, s = dscalars("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, ) f.trust_input = True try: g = copy.deepcopy(f) except NotImplementedError as e: if e[0].startswith("DebugMode is not picklable"): return else: raise assert f.trust_input is g.trust_input f(np.asarray(2.0)) with pytest.raises((ValueError, AttributeError, InvalidValueError)): f(2.0) g(np.asarray(2.0)) with pytest.raises((ValueError, AttributeError, InvalidValueError)): g(2.0)
def test_param_allow_downcast_int(self): a = wvector("a") # int16 b = bvector("b") # int8 c = bscalar("c") # int8 f = pfunc( [ In(a, allow_downcast=True), In(b, allow_downcast=False), In(c, allow_downcast=None), ], (a + b + c), ) # Both values are in range. Since they're not ndarrays (but lists), # they will be converted, and their value checked. assert np.all(f([3], [6], 1) == 10) # Values are in range, but a dtype too large has explicitly been given # For performance reasons, no check of the data is explicitly performed # (It might be OK to change this in the future.) with pytest.raises(TypeError): f([3], np.array([6], dtype="int16"), 1) # Value too big for a, silently ignored assert np.all(f([2 ** 20], np.ones(1, dtype="int8"), 1) == 2) # Value too big for b, raises TypeError with pytest.raises(TypeError): f([3], [312], 1) # Value too big for c, raises TypeError with pytest.raises(TypeError): f([3], [6], 806)
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 = scalar() y = x * 2 function([In(x)], y, givens={}) function([In(x)], y, updates={})
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
def test_deepcopy(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, ) 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_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_input_aliasing_affecting_inplace_operations(self): # Note: to trigger this bug with aesara rev 4586:2bc6fc7f218b, # you need to make in inputs mutable (so that inplace # operations are used) and to break the elemwise composition # with some non-elemwise op (here dot) x = dvector() y = dvector() m1 = dmatrix() m2 = dmatrix() f = function( [ In(x, mutable=True), In(y, mutable=True), In(m1, mutable=True), In(m2, mutable=True), ], aet.dot((x * 2), m1) + aet.dot((y * 3), m2), ) # Test 1. If the same variable is given twice # Compute bogus values v = np.asarray([1, 2, 3, 4, 5], dtype="float64") m = np.asarray( [ [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1], ], dtype="float64", ) bogus_vals = f(v, v, m, m) # Since we used inplace operation v and m may be corrupted # so we need to recreate them v = np.asarray([1, 2, 3, 4, 5], dtype="float64") m = np.asarray( [ [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1], ], dtype="float64", ) m_copy = m.copy() v_copy = v.copy() vals = f(v, v_copy, m, m_copy) assert np.allclose(vals, bogus_vals)
def test_sparse_input_aliasing_affecting_inplace_operations(self): sp = pytest.importorskip("scipy", minversion="0.7.0") from aesara import sparse # Note: to trigger this bug with aesara rev 4586:2bc6fc7f218b, # you need to make in inputs mutable (so that inplace # operations are used) and to break the elemwise composition # with some non-elemwise op (here dot) x = sparse.SparseType("csc", dtype="float64")() y = sparse.SparseType("csc", dtype="float64")() f = function([In(x, mutable=True), In(y, mutable=True)], (x + y) + (x + y)) # Test 1. If the same variable is given twice # Compute bogus values m = sp.sparse.csc_matrix( np.asarray( [ [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1], ], dtype="float64", ) ) bogus_vals = f(m, m) # Since we used inplace operation v and m may be corrupted # so we need to recreate them m = sp.sparse.csc_matrix( np.asarray( [ [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1], ], dtype="float64", ) ) m_copy = m.copy() vals = f(m, m_copy) assert np.allclose(vals.todense(), bogus_vals.todense())
def test_basic(self, type1, type2, converter): x = TensorType(dtype=type1, shape=(False, ))() y = converter(x) f = function([In(x, strict=True)], y) a = np.arange(10, dtype=type1) b = f(a) assert np.array_equal(b, np.arange(10, dtype=type2))
def test_insert_inplace(self): mySymbolicMatricesList = TypedListType( TensorType(aesara.config.floatX, (False, False)))() mySymbolicIndex = scalar(dtype="int64") mySymbolicMatrix = 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 = random_ranged(-1000, 1000, [100, 101]) y = random_ranged(-1000, 1000, [100, 101]) assert np.array_equal(f([x], np.asarray(1, dtype="int64"), y), [x, y])
def test_basic(self): for type1 in [ "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "float32", "float64", ]: x = TensorType(dtype=type1, broadcastable=(False, ))() for type2, converter in zip( ["int8", "int16", "int32", "int64", "float32", "float64"], [ _convert_to_int8, _convert_to_int16, _convert_to_int32, _convert_to_int64, _convert_to_float32, _convert_to_float64, ], ): y = converter(x) f = function([In(x, strict=True)], y) a = np.arange(10, dtype=type1) b = f(a) assert np.all(b == np.arange(10, dtype=type2))
def test_extend_inplace(self): mySymbolicMatricesList1 = TypedListType( TensorType(aesara.config.floatX, (False, False)))() mySymbolicMatricesList2 = TypedListType( TensorType(aesara.config.floatX, (False, False)))() z = Extend()(mySymbolicMatricesList1, mySymbolicMatricesList2) m = aesara.compile.mode.get_default_mode().including( "typed_list_inplace_opt") f = aesara.function( [ In(mySymbolicMatricesList1, borrow=True, mutable=True), mySymbolicMatricesList2, ], z, mode=m, ) assert f.maker.fgraph.toposort()[0].op.inplace x = random_ranged(-1000, 1000, [100, 101]) y = random_ranged(-1000, 1000, [100, 101]) assert np.array_equal(f([x], [y]), [x, y])
def test_in_shared_variable(self): # Ensure that an error is raised if the In wrapped is used to wrap # a shared variable a = aesara.shared(1.0) a_wrapped = In(a, update=a + 1) with pytest.raises(TypeError): function([a_wrapped])
def test_doc(self): # Ensure the code given in pfunc.txt works as expected # Example #1. a = lscalar() b = shared(1) f1 = pfunc([a], (a + b)) f2 = pfunc([In(a, value=44)], a + b, updates={b: b + 1}) assert b.get_value() == 1 assert f1(3) == 4 assert f2(3) == 4 assert b.get_value() == 2 assert f1(3) == 5 b.set_value(0) assert f1(3) == 3 # Example #2. a = lscalar() b = shared(7) f1 = pfunc([a], a + b) f2 = pfunc([a], a * b) assert f1(5) == 12 b.set_value(8) assert f1(5) == 13 assert f2(4) == 32
def test_in_update_wrong_dtype(self): # Ensure that an error is raised if an In-wrapped variables has # an update of a different type a = dscalar("a") b = dvector("b") with pytest.raises(TypeError): In(a, update=b)
def test_pickle(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, ) try: # Note that here we also test protocol 0 on purpose, since it # should work (even though one should not use it). g = pickle.loads(pickle.dumps(f, protocol=0)) g = pickle.loads(pickle.dumps(f, protocol=-1)) 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 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.
def test_in_mutable(self): a = dvector() a_out = a * 2 # assuming the op which makes this "in place" triggers # using mutable=True will let f change the value in aval f = function([In(a, mutable=True)], a_out, mode="FAST_RUN") aval = np.random.random((10, )) aval2 = aval.copy() assert np.array_equal(f(aval), (aval2 * 2)) assert not np.array_equal(aval, aval2) # using mutable=False should leave the input untouched f = function([In(a, mutable=False)], a_out, mode="FAST_RUN") aval = np.random.random((10, )) aval2 = aval.copy() assert np.array_equal(f(aval), (aval2 * 2)) assert np.array_equal(aval, aval2)
def _pfunc_param_to_in(param, strict=False, allow_downcast=None): if isinstance(param, Constant): raise TypeError("Constants not allowed in param list", param) if isinstance(param, Variable): # N.B. includes SharedVariable return In(variable=param, strict=strict, allow_downcast=allow_downcast) elif isinstance(param, In): return param raise TypeError(f"Unknown parameter type: {type(param)}")
def test_param_mutable(self): a = dvector() a_out = a * 2 # assuming the op which makes this "in place" triggers # using mutable=True will let fip change the value in aval fip = pfunc([In(a, mutable=True)], [a_out], mode="FAST_RUN") aval = np.random.rand(10) aval2 = aval.copy() assert np.all(fip(aval) == (aval2 * 2)) assert not np.all(aval == aval2) # using mutable=False should leave the input untouched f = pfunc([In(a, mutable=False)], [a_out], mode="FAST_RUN") aval = np.random.rand(10) aval2 = aval.copy() assert np.all(f(aval) == (aval2 * 2)) assert np.all(aval == aval2)
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_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.rand(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_param_strict(self): a = dvector() b = shared(7) out = a + b f = pfunc([In(a, strict=False)], [out]) # works, rand generates float64 by default f(np.random.rand(8)) # works, casting is allowed f(np.array([1, 2, 3, 4], dtype="int32")) f = pfunc([In(a, strict=True)], [out]) try: # fails, f expects float64 f(np.array([1, 2, 3, 4], dtype="int32")) except TypeError: pass
def test_in_update(self): a = dscalar("a") f = function([In(a, value=0.0, update=a + 1)], a, mode="FAST_RUN") # Ensure that, through the executions of the function, the state of the # input is persistent and is updated as it should assert f() == 0.0 assert f() == 1.0 assert f() == 2.0