def generate_helpers_C(self, chunk_size=100): """ translates the helpers to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. Parameters ---------- chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your helpers, the group size suggests itself for `chunk_size`. If smaller than 1, no chunking will happen. """ render_declarations( (helper[0] for helper in self.helpers), self._tmpfile("declare_general_helpers.c") ) render_and_write_code( [], self.helpers, self._tmpfile(), "helpers", {"y":"y"}, chunk_size = chunk_size )
def generate_helpers_C(self, chunk_size=100): """ translates the helpers to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. Parameters ---------- chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your helpers, the group size suggests itself for `chunk_size`. If smaller than 1, no chunking will happen. """ if self.helpers: get_helper = sympy.Function("get_general_helper") set_helper = sympy.Function("set_general_helper") self.helper_subs = [(helper[0],get_helper(i)) for i,helper in enumerate(self.helpers)] render_and_write_code( (set_helper(i, helper[1].subs(self.helper_subs)) for i,helper in enumerate(self.helpers)), self._tmpfile, "general_helpers", ["y", "get_general_helper", "set_general_helper"], chunk_size = chunk_size, arguments = [("Y", "PyArrayObject *restrict const"), ("general_helper","double *restrict const")] ) self._helper_C_source = True
def generate_jac_C(self, do_cse=False, chunk_size=100, sparse=True): """ translates the symbolic Jacobian to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. If the symbolic Jacobian has not been generated, it generates it by calling `generate_jac_sym`. Parameters ---------- do_cse : boolean Whether SymPy’s `common-subexpression detection <http://docs.sympy.org/dev/modules/rewriting.html#module-sympy.simplify.cse_main>`_ should be applied before translating to C code. It is almost always better to let the compiler do this (unless you want to set the compiler optimisation to `-O2` or lower): For simple differential equations this should not make any difference to the compiler’s optimisations. For large ones, it may make a difference but also take long. As this requires the entire Jacobian at once, it may void advantages gained from using generator functions as an input. chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your Jacobian, the respective group size suggests itself for `chunk_size`. For example, the derivative of each dynamical variable explicitly depends on 60 others and the Jacobian is sparse, a chunk size of 60 suggests itself. If smaller than 1, no chunking will happen. sparse : boolean Whether a sparse Jacobian should be assumed for optimisation. Note that this does not mean that the Jacobian is stored, parsed or handled as a sparse matrix. This kind of optimisation would require SciPy’s ODE to be able to handle sparse matrices. """ self._generate_jac_sym() jac_sym_wc = self.jac_sym self.sparse_jac = sparse if do_cse: jac_matrix = sympy.Matrix([ [entry for entry in line] for line in jac_sym_wc ]) _cse = sympy.cse( jac_matrix, symbols = sympy.numbered_symbols("dummy_jac_") ) more_helpers = _cse[0] jac_sym_wc = _cse[1][0].tolist() else: more_helpers = [] render_declarations( (helper[0] for helper in more_helpers), self._tmpfile("declare_jac_helpers.c") ) set_dfdy = sympy.Function("set_dfdy") render_and_write_code( ( set_dfdy(i,j,entry) for i,line in enumerate(jac_sym_wc) for j,entry in enumerate(line) if ( (entry != 0) or not self.sparse_jac ) ), more_helpers, self._tmpfile(), "jac", {"set_dfdy":"set_dfdy", "y":"y"}, chunk_size = chunk_size ) self._jac_C_source = True
def generate_f_C(self, simplify=True, do_cse=False, chunk_size=100): """ translates the derivative to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. Parameters ---------- simplify : boolean Whether the derivative should be `simplified <http://docs.sympy.org/dev/modules/simplify/simplify.html>`_ (with `ratio=1.0`) before translating to C code. The main reason why you could want to disable this is if your derivative is already optimised and so large that simplifying takes a considerable amount of time. do_cse : boolean Whether SymPy’s `common-subexpression detection <http://docs.sympy.org/dev/modules/rewriting.html#module-sympy.simplify.cse_main>`_ should be applied before translating to C code. It is almost always better to let the compiler do this (unless you want to set the compiler optimisation to `-O2` or lower): For simple differential equations this should not make any difference to the compiler’s optimisations. For large ones, it may make a difference but also take long. As this requires all entries of `f` at once, it may void advantages gained from using generator functions as an input. chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your :math:`f`, the group size suggests itself for `chunk_size`. For example, if you want to simulate the dynamics of three-dimensional oscillators coupled onto a 40×40 lattice and if the differential equations are grouped first by oscillator and then by lattice row, a chunk size of 120 suggests itself. If smaller than 1, no chunking will happen. """ f_sym_wc = self.f_sym() if simplify: f_sym_wc = (sympy.simplify(entry,ratio=1) for entry in f_sym_wc) if do_cse: _cse = sympy.cse( sympy.Matrix(list(self.f_sym())), symbols = sympy.numbered_symbols("dummy_f_") ) more_helpers = _cse[0] f_sym_wc = _cse[1][0] else: more_helpers = [] render_declarations( (helper[0] for helper in more_helpers), self._tmpfile("declare_f_helpers.c") ) set_dy = sympy.Function("set_dy") render_and_write_code( (set_dy(i,entry) for i,entry in enumerate(f_sym_wc)), more_helpers, self._tmpfile(), "f", {"set_dy":"set_dy", "y":"y"}, chunk_size = chunk_size ) self._f_C_source = True
def generate_jac_C(self, do_cse=False, chunk_size=100, sparse=True): """ translates the symbolic Jacobian to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. If the symbolic Jacobian has not been generated, it generates it by calling `generate_jac_sym`. Parameters ---------- do_cse : boolean Whether SymPy’s `common-subexpression detection <http://docs.sympy.org/dev/modules/rewriting.html#module-sympy.simplify.cse_main>`_ should be applied before translating to C code. It is almost always better to let the compiler do this (unless you want to set the compiler optimisation to `-O2` or lower): For simple differential equations this should not make any difference to the compiler’s optimisations. For large ones, it may make a difference but also take long. As this requires the entire Jacobian at once, it may void advantages gained from using generator functions as an input. chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your Jacobian, the respective group size suggests itself for `chunk_size`. For example, the derivative of each dynamical variable explicitly depends on 60 others and the Jacobian is sparse, a chunk size of 60 suggests itself. If smaller than 1, no chunking will happen. sparse : boolean Whether a sparse Jacobian should be assumed for optimisation. Note that this does not mean that the Jacobian is stored, parsed or handled as a sparse matrix. This kind of optimisation would require SciPy’s ODE to be able to handle sparse matrices. """ self._generate_helpers_C() self._generate_jac_sym() jac_sym_wc = sympy.Matrix([ [entry.subs(self.helper_subs) for entry in line] for line in self.jac_sym ]) self.sparse_jac = sparse arguments = [("Y", "PyArrayObject *restrict const")] if self._number_of_general_helpers: arguments.append(("general_helper","double const *restrict const")) if do_cse: get_helper = sympy.Function("get_jac_helper") set_helper = sympy.Function("set_jac_helper") _cse = sympy.cse( jac_sym_wc, symbols = (get_helper(i) for i in count()) ) more_helpers = _cse[0] jac_sym_wc = _cse[1][0] if more_helpers: arguments.append(("jac_helper","double *restrict const")) render_and_write_code( (set_helper(i, helper[1]) for i,helper in enumerate(more_helpers)), self._tmpfile, "jac_helpers", ["y", "get_jac_helper", "set_jac_helper", "get_general_helper"], chunk_size = chunk_size, arguments = arguments ) self._number_of_jac_helpers = len(more_helpers) jac_sym_wc = jac_sym_wc.tolist() set_dfdy = sympy.Function("set_dfdy") render_and_write_code( ( set_dfdy(i,j,entry) for i,line in enumerate(jac_sym_wc) for j,entry in enumerate(line) if ( (entry != 0) or not self.sparse_jac ) ), self._tmpfile, "jac", ["set_dfdy", "y", "get_jac_helper", "get_general_helper"], chunk_size = chunk_size, arguments = arguments+[("dfdY", "PyArrayObject *restrict const")] ) self._jac_C_source = True
def generate_f_C(self, simplify=True, do_cse=False, chunk_size=100): """ translates the derivative to C code using SymPy’s `C-code printer <http://docs.sympy.org/dev/modules/printing.html#module-sympy.printing.ccode>`_. Parameters ---------- simplify : boolean Whether the derivative should be `simplified <http://docs.sympy.org/dev/modules/simplify/simplify.html>`_ (with `ratio=1.0`) before translating to C code. The main reason why you could want to disable this is if your derivative is already optimised and so large that simplifying takes a considerable amount of time. do_cse : boolean Whether SymPy’s `common-subexpression detection <http://docs.sympy.org/dev/modules/rewriting.html#module-sympy.simplify.cse_main>`_ should be applied before translating to C code. It is almost always better to let the compiler do this (unless you want to set the compiler optimisation to `-O2` or lower): For simple differential equations this should not make any difference to the compiler’s optimisations. For large ones, it may make a difference but also take long. As this requires all entries of `f` at once, it may void advantages gained from using generator functions as an input. chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. After the generation of each chunk, SymPy’s cache is cleared. See `large_systems` on why this is useful. If there is an obvious grouping of your :math:`f`, the group size suggests itself for `chunk_size`. For example, if you want to simulate the dynamics of three-dimensional oscillators coupled onto a 40×40 lattice and if the differential equations are grouped first by oscillator and then by lattice row, a chunk size of 120 suggests itself. If smaller than 1, no chunking will happen. """ self._generate_helpers_C() f_sym_wc = self.f_sym() if simplify: f_sym_wc = (sympy.simplify(entry,ratio=1) for entry in f_sym_wc) if self.helpers: f_sym_wc = (entry.subs(self.helper_subs) for entry in f_sym_wc) arguments = [("Y", "PyArrayObject *restrict const")] if self._number_of_general_helpers: arguments.append(("general_helper","double const *restrict const")) if do_cse: get_helper = sympy.Function("get_f_helper") set_helper = sympy.Function("set_f_helper") _cse = sympy.cse( sympy.Matrix(list(f_sym_wc)), symbols = (get_helper(i) for i in count()) ) more_helpers = _cse[0] f_sym_wc = _cse[1][0] if more_helpers: arguments.append(("f_helper","double *restrict const")) render_and_write_code( (set_helper(i, helper[1]) for i,helper in enumerate(more_helpers)), self._tmpfile, "f_helpers", ["y", "get_f_helper", "set_f_helper", "get_general_helper"], chunk_size = chunk_size, arguments = arguments ) self._number_of_f_helpers = len(more_helpers) set_dy = sympy.Function("set_dy") render_and_write_code( (set_dy(i,entry) for i,entry in enumerate(f_sym_wc)), self._tmpfile, "f", ["set_dy", "y", "get_f_helper", "get_general_helper"], chunk_size = chunk_size, arguments = arguments+[("dY", "PyArrayObject *restrict const")] ) self._f_C_source = True