def test_contraction_structure_Mul_and_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') i_ji = x[i]**(y[j] * x[i]) assert get_contraction_structure(i_ji) == {None: {i_ji}} ij_i = (x[i] * y[j])**(y[i]) assert get_contraction_structure(ij_i) == {None: {ij_i}} j_ij_i = x[j] * (x[i] * y[j])**(y[i]) assert get_contraction_structure(j_ij_i) == {(j, ): {j_ij_i}} j_i_ji = x[j] * x[i]**(y[j] * x[i]) assert get_contraction_structure(j_i_ji) == {(j, ): {j_i_ji}} ij_exp_kki = x[i] * y[j] * exp(y[i] * y[k, k]) result = get_contraction_structure(ij_exp_kki) expected = { (i, ): {ij_exp_kki}, ij_exp_kki: [{ None: {exp(y[i] * y[k, k])}, exp(y[i] * y[k, k]): [{ None: {y[i] * y[k, k]}, y[i] * y[k, k]: [{ (k, ): {y[k, k]} }] }] }] } assert result == expected
def test_ufunc_support(): f = Function('f') g = Function('g') x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') a = symbols('a') assert get_indices(f(x[i])) == ({i}, {}) assert get_indices(f(x[i], y[j])) == ({i, j}, {}) assert get_indices(f(y[i]) * g(x[i])) == (set(), {}) assert get_indices(f(a, x[i])) == ({i}, {}) assert get_indices(f(a, y[i], x[j]) * g(x[i])) == ({j}, {}) assert get_indices(g(f(x[i]))) == ({i}, {}) assert get_contraction_structure(f(x[i])) == {None: {f(x[i])}} assert get_contraction_structure(f(y[i]) * g(x[i])) == { (i, ): {f(y[i]) * g(x[i])} } assert get_contraction_structure(f(y[i]) * g(f(x[i]))) == { (i, ): {f(y[i]) * g(f(x[i]))} } assert get_contraction_structure(f(x[j], y[i]) * g(x[i])) == { (i, ): {f(x[j], y[i]) * g(x[i])} }
def test_get_contraction_structure_complex(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') expr1 = y[i] + A[i, j] * x[j] d1 = {None: {y[i]}, (j, ): {A[i, j] * x[j]}} assert get_contraction_structure(expr1) == d1 expr2 = expr1 * A[k, i] + x[k] d2 = {None: {x[k]}, (i, ): {expr1 * A[k, i]}, expr1 * A[k, i]: [d1]} assert get_contraction_structure(expr2) == d2
def test_get_contraction_structure_basic(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_contraction_structure(x[i] * y[j]) == {None: {x[i] * y[j]}} assert get_contraction_structure(x[i] + y[j]) == {None: {x[i], y[j]}} assert get_contraction_structure(x[i] * y[i]) == {(i, ): {x[i] * y[i]}} assert get_contraction_structure(1 + x[i] * y[i]) == { None: {S.One}, (i, ): {x[i] * y[i]} } assert get_contraction_structure(x[i]**y[i]) == {None: {x[i]**y[i]}}
def test_contraction_structure_simple_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') ii_jj = x[i, i]**y[j, j] assert get_contraction_structure(ii_jj) == { None: {ii_jj}, ii_jj: [{ (i, ): {x[i, i]} }, { (j, ): {y[j, j]} }] }
def test_get_indices_Pow(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') assert get_indices(Pow(x[i], y[j])) == ({i, j}, {}) assert get_indices(Pow(x[i, k], y[j, k])) == ({i, j, k}, {}) assert get_indices(Pow(A[i, k], y[k] + A[k, j] * x[j])) == ({i, k}, {}) assert get_indices(Pow(2, x[i])) == get_indices(exp(x[i])) # test of a design decision, this may change: assert get_indices(Pow(x[i], 2)) == ({ i, }, {})
def test_contraction_structure_Add_in_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') s_ii_jj_s = (1 + x[i, i])**(1 + y[j, j]) expected = { None: {s_ii_jj_s}, s_ii_jj_s: [{ None: {S.One}, (i, ): {x[i, i]} }, { None: {S.One}, (j, ): {y[j, j]} }] } result = get_contraction_structure(s_ii_jj_s) assert result == expected
def test_get_contraction_structure_basic(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') f = Function('f') assert get_contraction_structure(x[i] * y[j]) == {None: {x[i] * y[j]}} assert get_contraction_structure(x[i] + y[j]) == {None: {x[i], y[j]}} assert get_contraction_structure(x[i] * y[i]) == {(i, ): {x[i] * y[i]}} assert get_contraction_structure(1 + x[i] * y[i]) == { None: {1}, (i, ): {x[i] * y[i]} } assert get_contraction_structure(x[i]**y[i]) == {None: {x[i]**y[i]}} assert (get_contraction_structure(f(x[i, i])) == { None: {f(x[i, i])}, f(x[i, i]): [{ (i, ): {x[i, i]} }] })
def test_get_indices_add(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') assert get_indices(x[i] + 2 * y[i]) == ({ i, }, {}) assert get_indices(y[i] + 2 * A[i, j] * x[j]) == ({ i, }, {}) assert get_indices(y[i] + 2 * (x[i] + A[i, j] * x[j])) == ({ i, }, {}) assert get_indices(y[i] + x[i] * (A[j, j] + 1)) == ({ i, }, {}) assert get_indices(y[i] + x[i] * x[j] * (y[j] + A[j, k] * x[k])) == ({ i, }, {})
def test_contraction_structure_Pow_in_Pow(): x = IndexedBase('x') y = IndexedBase('y') z = IndexedBase('z') i, j, k = Idx('i'), Idx('j'), Idx('k') ii_jj_kk = x[i, i]**y[j, j]**z[k, k] expected = { None: {ii_jj_kk}, ii_jj_kk: [{ (i, ): {x[i, i]} }, { None: {y[j, j]**z[k, k]}, y[j, j]**z[k, k]: [{ (j, ): {y[j, j]} }, { (k, ): {z[k, k]} }] }] } assert get_contraction_structure(ii_jj_kk) == expected
def test_scalar_broadcast(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[i] + y[i, i]) == ({i}, {})
def test_get_indices_exceptions(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') pytest.raises(IndexConformanceException, lambda: get_indices(x[i] + y[j]))
def test_get_indices_mul(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[j] * y[i]) == ({i, j}, {}) assert get_indices(x[i] * y[j]) == ({i, j}, {})
def test_get_indices_Idx(): f = Function('f') i, j = Idx('i'), Idx('j') assert get_indices(f(i) * j) == ({i, j}, {}) assert get_indices(f(j, i)) == ({j, i}, {}) assert get_indices(f(i) * i) == (set(), {})
def test_get_indices_Indexed(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[i, j]) == ({i, j}, {}) assert get_indices(x[j, i]) == ({j, i}, {})
def ufuncify(args, expr, language=None, backend='numpy', tempdir=None, flags=None, verbose=False, helpers=None): """ Generates a binary function that supports broadcasting on numpy arrays. Parameters ---------- args : iterable Either a Symbol or an iterable of symbols. Specifies the argument sequence for the function. expr A Diofant expression that defines the element wise operation. language : string, optional If supplied, (options: 'C' or 'F95'), specifies the language of the generated code. If ``None`` [default], the language is inferred based upon the specified backend. backend : string, optional Backend used to wrap the generated code. Either 'numpy' [default], 'cython', or 'f2py'. tempdir : string, optional Path to directory for temporary files. If this argument is supplied, the generated code and the wrapper input files are left intact in the specified path. flags : iterable, optional Additional option flags that will be passed to the backend verbose : bool, optional If True, autowrap will not mute the command line backends. This can be helpful for debugging. helpers : iterable, optional Used to define auxillary expressions needed for the main expr. If the main expression needs to call a specialized function it should be put in the ``helpers`` iterable. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be tuples with (<funtion_name>, <diofant_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. Notes ----- The default backend ('numpy') will create actual instances of ``numpy.ufunc``. These support ndimensional broadcasting, and implicit type conversion. Use of the other backends will result in a "ufunc-like" function, which requires equal length 1-dimensional arrays for all arguments, and will not perform any type conversions. References ---------- .. [1] http://docs.scipy.org/doc/numpy/reference/ufuncs.html Examples -------- >>> from diofant.utilities.autowrap import ufuncify >>> from diofant.abc import x, y >>> import numpy as np >>> f = ufuncify((x, y), y + x**2) >>> type(f) is np.ufunc True >>> f([1, 2, 3], 2) array([ 3., 6., 11.]) >>> f(np.arange(5), 3) array([ 3., 4., 7., 12., 19.]) For the F2Py and Cython backends, inputs are required to be equal length 1-dimensional arrays. The F2Py backend will perform type conversion, but the Cython backend will error if the inputs are not of the expected type. >>> f_fortran = ufuncify((x, y), y + x**2, backend='F2Py') >>> f_fortran(1, 2) 3 >>> f_fortran(numpy.array([1, 2, 3]), numpy.array([1.0, 2.0, 3.0])) array([2., 6., 12.]) >>> f_cython = ufuncify((x, y), y + x**2, backend='Cython') >>> f_cython(1, 2) Traceback (most recent call last): ... TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int) >>> f_cython(numpy.array([1.0]), numpy.array([2.0])) array([ 3.]) """ if isinstance(args, (Dummy, Symbol)): args = (args, ) else: args = tuple(args) if language: _validate_backend_language(backend, language) else: language = _infer_language(backend) helpers = helpers if helpers else () flags = flags if flags else () if backend.upper() == 'NUMPY': routine = make_routine('autofunc', expr, args) helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) code_wrapper = UfuncifyCodeWrapper(CCodeGen("ufuncify"), tempdir, flags, verbose) return code_wrapper.wrap_code(routine, helpers=helps) else: # Dummies are used for all added expressions to prevent name clashes # within the original expression. y = IndexedBase(Dummy()) m = Dummy(integer=True) i = Idx(Dummy(integer=True), m) f = implemented_function(Dummy().name, Lambda(args, expr)) # For each of the args create an indexed version. indexed_args = [IndexedBase(Dummy(str(a))) for a in args] # Order the arguments (out, args, dim) args = [y] + indexed_args + [m] args_with_indices = [a[i] for a in indexed_args] return autowrap(Eq(y[i], f(*args_with_indices)), language, backend, tempdir, tuple(args), flags, verbose, helpers)