def create_module(module_name, expression_name_tuples, directory): """Generates a cython module that can be imported.""" routines = [] for name, expression, args in expression_name_tuples: try: routine = make_routine(name, [expression], args) except CodeGenArgumentListError as e: new_args = [] for missing in e.missing_args: if not isinstance(missing, OutputArgument): raise new_args.append(missing.name) routine = make_routine(name, expression, list(args) + new_args) routines.append(routine) if not os.path.exists(directory): os.makedirs(directory) cg = CCodeGen() [(cf, cs), (hf, hs)] = cg.write(routines, module_name + '_code') with open(directory + '/' + cf, "w") as text_file: text_file.write(cs) with open(directory + '/' + hf, "w") as text_file: text_file.write(hs) ccw = CythonCodeWrapper(cg) with open(directory + '/' + module_name + '.pyx', "w") as text_file: ccw.dump_pyx(routines, text_file, module_name + '_code') create_setup(module_name + '.pyx', module_name + '_code.c', directory, module_name) open(directory + '/__init__.py', 'w').close() oldwork = os.getcwd() os.chdir(directory) workdir = os.getcwd() command = [sys.executable, "setup.py", "build_ext", "--inplace"] try: sys.path.append(workdir) retoutput = check_output(command, stderr=STDOUT) except CalledProcessError as e: raise CodeWrapError( "Error while executing command: %s. Command output is:\n%s" % (" ".join(command), e.output.decode())) finally: sys.path.remove(workdir) os.chdir(oldwork)
def ufuncify(args, expr, tmpman=None, tempdir=None, flags=None, cflags=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 : SymPy object or list of SymPy objects SymPy expression(s) that defines the element wise operation. tmpman : TempfileManager, optional Context manager for temporary file cleanup. 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 cflags : iterable, optional Additional compiler flags that will be passed to ``extra_compile_args`` 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. """ if isinstance(args, Symbol): args = (args,) else: args = tuple(args) if tmpman is None: raise ValueError('Missing temporary file context manager') helpers = helpers if helpers else () flags = flags if flags else () cflags = cflags if cflags else () helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) code_wrapper = UfuncifyCodeWrapper(CCodeGen("ufuncify"), tmpman, tempdir, flags, verbose) if not isinstance(expr, (list, tuple)): expr = [expr] routines = [make_routine('autofunc{}'.format(idx), exprx, args) for idx, exprx in enumerate(expr)] return code_wrapper.wrap_code(routines, helpers=helps, cflags=cflags)
def test_inline_function(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m = symbols('n m', integer=True) A, x, y = map(IndexedBase, 'Axy') i = Idx('i', m) p = FCodeGen() func = implemented_function('func', Lambda(n, n*(n + 1))) routine = make_routine('test_inline', Eq(y[i], func(x[i]))) code = get_string(p.dump_f95, [routine]) expected = ( 'subroutine test_inline(m, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'REAL*8, intent(in), dimension(1:m) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'do i = 1, m\n' ' y(i) = %s*%s\n' 'end do\n' 'end subroutine\n' ) args = ('x(i)', '(x(i) + 1)') assert code == expected % args or\ code == expected % args[::-1]
def test_dummy_loops_f95(): from sympy.tensor import IndexedBase, Idx # the following line could also be # [Dummy(s, integer=True) for s in 'im'] # or [Dummy(integer=True) for s in 'im'] i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'subroutine test_dummies(m_%(mcount)i, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m_%(mcount)i\n' 'REAL*8, intent(in), dimension(1:m_%(mcount)i) :: x\n' 'REAL*8, intent(out), dimension(1:m_%(mcount)i) :: y\n' 'INTEGER*4 :: i_%(icount)i\n' 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do\n' 'end subroutine\n' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} r = make_routine('test_dummies', Eq(y[i], x[i])) c = FCodeGen() code = get_string(c.dump_f95, [r]) assert code == expected
def test_multiple_results_f(): x, y, z = symbols('x,y,z') expr1 = (x + y)*z expr2 = (x - y)*z routine = make_routine( "test", [expr1, expr2] ) code_gen = FCodeGen() raises(CodeGenError, lambda: get_string(code_gen.dump_h, [routine]))
def test_m_code_argument_order(): expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y], language="octave") code_gen = OctaveCodeGen() output = StringIO() code_gen.dump_m([routine], output, "test", header=False, empty=False) source = output.getvalue() expected = ("function out1 = test(z, x, y)\n" " out1 = x + y;\n" "end\n") assert source == expected
def test_Routine_argument_order(): a, x, y, z = symbols('a x y z') expr = (x + y)*z raises(CodeGenArgumentListError, lambda: make_routine("test", expr, argument_sequence=[z, x])) raises(CodeGenArgumentListError, lambda: make_routine("test", Eq(a, expr), argument_sequence=[z, x, y])) r = make_routine('test', Eq(a, expr), argument_sequence=[z, x, a, y]) assert [ arg.name for arg in r.arguments ] == [z, x, a, y] assert [ type(arg) for arg in r.arguments ] == [ InputArgument, InputArgument, OutputArgument, InputArgument ] r = make_routine('test', Eq(z, expr), argument_sequence=[z, x, y]) assert [ type(arg) for arg in r.arguments ] == [ InOutArgument, InputArgument, InputArgument ] from sympy.tensor import IndexedBase, Idx A, B = map(IndexedBase, ['A', 'B']) m = symbols('m', integer=True) i = Idx('i', m) r = make_routine('test', Eq(A[i], B[i]), argument_sequence=[B, A, m]) assert [ arg.name for arg in r.arguments ] == [B.label, A.label, m] expr = Integral(x*y*z, (x, 1, 2), (y, 1, 3)) r = make_routine('test', Eq(a, expr), argument_sequence=[z, x, a, y]) assert [ arg.name for arg in r.arguments ] == [z, x, a, y]
def test_simple_c_header(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = make_routine("test", expr) code_gen = C89CodeGen() source = get_string(code_gen.dump_h, [routine]) expected = ( "#ifndef PROJECT__FILE__H\n" "#define PROJECT__FILE__H\n" "double test(double x, double y, double z);\n" "#endif\n" ) assert source == expected
def test_m_code_argument_order(): expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y], language="octave") code_gen = OctaveCodeGen() output = StringIO() code_gen.dump_m([routine], output, "test", header=False, empty=False) source = output.getvalue() expected = ( "function out1 = test(z, x, y)\n" " out1 = x + y;\n" "end\n" ) assert source == expected
def is_feasible(language, commands): # This test should always work, otherwise the compiler is not present. routine = make_routine("test", x) numerical_tests = [ ("test", ( 1.0,), 1.0, 1e-15), ("test", (-1.0,), -1.0, 1e-15), ] try: run_test("is_feasible", [routine], numerical_tests, language, commands, friendly=False) return True except AssertionError: return False
def test_erf_f_code(): x = symbols('x') routine = make_routine("test", erf(x) - erf(-2 * x)) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(x)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "test = erf(x) + erf(2.0d0*x)\n" "end function\n" ) assert source == expected, source
def test_numbersymbol_f_code(): routine = make_routine("test", pi**Catalan) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test()\n" "implicit none\n" "REAL*8, parameter :: Catalan = 0.915965594177219d0\n" "REAL*8, parameter :: pi = 3.14159265358979d0\n" "test = pi**Catalan\n" "end function\n" ) assert source == expected
def test_cython_wrapper_scalar_function(): x, y, z = symbols('x,y,z') expr = (x + y) * z routine = make_routine("test", expr) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ("cdef extern from 'file.h':\n" " double test(double x, double y, double z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " return test(x, y, z)") assert source == expected
def test_argument_order(): expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y], language="rust") code_gen = RustCodeGen() output = StringIO() code_gen.dump_rs([routine], output, "test", header=False, empty=False) source = output.getvalue() expected = ( "fn test(z: f64, x: f64, y: f64) -> f64 {\n" " let out1 = x + y;\n" " out1\n" "}\n" ) assert source == expected
def test_jl_code_argument_order(): expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y], language="julia") code_gen = JuliaCodeGen() output = StringIO() code_gen.dump_jl([routine], output, "test", header=False, empty=False) source = output.getvalue() expected = ( "function test(z, x, y)\n" " out1 = x + y\n" " return out1\n" "end\n" ) assert source == expected
def test_cython_wrapper_inoutarg(): from sympy import Equality x, y, z = symbols('x,y,z') code_gen = CythonCodeWrapper(C99CodeGen()) routine = make_routine("test", Equality(z, x + y + z)) source = get_string(code_gen.dump_pyx, [routine]) expected = ("cdef extern from 'file.h':\n" " void test(double x, double y, double *z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " test(x, y, &z)\n" " return z") assert source == expected
def test_cython_wrapper_scalar_function(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = make_routine("test", expr) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ( "cdef extern from 'file.h':\n" " double test(double x, double y, double z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " return test(x, y, z)") assert source == expected
def sympy_into_c(sympy_functions, global_vars=None): from sympy.utilities import codegen routines = [] for name, expr, args in sympy_functions: r = codegen.make_routine(name, expr, language="C99", global_vars=global_vars) # argument ordering input to sympy is broken with function with output arguments nargs = [] # reorder the input arguments for aa in args: if aa is None: nargs.append( codegen.InputArgument(sp.Symbol('unused'), dimensions=[1, 1])) continue found = False for a in r.arguments: if str(aa.name) == str(a.name): nargs.append(a) found = True break if not found: # [1,1] is a hack for Matrices nargs.append(codegen.InputArgument(aa, dimensions=[1, 1])) # add the output arguments for a in r.arguments: if type(a) == codegen.OutputArgument: nargs.append(a) # assert len(r.arguments) == len(args)+1 r.arguments = nargs # add routine to list routines.append(r) [(c_name, c_code), (h_name, c_header) ] = codegen.get_code_generator('C', 'ekf', 'C99').write(routines, "ekf") c_header = '\n'.join(x for x in c_header.split("\n") if len(x) > 0 and x[0] != '#') c_code = '\n'.join(x for x in c_code.split("\n") if len(x) > 0 and x[0] != '#') c_code = 'extern "C" {\n#include <math.h>\n' + c_code + "\n}\n" return c_header, c_code
def test_numbersymbol_c_code(): routine = make_routine("test", pi**Catalan) code_gen = C89CodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include <math.h>\n" "double test() {\n" " double test_result;\n" " double const Catalan = 0.915965594177219;\n" " test_result = pow(M_PI, Catalan);\n" " return test_result;\n" "}\n" ) assert source == expected
def test_cython_wrapper_inoutarg(): from sympy import Equality x, y, z = symbols('x,y,z') code_gen = CythonCodeWrapper(C99CodeGen()) routine = make_routine("test", Equality(z, x + y + z)) source = get_string(code_gen.dump_pyx, [routine]) expected = ( "cdef extern from 'file.h':\n" " void test(double x, double y, double *z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " test(x, y, &z)\n" " return z") assert source == expected
def test_simple_c_code(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = make_routine("test", expr) code_gen = C89CodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include <math.h>\n" "double test(double x, double y, double z) {\n" " double test_result;\n" " test_result = z*(x + y);\n" " return test_result;\n" "}\n" ) assert source == expected
def test_f_code_argument_order(): x, y, z = symbols('x,y,z') expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y]) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(z, x, y)\n" "implicit none\n" "REAL*8, intent(in) :: z\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "test = x + y\n" "end function\n" ) assert source == expected
def test_output_arg_f(): from sympy import sin, cos, Equality x, y, z = symbols("x,y,z") r = make_routine("foo", [Equality(y, sin(x)), cos(x)]) c = FCodeGen() result = c.write([r], "test", header=False, empty=False) assert result[0][0] == "test.f90" assert result[0][1] == ( 'REAL*8 function foo(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(out) :: y\n' 'y = sin(x)\n' 'foo = cos(x)\n' 'end function\n' )
def test_cython_wrapper_scalar_function(): x, y, z = symbols('x,y,z') expr = (x + y) * z routine = make_routine("test", expr) with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=SymPyDeprecationWarning) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ("cdef extern from 'file.h':\n" " double test(double x, double y, double z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " return test(x, y, z)") assert source == expected
def test_c_code_reserved_words(): x, y, z = symbols('if, typedef, while') expr = (x + y) * z routine = make_routine("test", expr) code_gen = C99CodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include <math.h>\n" "double test(double if_, double typedef_, double while_) {\n" " double test_result;\n" " test_result = while_*(if_ + typedef_);\n" " return test_result;\n" "}\n" ) assert source == expected
def test_simple_f_code(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = make_routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "test = z*(x + y)\n" "end function\n" ) assert source == expected
def test_c_code_argument_order(): x, y, z = symbols('x,y,z') expr = x + y routine = make_routine("test", expr, argument_sequence=[z, x, y]) code_gen = C89CodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include <math.h>\n" "double test(double z, double x, double y) {\n" " double test_result;\n" " test_result = x + y;\n" " return test_result;\n" "}\n" ) assert source == expected
def test_cython_wrapper_scalar_function(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = make_routine("test", expr) with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=SymPyDeprecationWarning) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ( "cdef extern from 'file.h':\n" " double test(double x, double y, double z)\n" "\n" "def test_c(double x, double y, double z):\n" "\n" " return test(x, y, z)") assert source == expected
def test_output_arg_c_reserved_words(): from sympy import sin, cos, Equality x, y, z = symbols("if, while, z") r = make_routine("foo", [Equality(y, sin(x)), cos(x)]) c = C89CodeGen() result = c.write([r], "test", header=False, empty=False) assert result[0][0] == "test.c" expected = ( '#include "test.h"\n' '#include <math.h>\n' 'double foo(double if_, double *while_) {\n' ' (*while_) = sin(if_);\n' ' double foo_result;\n' ' foo_result = cos(if_);\n' ' return foo_result;\n' '}\n' ) assert result[0][1] == expected
def test_cython_wrapper_unique_dummyvars(): from sympy import Dummy, Equality x, y, z = Dummy('x'), Dummy('y'), Dummy('z') x_id, y_id, z_id = [str(d.dummy_index) for d in [x, y, z]] expr = Equality(z, x + y) routine = make_routine("test", expr) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected_template = ( "cdef extern from 'file.h':\n" " void test(double x_{x_id}, double y_{y_id}, double *z_{z_id})\n" "\n" "def test_c(double x_{x_id}, double y_{y_id}):\n" "\n" " cdef double z_{z_id} = 0\n" " test(x_{x_id}, y_{y_id}, &z_{z_id})\n" " return z_{z_id}") expected = expected_template.format(x_id=x_id, y_id=y_id, z_id=z_id) assert source == expected
def test_dummy_loops_c(): from sympy.tensor import IndexedBase, Idx i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( '#include "file.h"\n' '#include <math.h>\n' 'void test_dummies(int m_%(mno)i, double *x, double *y) {\n' ' for (int i_%(ino)i=0; i_%(ino)i<m_%(mno)i; i_%(ino)i++){\n' ' y[i_%(ino)i] = x[i_%(ino)i];\n' ' }\n' '}\n' ) % {'ino': i.label.dummy_index, 'mno': m.dummy_index} r = make_routine('test_dummies', Eq(y[i], x[i])) c = CCodeGen() code = get_string(c.dump_c, [r]) assert code == expected
def test_dummy_loops_f95(): from sympy.tensor import IndexedBase, Idx i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'subroutine test_dummies(m_%(mcount)i, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m_%(mcount)i\n' 'REAL*8, intent(in), dimension(1:m_%(mcount)i) :: x\n' 'REAL*8, intent(out), dimension(1:m_%(mcount)i) :: y\n' 'INTEGER*4 :: i_%(icount)i\n' 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do\n' 'end subroutine\n' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} r = make_routine('test_dummies', Eq(y[i], x[i])) c = FCodeGen() code = get_string(c.dump_f95, [r]) assert code == expected
def test_dummy_loops_c(): from sympy.tensor import IndexedBase, Idx i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( '#include "file.h"\n' '#include <math.h>\n' 'void test_dummies(int m_%(mno)i, double *x, double *y) {\n' ' for (int i_%(ino)i=0; i_%(ino)i<m_%(mno)i; i_%(ino)i++){\n' ' y[i_%(ino)i] = x[i_%(ino)i];\n' ' }\n' '}\n' ) % {'ino': i.label.dummy_index, 'mno': m.dummy_index} r = make_routine('test_dummies', Eq(y[i], x[i])) c89 = C89CodeGen() c99 = C99CodeGen() code = get_string(c99.dump_c, [r]) assert code == expected with raises(NotImplementedError): get_string(c89.dump_c, [r])
def test_f_code_call_signature_wrap(): # Issue #7934 x = symbols('x:20') expr = 0 for sym in x: expr += sym routine = make_routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = """\ REAL*8 function test(x0, x1, x10, x11, x12, x13, x14, x15, x16, x17, x18, & x19, x2, x3, x4, x5, x6, x7, x8, x9) implicit none REAL*8, intent(in) :: x0 REAL*8, intent(in) :: x1 REAL*8, intent(in) :: x10 REAL*8, intent(in) :: x11 REAL*8, intent(in) :: x12 REAL*8, intent(in) :: x13 REAL*8, intent(in) :: x14 REAL*8, intent(in) :: x15 REAL*8, intent(in) :: x16 REAL*8, intent(in) :: x17 REAL*8, intent(in) :: x18 REAL*8, intent(in) :: x19 REAL*8, intent(in) :: x2 REAL*8, intent(in) :: x3 REAL*8, intent(in) :: x4 REAL*8, intent(in) :: x5 REAL*8, intent(in) :: x6 REAL*8, intent(in) :: x7 REAL*8, intent(in) :: x8 REAL*8, intent(in) :: x9 test = x0 + x1 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + & x19 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 end function """ assert source == expected
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 SymPy 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. Note ---- 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 sympy.utilities.autowrap import ufuncify >>> from sympy.abc import x, y >>> import numpy as np >>> f = ufuncify((x, y), y + x**2) >>> type(f) <class 'numpy.ufunc'> >>> 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) array([ 3.]) >>> f_fortran(np.array([1, 2, 3]), np.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) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int) >>> f_cython(np.array([1.0]), np.array([2.0])) array([ 3.]) """ if isinstance(args, 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': # maxargs is set by numpy compile-time constant NPY_MAXARGS # If a future version of numpy modifies or removes this restriction # this variable should be changed or removed maxargs = 32 helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) code_wrapper = UfuncifyCodeWrapper(C99CodeGen("ufuncify"), tempdir, flags, verbose) if not isinstance(expr, (list, tuple)): expr = [expr] if len(expr) == 0: raise ValueError('Expression iterable has zero length') if (len(expr) + len(args)) > maxargs: raise ValueError( 'Cannot create ufunc with more than {0} total arguments: got {1} in, {2} out' .format(maxargs, len(args), len(expr))) routines = [ make_routine('autofunc{}'.format(idx), exprx, args) for idx, exprx in enumerate(expr) ] return code_wrapper.wrap_code(routines, 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, args, flags, verbose, helpers)
def autowrap(expr, language=None, backend='f2py', tempdir=None, args=None, flags=None, verbose=False, helpers=None): """Generates python callable binaries based on the math expression. Parameters ---------- expr The SymPy expression that should be wrapped as a binary routine. 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 'f2py' [default], or 'cython'. 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. args : iterable, optional An ordered iterable of symbols. Specifies the argument sequence for the function. 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. >>> from sympy.abc import x, y, z >>> from sympy.utilities.autowrap import autowrap >>> expr = ((x - y + z)**(13)).expand() >>> binary_func = autowrap(expr) >>> binary_func(1, 4, 2) -1.0 """ if language: _validate_backend_language(backend, language) else: language = _infer_language(backend) helpers = [helpers] if helpers else () flags = flags if flags else () args = list(args) if iterable(args, exclude=set) else args code_generator = get_code_generator(language, "autowrap") CodeWrapperClass = _get_code_wrapper_class(backend) code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) helps = [] for name_h, expr_h, args_h in helpers: helps.append(make_routine(name_h, expr_h, args_h)) for name_h, expr_h, args_h in helpers: if expr.has(expr_h): name_h = binary_function(name_h, expr_h, backend='dummy') expr = expr.subs(expr_h, name_h(*args_h)) try: routine = make_routine('autofunc', expr, args) except CodeGenArgumentListError as e: # if all missing arguments are for pure output, we simply attach them # at the end and try again, because the wrappers will silently convert # them to return values anyway. new_args = [] for missing in e.missing_args: if not isinstance(missing, OutputArgument): raise new_args.append(missing.name) routine = make_routine('autofunc', expr, args + new_args) return code_wrapper.wrap_code(routine, helpers=helps)
def test_no_results_f(): raises(ValueError, lambda: make_routine("test", []))
def ufuncify(args, expr, language=None, backend='numpy', tempdir=None, flags=None, verbose=False, helpers=None, **kwargs): """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 SymPy 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 auxiliary 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. kwargs : dict These kwargs will be passed to autowrap if the `f2py` or `cython` backend is used and ignored if the `numpy` backend is used. 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 sympy.utilities.autowrap import ufuncify >>> from sympy.abc import x, y >>> import numpy as np >>> f = ufuncify((x, y), y + x**2) >>> type(f) <class 'numpy.ufunc'> >>> 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) array([ 3.]) >>> f_fortran(np.array([1, 2, 3]), np.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) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int) >>> f_cython(np.array([1.0]), np.array([2.0])) array([ 3.]) """ if isinstance(args, 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': # maxargs is set by numpy compile-time constant NPY_MAXARGS # If a future version of numpy modifies or removes this restriction # this variable should be changed or removed maxargs = 32 helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) code_wrapper = UfuncifyCodeWrapper(C99CodeGen("ufuncify"), tempdir, flags, verbose) if not isinstance(expr, (list, tuple)): expr = [expr] if len(expr) == 0: raise ValueError('Expression iterable has zero length') if (len(expr) + len(args)) > maxargs: msg = ('Cannot create ufunc with more than {0} total arguments: ' 'got {1} in, {2} out') raise ValueError(msg.format(maxargs, len(args), len(expr))) routines = [make_routine('autofunc{}'.format(idx), exprx, args) for idx, exprx in enumerate(expr)] return code_wrapper.wrap_code(routines, helpers=helps) else: # Dummies are used for all added expressions to prevent name clashes # within the original expression. y = IndexedBase(Dummy('y')) m = Dummy('m', integer=True) i = Idx(Dummy('i', integer=True), m) f_dummy = Dummy('f') f = implemented_function('%s_%d' % (f_dummy.name, f_dummy.dummy_index), 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, args, flags, verbose, helpers, **kwargs)
def autowrap( expr, language=None, backend='f2py', tempdir=None, args=None, flags=None, verbose=False, helpers=None): """Generates python callable binaries based on the math expression. Parameters ---------- expr The SymPy expression that should be wrapped as a binary routine. 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 'f2py' [default], or 'cython'. 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. args : iterable, optional An iterable of symbols. Specifies the argument sequence for the function. 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. >>> from sympy.abc import x, y, z >>> from sympy.utilities.autowrap import autowrap >>> expr = ((x - y + z)**(13)).expand() >>> binary_func = autowrap(expr) >>> binary_func(1, 4, 2) -1.0 """ if language: _validate_backend_language(backend, language) else: language = _infer_language(backend) helpers = helpers if helpers else () flags = flags if flags else () code_generator = get_code_generator(language, "autowrap") CodeWrapperClass = _get_code_wrapper_class(backend) code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) try: routine = make_routine('autofunc', expr, args) except CodeGenArgumentListError as e: # if all missing arguments are for pure output, we simply attach them # at the end and try again, because the wrappers will silently convert # them to return values anyway. new_args = [] for missing in e.missing_args: if not isinstance(missing, OutputArgument): raise new_args.append(missing.name) routine = make_routine('autofunc', expr, args + new_args) helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) return code_wrapper.wrap_code(routine, helpers=helps)
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 SymPy 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>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. Note ---- 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 sympy.utilities.autowrap import ufuncify >>> from sympy.abc import x, y >>> import numpy as np >>> f = ufuncify((x, y), y + x**2) >>> type(f) numpy.ufunc >>> 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): File "<stdin>", line 1, in <module> 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, 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, args, flags, verbose, helpers)