def autowrap( expr, language='F95', backend='f2py', tempdir=None, args=None, flags=[], verbose=False, helpers=[]): """Generates python callable binaries based on the math expression. expr The SymPy expression that should be wrapped as a binary routine :Optional arguments: language The programming language to use, currently 'C' or 'F95' backend The wrapper backend to use, currently f2py or Cython tempdir 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 Sequence of the formal parameters of the generated code, if ommited the function signature is determined by the code generator. flags Additional option flags that will be passed to the backend verbose If True, autowrap will not mute the command line backends. This can be helpful for debugging. helpers Used to define auxillary expressions needed for the main expr. If the main expression need to do call a specialized function it should be put in the ``helpers`` list. 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) # doctest: +SKIP >>> binary_func(1, 4, 2) # doctest: +SKIP -1.0 """ code_generator = get_code_generator(language, "autowrap") CodeWrapperClass = _get_code_wrapper_class(backend) code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) try: routine = Routine('autofunc', expr, args) except CodeGenArgumentListError, 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 = Routine('autofunc', expr, args + new_args)
def generate_c_func(name, expr, in_args, column_major_storage=True, **kwargs): """ generates C function code for a sympy exrpession name is the name of the function expr is the matrix expression in_args is a list of tuples containing a variable-name string and the variable symbol. The symbol can be a matrix or a scalar. The sequence of the arguments will be the sequence of in_args and the output (called 'out') is last """ if column_major_storage: # generated C code matrix access is in row-major order # [source: sympy/printing/ccode.py:215, _print_MatrixElement()] # this is why we transpose the matrices in column-major mode expr = expr.transpose() subs_tab = [] arg_list = [] for argname, arg_sym in in_args: if isinstance(arg_sym, sp.Matrix): if column_major_storage: arg_sym = arg_sym.transpose() new_sym = sp.MatrixSymbol('__in__' + argname, *arg_sym.shape) subs_tab += sympy_util.matrix_subs(arg_sym, new_sym) arg_list.append(cg.InputArgument(new_sym, dimensions=new_sym.shape)) else: new_sym = sp.symbols(argname) subs_tab.append((arg_sym, new_sym)) arg_list.append(cg.InputArgument(new_sym)) out_sym = sp.MatrixSymbol('out', expr.shape[0], expr.shape[1]) out_arg = cg.OutputArgument(out_sym, out_sym, expr.subs(subs_tab), dimensions=out_sym.shape) arg_list.append(out_arg) no_return_val = [] no_local_vars = [] no_global_vars = [] code_gen = cg.get_code_generator("C", "projectname") routines = [cg.Routine(name, arg_list, no_return_val, no_local_vars, no_global_vars)] [(c_name, c_code), (h_name, c_header)] = code_gen.write(routines, "prefix", header=False) c_code = unused_param_warn_suppr(c_code, [argname for argname, _sym in in_args]) c_code = clean_c_code(c_code, **kwargs) return c_code
def run_test(label, routines, numerical_tests, language, commands, friendly=True): """A driver for the codegen tests. This driver assumes that a compiler ifort is present in the PATH and that ifort is (at least) a Fortran 90 compiler. The generated code is written in a temporary directory, together with a main program that validates the generated code. The test passes when the compilation and the validation run correctly. """ # Check input arguments before touching the file system language = language.upper() assert language in main_template assert language in numerical_test_template # Check that evironment variable makes sense clean = os.getenv('SYMPY_TEST_CLEAN_TEMP', 'always').lower() if clean not in ('always', 'success', 'never'): raise ValueError("SYMPY_TEST_CLEAN_TEMP must be one of the following: 'always', 'success' or 'never'.") # Do all the magic to compile, run and validate the test code # 1) prepare the temporary working directory, switch to that dir work = tempfile.mkdtemp("_sympy_%s_test" % language, "%s_" % label) oldwork = os.getcwd() os.chdir(work) # 2) write the generated code if friendly: # interpret the routines as a name_expr list and call the friendly # function codegen codegen(routines, language, "codegen", to_files=True) else: code_gen = get_code_generator(language, "codegen") code_gen.write(routines, "codegen", to_files=True) # 3) write a simple main program that links to the generated code, and that # includes the numerical tests test_strings = [] for fn_name, args, expected, threshold in numerical_tests: call_string = "%s(%s)-(%s)" % (fn_name, ",".join(str(arg) for arg in args), expected) if language == "F95": call_string = fortranize_double_constants(call_string) threshold = fortranize_double_constants(str(threshold)) test_strings.append(numerical_test_template[language] % { "call": call_string, "threshold": threshold, }) if language == "F95": f = file("main.f90", "w") elif language == "C": f = file("main.c", "w") else: raise NotImplemented( "FIXME: filename extension unknown for language: %s"%language) f.write(main_template[language] % {'statements': "".join(test_strings)}) f.close() # 4) Compile and link compiled = try_run(commands) # 5) Run if compiled if compiled: executed = try_run(["./test.exe"]) else: executed = False # 6) Clean up stuff if clean == 'always' or (clean == 'success' and compiled and executed): def safe_remove(filename): if os.path.isfile(filename): os.remove(filename) safe_remove("codegen.f90") safe_remove("codegen.c") safe_remove("codegen.h") safe_remove("codegen.o") safe_remove("main.f90") safe_remove("main.c") safe_remove("main.o") safe_remove("test.exe") os.chdir(oldwork) os.rmdir(work) else: print >> sys.stderr, "TEST NOT REMOVED: %s" % work os.chdir(oldwork) # 7) Do the assertions in the end assert compiled, "failed to compile %s code with:\n%s" %(language, "\n".join(commands)) assert executed, "failed to execute %s code from:\n%s" %(language, "\n".join(commands))
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 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 autowrap(expr, language=None, backend='f2py', tempdir=None, args=None, flags=None, verbose=False, helpers=None, code_gen=None, **kwargs): """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 : 3-tuple or iterable of 3-tuples, optional Used to define auxiliary expressions needed for the main expr. If the main expression needs to call a specialized function it should be passed in via ``helpers``. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be 3-tuples with (<function_name>, <sympy_expression>, <argument_tuple>). It is mandatory to supply an argument sequence to helper routines. code_gen : CodeGen instance An instance of a CodeGen subclass. Overrides ``language``. include_dirs : [string] A list of directories to search for C/C++ header files (in Unix form for portability). library_dirs : [string] A list of directories to search for C/C++ libraries at link time. libraries : [string] A list of library names (not filenames or paths) to link against. extra_compile_args : [string] Any extra platform- and compiler-specific information to use when compiling the source files in 'sources'. For platforms and compilers where "command line" makes sense, this is typically a list of command-line arguments, but for other platforms it could be anything. extra_link_args : [string] Any extra platform- and compiler-specific information to use when linking object files together to create the extension (or to create a new static Python interpreter). Similar interpretation as for 'extra_compile_args'. Examples ======== >>> 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: if not isinstance(language, type): _validate_backend_language(backend, language) else: language = _infer_language(backend) # two cases 1) helpers is an iterable of 3-tuples and 2) helpers is a # 3-tuple if iterable(helpers) and len(helpers) != 0 and iterable(helpers[0]): helpers = helpers if helpers else () else: helpers = [helpers] if helpers else () args = list(args) if iterable(args, exclude=set) else args if code_gen is None: code_gen = get_code_generator(language, "autowrap") CodeWrapperClass = { 'F2PY': F2PyCodeWrapper, 'CYTHON': CythonCodeWrapper, 'DUMMY': DummyWrapper }[backend.upper()] code_wrapper = CodeWrapperClass(code_gen, tempdir, flags if flags else (), verbose, **kwargs) helps = [] for name_h, expr_h, args_h in helpers: helps.append(code_gen.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 = code_gen.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 = code_gen.routine('autofunc', expr, args + new_args) return code_wrapper.wrap_code(routine, helpers=helps)
def run_test(label, routines, numerical_tests, language, commands, friendly=True): """A driver for the codegen tests. This driver assumes that a compiler ifort is present in the PATH and that ifort is (at least) a Fortran 90 compiler. The generated code is written in a temporary directory, together with a main program that validates the generated code. The test passes when the compilation and the validation run correctly. """ # Check input arguments before touching the file system language = language.upper() assert language in main_template assert language in numerical_test_template # Check that environment variable makes sense clean = os.getenv('SYMPY_TEST_CLEAN_TEMP', 'always').lower() if clean not in ('always', 'success', 'never'): raise ValueError("SYMPY_TEST_CLEAN_TEMP must be one of the following: 'always', 'success' or 'never'.") # Do all the magic to compile, run and validate the test code # 1) prepare the temporary working directory, switch to that dir work = tempfile.mkdtemp("_sympy_%s_test" % language, "%s_" % label) oldwork = os.getcwd() os.chdir(work) # 2) write the generated code if friendly: # interpret the routines as a name_expr list and call the friendly # function codegen codegen(routines, language, "codegen", to_files=True) else: code_gen = get_code_generator(language, "codegen") code_gen.write(routines, "codegen", to_files=True) # 3) write a simple main program that links to the generated code, and that # includes the numerical tests test_strings = [] for fn_name, args, expected, threshold in numerical_tests: call_string = "%s(%s)-(%s)" % ( fn_name, ",".join(str(arg) for arg in args), expected) if language == "F95": call_string = fortranize_double_constants(call_string) threshold = fortranize_double_constants(str(threshold)) test_strings.append(numerical_test_template[language] % { "call": call_string, "threshold": threshold, }) if language == "F95": f_name = "main.f90" elif language.startswith("C"): f_name = "main.c" else: raise NotImplementedError( "FIXME: filename extension unknown for language: %s" % language) with open(f_name, "w") as f: f.write( main_template[language] % {'statements': "".join(test_strings)}) # 4) Compile and link compiled = try_run(commands) # 5) Run if compiled if compiled: executed = try_run(["./test.exe"]) else: executed = False # 6) Clean up stuff if clean == 'always' or (clean == 'success' and compiled and executed): def safe_remove(filename): if os.path.isfile(filename): os.remove(filename) safe_remove("codegen.f90") safe_remove("codegen.c") safe_remove("codegen.h") safe_remove("codegen.o") safe_remove("main.f90") safe_remove("main.c") safe_remove("main.o") safe_remove("test.exe") os.chdir(oldwork) os.rmdir(work) else: print("TEST NOT REMOVED: %s" % work, file=sys.stderr) os.chdir(oldwork) # 7) Do the assertions in the end assert compiled, "failed to compile %s code with:\n%s" % ( language, "\n".join(commands)) assert executed, "failed to execute %s code from:\n%s" % ( language, "\n".join(commands))
def autowrap(expr, language='F95', backend='f2py', tempdir=None, args=None, flags=[], verbose=False, helpers=[]): """Generates python callable binaries based on the math expression. expr The SymPy expression that should be wrapped as a binary routine :Optional arguments: language The programming language to use, currently 'C' or 'F95' backend The wrapper backend to use, currently f2py or Cython tempdir 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 Sequence of the formal parameters of the generated code, if ommited the function signature is determined by the code generator. flags Additional option flags that will be passed to the backend verbose If True, autowrap will not mute the command line backends. This can be helpful for debugging. helpers Used to define auxillary expressions needed for the main expr. If the main expression need to do call a specialized function it should be put in the ``helpers`` list. 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) # doctest: +SKIP >>> binary_func(1, 4, 2) # doctest: +SKIP -1.0 """ code_generator = get_code_generator(language, "autowrap") CodeWrapperClass = _get_code_wrapper_class(backend) code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) try: routine = Routine('autofunc', expr, args) except CodeGenArgumentListError, 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 = Routine('autofunc', expr, args + new_args)
def ekf_generate_c_code(f, F, h, H, x, u, f_additional_in=[], h_additional_in=[]): # generated C code matrix access is in row-major order # [source: sympy/printing/ccode.py:215, _print_MatrixElement()] # Since Eigen uses column-major by default we transpose the matrices before # generating the C code H = H.transpose() F = F.transpose() f_additional_in_sym = [sp.symbols('in{}_{}'.format(i+2, var.name)) for i, var in enumerate(f_additional_in)] f_add_subs_tab = list(zip(f_additional_in, f_additional_in_sym)) h_additional_in_sym = [sp.symbols('in{}_{}'.format(i+1, var.name)) for i, var in enumerate(h_additional_in)] h_add_subs_tab = list(zip(h_additional_in, h_additional_in_sym)) x_sym = sp.MatrixSymbol('in0_x', len(x), 1) x_subs_tab = [(elem_sym, x_sym[i, 0]) for i, elem_sym in enumerate(x)] u_sym = sp.MatrixSymbol('in1_u', len(u), 1) u_subs_tab = [(elem_sym, u_sym[i, 0]) for i, elem_sym in enumerate(u)] subs_tab = x_subs_tab + u_subs_tab + f_add_subs_tab + h_add_subs_tab # c_code = generate_c_code('f', f.subs(subs_tab)) + '\n' # c_code += generate_c_code('F', F.subs(subs_tab)) + '\n' # c_code += generate_c_code('h', h.subs(subs_tab)) + '\n' # c_code += generate_c_code('H', H.subs(subs_tab)) + '\n' no_return_val = [] no_local_vars = [] f_additional_in_arg = [cg.InputArgument(sym) for sym in f_additional_in_sym] h_additional_in_arg = [cg.InputArgument(sym) for sym in h_additional_in_sym] f_out_sym = sp.MatrixSymbol('f_out', len(x), 1) f_arg_list = [cg.InputArgument(x_sym, dimensions=x_sym.shape), cg.InputArgument(u_sym, dimensions=u_sym.shape)] f_arg_list += f_additional_in_arg f_arg_list += [cg.OutputArgument(f_out_sym, f_out_sym, f.subs(subs_tab), dimensions=f_out_sym.shape)] F_out_sym = sp.MatrixSymbol('F_out', F.shape[0], F.shape[1]) F_arg_list = [cg.InputArgument(x_sym, dimensions=x_sym.shape), cg.InputArgument(u_sym, dimensions=u_sym.shape)] F_arg_list += f_additional_in_arg F_arg_list += [cg.OutputArgument(F_out_sym, F_out_sym, F.subs(subs_tab), dimensions=F_out_sym.shape)] h_out_sym = sp.MatrixSymbol('h_out', len(h), 1) h_arg_list = [cg.InputArgument(x_sym, dimensions=x_sym.shape)] h_arg_list += h_additional_in_arg h_arg_list += [cg.OutputArgument(h_out_sym, h_out_sym, h.subs(subs_tab), dimensions=h_out_sym.shape)] H_out_sym = sp.MatrixSymbol('H_out', H.shape[0], H.shape[1]) H_arg_list = [cg.InputArgument(x_sym, dimensions=x_sym.shape)] H_arg_list += h_additional_in_arg H_arg_list += [cg.OutputArgument(H_out_sym, H_out_sym, H.subs(subs_tab), dimensions=H_out_sym.shape)] routines = [cg.Routine("f", f_arg_list, no_return_val, no_local_vars), cg.Routine("F", F_arg_list, no_return_val, no_local_vars), cg.Routine("h", h_arg_list, no_return_val, no_local_vars), cg.Routine("H", H_arg_list, no_return_val, no_local_vars)] code_gen = cg.get_code_generator("C", "projectname") [(c_name, c_code), (h_name, c_header)] = code_gen.write(routines, "prefix", header=False) c_code = clean_c_code(c_code, use_single_float=True) c_code_head = '// This file has been automatically generated\n' c_code_head += '// DO NOT EDIT!\n\n' c_code_head += '#include <math.h>\n\n' c_code_head += 'const int STATE_DIM = {};\n'.format(len(x)) c_code_head += 'const int CONTROL_DIM = {};\n'.format(len(u)) c_code_head += 'const int MEASURE_DIM = {};\n'.format(len(h)) c_code_head += '\n\n' return c_code_head + c_code