Esempio n. 1
0
def generate_symbolic_tfs(stages=4):
    import sympy as sp

    # construct quad pendulum equation of motion matrix
    ksyms = sp.numbered_symbols('k')
    msyms = sp.numbered_symbols('m')
    w = sp.symbols('w')
    k = [next(ksyms) for n in range(stages)]
    m = [next(msyms) for n in range(stages)]
    A = sp.zeros(stages)
    for n in range(stages-1):
        # mass and restoring forces (diagonal elements)
        A[n, n] = k[n] + k[n+1] - m[n] * w**2
        # couplings to stages above and below
        A[n, n+1] = -k[n+1]
        A[n+1, n] = -k[n+1]
    # mass and restoring force of bottom stage
    A[-1, -1] = k[-1] - m[-1] * w**2

    # want TM equations of motion, so index 4
    b = sp.zeros(stages, 1)
    b[-1] = 1

    # solve linear system
    xsyms = sp.numbered_symbols('x')
    x = [next(xsyms) for n in range(stages)]
    ans = sp.linsolve((A, b), x)
    return ans
Esempio n. 2
0
 def __init__(self, n=4, h=4, canonic=True):
     if canonic:
         x, y = sym.symbols('x, y')
         a = tuple(take(n, sym.numbered_symbols('a', start=1)))
     else:
         x, y = sym.symbols('z_2, z_1', real=True)
         a = tuple(-r for r in take(
             n, sym.numbered_symbols('r', start=1, real=True)))
     super().__init__(x, y, a, h)
Esempio n. 3
0
def cse2func(funcname,
             exprs,
             module=math,
             auto_import=True,
             calc_number=True,
             symbols="_tmp"):
    import textwrap
    printer = CseExprPrinter(settings={
        "func_module": module,
        "calc_number": calc_number
    })
    seq, results = cse(exprs, symbols=numbered_symbols(symbols))
    codes = ["def %s:" % funcname]
    for variable, value in seq:
        if calc_number:
            value = value.evalf()
        codes.append("%s = %s" % (variable, printer._print(value)))
    returns = "return (%s)" % ", ".join(
        [printer._print(value) for value in results])
    codes.append('\n'.join(textwrap.wrap(returns, 80)))
    if auto_import and printer.functions:
        codes.insert(
            1, "from {} import {}".format(module.__name__,
                                          ", ".join(printer.functions)))
    code = '\n    '.join(codes)
    return code
Esempio n. 4
0
def OutputVector_CollectingFactorsNonZero(A,name, mode, initial_tabs = 1, max_index=30, optimizations='basic'):
    """ This method collects the constants of the replacement for vectors (only non-zero terms)

    Keyword arguments:
    A -- The  factors
    name -- The name of the constant
    mode -- The mode of replacement
    initial_tabs -- The number of initial tabulations
    max_index -- The max number of indexes
    optimizations -- The level of optimizations
    """
    symbol_name = "c" + name
    A_factors, A_collected = sympy.cse(A, sympy.numbered_symbols(symbol_name), optimizations)
    A = A_collected[0] # Overwrite lhs with the one with the collected components

    aux_dict = {}

    Acoefficient_str = ""
    for factor in A_factors:
        varname = factor[0]
        value = factor[1]
        output_value = OutputSymbolicVariable(value, mode, varname, aux_dict)
        Acoefficient_str += "    const double " + varname.__str__() + " = " + output_value
    A_out = Acoefficient_str + "\n" + OutputVectorNonZero(A, name, mode, initial_tabs, max_index, aux_dict)
    return A_out
Esempio n. 5
0
    def get_cse_code(self, exprs, basename=None,
                     dummy_groups=(), arrayify_groups=()):
        """ Get arrayified code for common subexpression.

        Parameters
        ----------
        exprs : list of sympy expressions
        basename : str
            Stem of variable names (default: cse).
        dummy_groups : tuples

        """
        if basename is None:
            basename = 'cse'
        cse_defs, cse_exprs = sympy.cse(
            exprs, symbols=sympy.numbered_symbols(basename))

        # Let's convert the new expressions into (arrayified) code
        cse_defs_code = [
            (vname, self.as_arrayified_code(
                vexpr, dummy_groups, arrayify_groups))
            for vname, vexpr in cse_defs
        ]
        cse_exprs_code = [self.as_arrayified_code(
            x, dummy_groups, arrayify_groups) for x in cse_exprs]
        return cse_defs_code, cse_exprs_code
Esempio n. 6
0
def test_satisfiable_all_models():
    from sympy.abc import A, B
    assert next(satisfiable(False, all_models=True)) is False
    assert list(satisfiable((A >> ~A) & A, all_models=True)) == [False]
    assert list(satisfiable(True, all_models=True)) == [{true: true}]

    models = [{A: True, B: False}, {A: False, B: True}]
    result = satisfiable(A ^ B, all_models=True)
    models.remove(next(result))
    models.remove(next(result))
    raises(StopIteration, lambda: next(result))
    assert not models

    assert list(satisfiable(Equivalent(A, B), all_models=True)) == \
    [{A: False, B: False}, {A: True, B: True}]

    models = [{A: False, B: False}, {A: False, B: True}, {A: True, B: True}]
    for model in satisfiable(A >> B, all_models=True):
        models.remove(model)
    assert not models

    # This is a santiy test to check that only the required number
    # of solutions are generated. The expr below has 2**100 - 1 models
    # which would time out the test if all are generated at once.
    from sympy import numbered_symbols
    from sympy.logic.boolalg import Or
    sym = numbered_symbols()
    X = [next(sym) for i in range(100)]
    result = satisfiable(Or(*X), all_models=True)
    for i in range(10):
        assert next(result)
Esempio n. 7
0
def Derivatives_CollectingFactors(A,name, mode, initial_tabs = 1, max_index=30, optimizations='basic', postprocess=None, order='canonical'):
    """ This method collects the constants of the replacement for derivatives
    TODO Think about collecting factors also in the derivatives

    Keyword arguments:
    A -- The  factors
    name -- The name of the constant
    mode -- The mode of replacement
    initial_tabs -- The number of initial tabulations
    max_index -- The max number of indexes
    optimizations -- The level of optimizations
    postprocess -- The type of postprocess
    order -- The type of order
    """
    symbol_name = "c" + name
    A_factors, A_collected = sympy.cse(A, sympy.numbered_symbols(symbol_name), optimizations, postprocess, order)
    A = A_collected

    Acoefficient_str = ""
    for factor in A_factors:
        varname = factor[0]
        value = factor[1]
        Acoefficient_str += "    const double " + varname.__str__() + " = " + value

    return [A, Acoefficient_str]
Esempio n. 8
0
def commonsubelim(filename1, filename2):
    modifiedlines = []
    with open(filename1, 'r') as file:
        for line in file:
            line = line.strip('\n')
            currentline = line + '\n'
            flag = False
            if ('=' in line):
                line = line.strip(';')
                lines = line.split('=')
                docse = False
                lines[1] = lines[1].replace(' ', '')
                docse = re.search("[0-9]", lines[1])
                if (not docse):
                    lines[1] = eval(lines[1])
                    compressedexpr = cse(lines[1], numbered_symbols("help"))
                    currentline = lines[0] + '='
                    for helper in compressedexpr[0]:
                        flag = True
                        #currentline=currentline+str(ccode(helper[1],helper[0]))+"\n"
                        modifiedlines.append("int " +
                                             str(ccode(helper[1], helper[0])) +
                                             "\n")
                    for i, result in enumerate(compressedexpr[1]):
                        modifiedlines.append("int " +
                                             (ccode(result, "result")))
                        modifiedlines.append("\n")
            if (flag == False):
                modifiedlines.append(currentline)
    with open(filename2, 'w') as file:
        for line in modifiedlines:
            file.write(line)
        file.write('\n')
        file.truncate()
Esempio n. 9
0
def com_sub_elim():
    X1, Y1, Z1, X2, Y2 = symbols('X1 Y1 Z1 X2 Y2')
    Q = [X1, Y1, Z1]
    P = [X2, Y2]
    Rx, Ry, Rz = mixadd_complete(Q, P)
    tree = tree_cse([Rx, Ry, Rz], numbered_symbols('e'))
    print(tree)
Esempio n. 10
0
def test_satisfiable_all_models():
    from sympy.abc import A, B
    assert next(satisfiable(False, all_models=True)) is False
    assert list(satisfiable((A >> ~A) & A , all_models=True)) == [False]
    assert list(satisfiable(True, all_models=True)) == [{true: true}]

    models = [{A: True, B: False}, {A: False, B: True}]
    result = satisfiable(A ^ B, all_models=True)
    models.remove(next(result))
    models.remove(next(result))
    raises(StopIteration, lambda: next(result))
    assert not models

    assert list(satisfiable(Equivalent(A, B), all_models=True)) == \
    [{A: False, B: False}, {A: True, B: True}]

    models = [{A: False, B: False}, {A: False, B: True}, {A: True, B: True}]
    for model in satisfiable(A >> B, all_models=True):
        models.remove(model)
    assert not models

    # This is a santiy test to check that only the required number
    # of solutions are generated. The expr below has 2**100 - 1 models
    # which would time out the test if all are generated at once.
    from sympy import numbered_symbols
    from sympy.logic.boolalg import Or
    sym = numbered_symbols()
    X = [next(sym) for i in range(100)]
    result = satisfiable(Or(*X), all_models=True)
    for i in range(10):
        assert next(result)
Esempio n. 11
0
	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
Esempio n. 12
0
    def generate_predict(self, f, u, Q, dt):
        ''' Generate a method for doing an EKF prediction step. f(X, u) -> X is
        the system dynamics model, X is a symbolic vector of each state
        variable name u is the symbolic control input vector. '''
        N = self.N
        F = f.jacobian(self.X)
        U = f.jacobian(u)
        vs, es = sp.cse([F - sp.eye(N), f - self.X, Q],
                        optimizations='basic',
                        symbols=sp.numbered_symbols("tmp"))

        self.generate_predict_cc(f, u, Q, dt, N, F, vs, es)
        self.generate_predict_py(f, u, Q, dt, N, F, vs, es)

        vs, es = sp.cse([f - self.X, F - sp.eye(N), U],
                        optimizations='basic',
                        symbols=sp.numbered_symbols("tmp"))
        self.generate_controlstep_py(f, u, dt, N, vs, es)
Esempio n. 13
0
    def _generate_cse(self, prefix='pydy_'):
        # NOTE : This assumes the first two items in self.matrices are A and b
        # of and Ax=b system. This also ignores cse=False.

        gen1 = sm.numbered_symbols(prefix)
        subexprs1, mats_simp = sm.cse(self.matrices, symbols=gen1)

        A_simp = mats_simp[0]
        b_simp = mats_simp[1]

        x = A_simp.LUsolve(b_simp)

        gen2 = sm.numbered_symbols(prefix, start=len(subexprs1))
        subexprs2, x_simp = sm.cse(x, symbols=gen2)

        # swap the b matrix with the x result
        mats_simp[1] = x_simp[0]

        self.subexprs = subexprs1 + subexprs2
        self.simplified_matrices = tuple(mats_simp)
Esempio n. 14
0
def _optimize(expressions):
    from sympy import cse, numbered_symbols
    commons, outputs = cse(
        expressions,
        numbered_symbols('t'),
        optimizations='basic',
    )
    for symbol, expr in commons:
        print(symbol, '=', expr)
    print()
    for expr in outputs:
        print(expr)
Esempio n. 15
0
def _optimize(expressions):
    from sympy import cse, numbered_symbols
    commons, outputs = cse(
        expressions,
        numbered_symbols('t'),
        optimizations='basic',
    )
    for symbol, expr in commons:
        print(symbol, '=', expr)
    print()
    for expr in outputs:
        print(expr)
Esempio n. 16
0
def cse_codegen(symbols):
    cse_results = sympy.cse(symbols, sympy.numbered_symbols("c"))
    output = io.StringIO()
    for helper in cse_results[0]:
        output.write("Scalar const ")
        output.write(sympy.printing.ccode(helper[1], helper[0]))
        output.write("\n")
    assert len(cse_results[1]) == 1

    output.write(sympy.printing.ccode(cse_results[1][0], "result"))
    output.write("\n")
    output.seek(0)
    return output
Esempio n. 17
0
    def generate_measurement(self, name, h_x, h_z, z_k, R_k):
        H = h_x.jacobian(self.X)
        M = h_z.jacobian(z_k) + h_x.jacobian(z_k)
        y_k = h_z - h_x
        vs, es = sp.cse([y_k, H, M],
                        optimizations='basic',
                        symbols=sp.numbered_symbols("tmp"))

        self.generate_measurement_cc(name, h_x, h_z, z_k, R_k, H, M, y_k, vs,
                                     es)

        self.generate_measurement_py(name, h_x, h_z, z_k, R_k, H, M, y_k, vs,
                                     es)
Esempio n. 18
0
	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
Esempio n. 19
0
 def _print_list(self, list_of_exprs):
     if all(isinstance(x, sp.Eq) for x in list_of_exprs):
         list_of_lhs = [x.lhs for x in list_of_exprs]
         list_of_rhs = [x.rhs for x in list_of_exprs]
         common_variables, list_of_rhs = sp.cse(
             list_of_rhs, symbols=sp.numbered_symbols(prefix='com'))
         lines = []
         for variable, expression in common_variables:
             ass = Assignment(variable, expression)
             lines.append('const double ' + self._print(ass))
         for i in range(len(list_of_rhs)):
             ass = Assignment(list_of_lhs[i], list_of_rhs[i])
             lines.append(self._print(ass))
     return '\n'.join(lines)
Esempio n. 20
0
def cse2func(funcname, exprs, module=math, auto_import=True, calc_number=True, symbols="_tmp"):
    import textwrap
    printer = CseExprPrinter(settings={"func_module": module, "calc_number": calc_number})
    seq, results = cse(exprs, symbols=numbered_symbols(symbols))
    codes = ["def %s:" % funcname]
    for variable, value in seq:
        if calc_number:
            value = value.evalf()
        codes.append("%s = %s" % (variable, printer._print(value)))
    returns = "return (%s)" % ", ".join([printer._print(value) for value in results])
    codes.append('\n'.join(textwrap.wrap(returns, 80)))
    if auto_import and printer.functions:
        codes.insert(1, "from {} import {}".format(module.__name__, ", ".join(printer.functions)))
    code = '\n    '.join(codes)
    return code
Esempio n. 21
0
    def test_lsystem_to_matrix_and_ordinate(self):
        """Should make a corresponding matrix from linear equations"""
        A = sympy.Matrix([[1, 1, 1], [1, 0, 2], [1, 11, 3]])
        a = [-2, 0, 0]
        vars_gen = sympy.numbered_symbols("V")
        vars_list = [next(vars_gen) for _ in range(0, 3)]
        equations = [
            vars_list[0] + vars_list[1] + vars_list[2] + 2,
            vars_list[0] + 2 * vars_list[2],
            vars_list[0] + 11 * vars_list[1] + 3 * vars_list[2],
        ]

        B, b = Equationeer().lsystem_to_matrix_and_ordinate(equations, vars_list)
        # print(B)
        self.assertEqual(A, B)
        self.assertEqual(a, b)
Esempio n. 22
0
def generate_grid(matrix_dimension, P,  frequency=1.02, ed=1, forced_em=None):    
    
    grid = Grid()
    
    def choose_conductivity(em, ed):
        """Uses given probability to simulate different concentrations of metallic particles"""
        return (em, True) if rnd.random() < P else (ed, False)
    
    def choose_emf(node_from, node_to):
        """Places a battery only in vertical wires"""
        return 0 if abs(node_from.id - node_to.id) < matrix_dimension else 1
    
    if forced_em == None:
        em = calculate_em(frequency)
    else:
        em = forced_em
        
    print("Em =", em)

    currents_gen = sympy.numbered_symbols('I')
    currents = [next(currents_gen) for _ in range(0, 2 * matrix_dimension ** 2)]
    currents_iter = iter(currents) 
    
    grid.nodes = [Node(number_id) for number_id in range(0, matrix_dimension ** 2)]
    
    i = 0
    for node_from in grid.nodes:
        for node_to_id in grid.neighborhood_nodes2(node_from):
            conductivity, conductor = choose_conductivity(em, ed)
            wire = Wire(grid, conductivity, conductor,
                       choose_emf(node_from, grid.nodes[node_to_id]),
                       node_from,
                       grid.nodes[node_to_id],
                       next(currents_iter))
            grid.wires.append(wire)
            if wire.horizontal: 
                grid.nodes[i].wires_from[0] = wire
                grid.nodes[node_to_id].wires_to[0] = wire
            else: 
                grid.nodes[i].wires_from[1] = wire           
                grid.nodes[node_to_id].wires_to[1] = wire
        i += 1
    
    grid.dimension = matrix_dimension
    return grid, currents
Esempio n. 23
0
def generate_code(inputs,
                  exprs,
                  func_name='generated_function',
                  printer=None,
                  simplify=False):
    """Generate code for a Python function from symbolic expression(s)."""
    if printer is None:
        printer = TupleArrayPrinter()
    if not isinstance(exprs, list) and not isinstance(exprs, tuple):
        exprs = [exprs]
    elif isinstance(exprs, tuple):
        exprs = list(exprs)
    func_expr_args = {}
    ignore_set = set()
    for i, expr in enumerate(exprs):
        if isinstance(expr, FunctionExpression):
            func_expr_args[i] = expr.args
            for arg in expr.args:
                ignore_set.update((
                    arg,
                ) if isinstance(arg, sym.Symbol) else arg.free_symbols)
            exprs[i] = expr.return_val
    flat_exprs, unflatten = flatten_arrays(exprs)
    if simplify:
        flat_exprs = [sym.simplify(expr) for expr in flat_exprs]
    intermediates, flat_outputs = sym.cse(flat_exprs,
                                          symbols=sym.numbered_symbols('_i'),
                                          optimizations='basic',
                                          ignore=ignore_set)
    outputs = unflatten(flat_outputs)
    code = f'def {func_name}({generate_input_list_string(inputs)}):\n    '
    code += '\n    '.join([
        f'{printer.doprint(i[0])} = {printer.doprint(i[1])}'
        for i in intermediates
    ])
    code += '\n    return (\n        '
    code += ',\n        '.join([
        (f'lambda {generate_input_list_string(func_expr_args[i])}: '
         if i in func_expr_args else '') + f'{printer.doprint(output)}'
        for i, output in enumerate(outputs)
    ])
    code += '\n    )'
    return code
Esempio n. 24
0
def try_solving_moving_endpoints_of_spline():
    import sympy as sy
    sy.init_printing()

    a0, a1, a2, a3, k0, k1, j0, j1, new_t = sy.symbols(
        'a0, a1, a2, a3, k0, k1, j0, j1, new_t')

    # Q: How much simpler is it if we only move one end?
    #j0 = k0  # A: Wow, much simpler!  30+ ops instead of 80+
    #j1 = k1  # A: GADS, not much simpler at all, still 80+ operations.

    years = new_t * (j1 - j0) + j0
    old_t = (years - k0) / (k1 - k0)
    d = (((a3 * old_t + a2) * old_t) + a1) * old_t + a0

    #d = sy.factor(d)
    #d = sy.expand(d)
    #d = sy.simplify(d)
    d = sy.expand(d)

    d = sy.collect(d, new_t)
    b0 = d.coeff(new_t, 0)
    b1 = d.coeff(new_t, 1)
    b2 = d.coeff(new_t, 2)
    b3 = d.coeff(new_t, 3)

    commons, outputs = sy.cse(
        [b0, b1, b2, b3],
        sy.numbered_symbols('u'),
        #optimizations='basic',
    )
    n = 0
    for symbol, expr in commons:
        n += sy.count_ops(expr)
        print(symbol, '=', expr)
    print()
    for i, expr in enumerate(outputs):
        n += sy.count_ops(expr)
        print('b{} = {}'.format(i, expr))
    print('Total operations: {}'.format(n))
Esempio n. 25
0
def get_cse(matrix):
    rows = len(matrix)
    cols = len(matrix[0])
    expr_list = []
    coord_list = {}
    for row in range(0, rows):
        for col in range(0, cols):
            scase = sympy.sympify(matrix[row][col])
            coord_list[(row, col)] = len(expr_list)
            expr_list.append(scase)
    cse = sympy.cse(expr_list, sympy.numbered_symbols('tmp'))
    tmp_list = cse[0]
    expr_list = cse[1]
    expr_mat = []
    for row in range(0, rows):
        row_list = []
        for col in range(0, cols):
            i = coord_list[(row, col)]
            e = expr_list[i]
            row_list.append(expr_list[i])
        expr_mat.append(row_list)
    return (tmp_list, expr_mat)
Esempio n. 26
0
    def _generate_sub_expressions(self, int_var='z_'):
        """Finds common subexpressions in the mass matrix and forcing vector
        expression lists and rewrites them."""

        # TODO: make common sub expressions optional

        list_of_lists = self.symbols.values()
        if None in list_of_lists:
            list_of_lists.remove(None)
        all_symbols = list(chain.from_iterable(list_of_lists))
        while symbols(int_var) in all_symbols:
            int_var = random.choice(all_letters) + '_'

        sub_expressions, expressions = \
            cse([entry[0] for entry in self.expressions['mass_matrix'] +
                 self.expressions['forcing_vector']],
                numbered_symbols(int_var))

        self.expressions = \
            {'common_sub': sub_expressions,
             'mass_matrix': expressions[:self.rows * self.cols],
             'forcing_vector': expressions[self.rows * self.cols:]}
Esempio n. 27
0
def get_cse(matrix):
    rows = len(matrix)
    cols = len(matrix[0])
    expr_list = []
    coord_list = {}
    for row in range(0, rows):
        for col in range(0, cols):
            scase = sympy.sympify(matrix[row][col])
            coord_list[(row,col)] = len(expr_list)
            expr_list.append(scase)
    cse = sympy.cse(expr_list, sympy.numbered_symbols('tmp'))
    tmp_list = cse[0]
    expr_list = cse[1]
    expr_mat = []
    for row in range(0, rows):
        row_list = []
        for col in range(0, cols):
            i = coord_list[(row,col)]
            e = expr_list[i]
            row_list.append(expr_list[i])
        expr_mat.append(row_list)
    return (tmp_list, expr_mat)
Esempio n. 28
0
    def _generate_sub_expressions(self, int_var='z_'):
        """Finds common subexpressions in the mass matrix and forcing vector
        expression lists and rewrites them."""

        # TODO: make common sub expressions optional

        list_of_lists = self.symbols.values()
        if None in list_of_lists:
            list_of_lists.remove(None)
        all_symbols = list(chain.from_iterable(list_of_lists))
        while symbols(int_var) in all_symbols:
            int_var = random.choice(all_letters) + '_'

        sub_expressions, expressions = \
            cse([entry[0] for entry in self.expressions['mass_matrix'] +
                 self.expressions['forcing_vector']],
                numbered_symbols(int_var))

        self.expressions = \
            {'common_sub': sub_expressions,
             'mass_matrix': expressions[:self.rows * self.cols],
             'forcing_vector': expressions[self.rows * self.cols:]}
Esempio n. 29
0
    def _generate_cse(self):

        # This makes a big long list of every expression in all of the
        # matrices.
        exprs = []
        for matrix in self.matrices:
            exprs += matrix[:]

        # Compute the common subexpresions.
        gen = sm.numbered_symbols('pydy_')
        self.subexprs, simplified_exprs = sm.cse(exprs, symbols=gen)

        # Turn the expressions back into matrices of the same type.
        simplified_matrices = []
        idx = 0
        for matrix in self.matrices:
            num_rows, num_cols = matrix.shape
            length = num_rows * num_cols
            m = type(matrix)(simplified_exprs[idx:idx + length])
            simplified_matrices.append(m.reshape(num_rows, num_cols))
            idx += length

        self.simplified_matrices = tuple(simplified_matrices)
Esempio n. 30
0
    def _generate_cse(self, prefix='pydy_'):

        # This makes a big long list of every expression in all of the
        # matrices.
        exprs = []
        for matrix in self.matrices:
            exprs += matrix[:]

        # Compute the common subexpresions.
        gen = sm.numbered_symbols(prefix)
        self.subexprs, simplified_exprs = sm.cse(exprs, symbols=gen)

        # Turn the expressions back into matrices of the same type.
        simplified_matrices = []
        idx = 0
        for matrix in self.matrices:
            num_rows, num_cols = matrix.shape
            length = num_rows * num_cols
            m = type(matrix)(simplified_exprs[idx:idx + length])
            simplified_matrices.append(m.reshape(num_rows, num_cols))
            idx += length

        self.simplified_matrices = tuple(simplified_matrices)
Esempio n. 31
0
def clean_numbers(expr, eps=1e-10):
    """
    trys to clean all numbers from numeric noise
    """

    if isinstance(expr, (list, tuple)):
        return [clean_numbers(elt, eps) for elt in expr]

    expr = trunc_small_values(expr)

    maxden = int(1/eps)
    floats = list(atoms(expr, sp.Float))
    rats = []
    dummy_symbs = []
    symb_gen = sp.numbered_symbols('cde', cls = sp.Dummy)
    for f in floats:
        rat = sp_fff(f, maxden)
        rats.append(rat)
        dummy_symbs.append(next(symb_gen))

    res1 = expr.subs(list(zip(floats, dummy_symbs)))
    res2 = res1.subs(list(zip(dummy_symbs, rats)))

    return res2
Esempio n. 32
0
def clean_numbers(expr, eps=1e-10):
    """
    trys to clean all numbers from numeric noise
    """

    if isinstance(expr, (list, tuple)):
        return [clean_numbers(elt, eps) for elt in expr]

    expr = trunc_small_values(expr)

    maxden = int(1/eps)
    floats = list(atoms(expr, sp.Float))
    rats = []
    dummy_symbs = []
    symb_gen = sp.numbered_symbols('cde', cls = sp.Dummy)
    for f in floats:
        rat = sp_fff(f, maxden)
        rats.append(rat)
        dummy_symbs.append(symb_gen.next())

    res1 = expr.subs(zip(floats, dummy_symbs))
    res2 = res1.subs(zip(dummy_symbs, rats))

    return res2
Esempio n. 33
0
def get_alpha_dot():
    """Use sympy to perform most of the math and use the resulting formulae
    to calculate:

            inv(I) (\tau - w x (I w))
    """
    import sympy as S
    ixx, iyy, izz, ixy, ixz, iyz = S.symbols("ixx, iyy, izz, ixy, ixz, iyz")
    tx, ty, tz = S.symbols("tx, ty, tz")
    wx, wy, wz = S.symbols('wx, wy, wz')
    tau = S.Matrix([tx, ty, tz])
    I = S.Matrix([[ixx, ixy, ixz], [ixy, iyy, iyz], [ixz, iyz, izz]])
    w = S.Matrix([wx, wy, wz])
    Iinv = I.inv()
    Iinv.simplify()
    # inv(I) (\tau - w x (Iw))
    res = Iinv * (tau - w.cross(I * w))
    res.simplify()
    # Now do some awesome sympy magic.
    syms, result = S.cse(res, symbols=S.numbered_symbols('tmp'))
    for lhs, rhs in syms:
        print("%s = %s" % (lhs, rhs))
    for i in range(3):
        print("omega_dot[%d] =" % i, result[0][i])
Esempio n. 34
0
def balance_stoichiometry(reactants, products, substances=None,
                          substance_factory=Substance.from_formula,
                          parametric_symbols=None, underdetermined=True, allow_duplicates=False):
    """ Balances stoichiometric coefficients of a reaction

    Parameters
    ----------
    reactants : iterable of reactant keys
    products : iterable of product keys
    substances : OrderedDict or string or None
        Mapping reactant/product keys to instances of :class:`Substance`.
    substance_factory : callback
    parametric_symbols : generator of symbols
        Used to generate symbols for parametric solution for
        under-determined system of equations. Default is numbered "x-symbols" starting
        from 1.
    underdetermined : bool
        Allows to find a non-unique solution (in addition to a constant factor
        across all terms). Set to ``False`` to disallow (raise ValueError) on
        e.g. "C + O2 -> CO + CO2". Set to ``None`` if you want the symbols replaced
        so that the coefficients are the smallest possible positive (non-zero) integers.
    allow_duplicates : bool
        If False: raises an excpetion if keys appear in both ``reactants`` and ``products``.

    Examples
    --------
    >>> ref = {'C2H2': 2, 'O2': 3}, {'CO': 4, 'H2O': 2}
    >>> balance_stoichiometry({'C2H2', 'O2'}, {'CO', 'H2O'}) == ref
    True
    >>> ref2 = {'H2': 1, 'O2': 1}, {'H2O2': 1}
    >>> balance_stoichiometry('H2 O2'.split(), ['H2O2'], 'H2 O2 H2O2') == ref2
    True
    >>> reac, prod = 'CuSCN KIO3 HCl'.split(), 'CuSO4 KCl HCN ICl H2O'.split()
    >>> Reaction(*balance_stoichiometry(reac, prod)).string()
    '4 CuSCN + 7 KIO3 + 14 HCl -> 4 CuSO4 + 7 KCl + 4 HCN + 7 ICl + 5 H2O'
    >>> balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'}, underdetermined=False)
    Traceback (most recent call last):
        ...
    ValueError: The system was under-determined
    >>> r, p = balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'})
    >>> list(set.union(*[v.free_symbols for v in r.values()]))
    [x1]
    >>> b = balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'}, underdetermined=None)
    >>> b == ({'Fe': 3, 'O2': 2}, {'FeO': 1, 'Fe2O3': 1})
    True
    >>> d = balance_stoichiometry({'C', 'CO'}, {'C', 'CO', 'CO2'}, underdetermined=None, allow_duplicates=True)
    >>> d == ({'CO': 2}, {'C': 1, 'CO2': 1})
    True

    Returns
    -------
    balanced reactants : dict
    balanced products : dict

    """
    import sympy
    from sympy import (
        MutableDenseMatrix, gcd, zeros, linsolve, numbered_symbols, Wild, Symbol,
        Integer, Tuple, preorder_traversal as pre
    )

    _intersect = sorted(set.intersection(*map(set, (reactants, products))))
    if _intersect:
        if allow_duplicates:
            if underdetermined is not None:
                raise NotImplementedError("allow_duplicates currently requires underdetermined=None")
            if set(reactants) == set(products):
                raise ValueError("cannot balance: reactants and products identical")

            # For each duplicate, try to drop it completely:
            for dupl in _intersect:
                try:
                    result = balance_stoichiometry(
                        [sp for sp in reactants if sp != dupl],
                        [sp for sp in products if sp != dupl],
                        substances=substances, substance_factory=substance_factory,
                        underdetermined=underdetermined, allow_duplicates=True)
                except Exception:
                    continue
                else:
                    return result
            for perm in product(*[(False, True)]*len(_intersect)):  # brute force (naive)
                r = set(reactants)
                p = set(products)
                for remove_reac, dupl in zip(perm, _intersect):
                    if remove_reac:
                        r.remove(dupl)
                    else:
                        p.remove(dupl)
                try:
                    result = balance_stoichiometry(
                        r, p, substances=substances, substance_factory=substance_factory,
                        parametric_symbols=parametric_symbols, underdetermined=underdetermined,
                        allow_duplicates=False)
                except ValueError:
                    continue
                else:
                    return result
            else:
                raise ValueError("Failed to remove duplicate keys: %s" % _intersect)
        else:
            raise ValueError("Substances on both sides: %s" % str(_intersect))
    if substances is None:
        substances = OrderedDict([(k, substance_factory(k)) for k in chain(reactants, products)])
    if isinstance(substances, str):
        substances = OrderedDict([(k, substance_factory(k)) for k in substances.split()])
    if type(reactants) == set:  # we don't want isinstance since it might be "OrderedSet"
        reactants = sorted(reactants)
    if type(products) == set:
        products = sorted(products)
    subst_keys = list(reactants) + list(products)

    cks = Substance.composition_keys(substances.values())

    if parametric_symbols is None:
        parametric_symbols = numbered_symbols('x', start=1, integer=True, positive=True)

    # ?C2H2 + ?O2 -> ?CO + ?H2O
    # Ax = 0
    #   A:                    x:
    #
    #   C2H2   O2  CO  H2O
    # C -2     0    1   0      x0    =   0
    # H -2     0    0   2      x1        0
    # O  0    -2    1   1      x2        0
    #                          x3

    def _get(ck, sk):
        return substances[sk].composition.get(ck, 0) * (-1 if sk in reactants else 1)

    for ck in cks:  # check that all components are present on reactant & product sides
        for rk in reactants:
            if substances[rk].composition.get(ck, 0) != 0:
                break
        else:
            any_pos = any(substances[pk].composition.get(ck, 0) > 0 for pk in products)
            any_neg = any(substances[pk].composition.get(ck, 0) < 0 for pk in products)
            if any_pos and any_neg:
                pass  # negative and positive parts among products, no worries
            else:
                raise ValueError("Component '%s' not among reactants" % ck)

        for pk in products:
            if substances[pk].composition.get(ck, 0) != 0:
                break
        else:
            any_pos = any(substances[pk].composition.get(ck, 0) > 0 for pk in reactants)
            any_neg = any(substances[pk].composition.get(ck, 0) < 0 for pk in reactants)
            if any_pos and any_neg:
                pass  # negative and positive parts among reactants, no worries
            else:
                raise ValueError("Component '%s' not among products" % ck)

    A = MutableDenseMatrix([[_get(ck, sk) for sk in subst_keys] for ck in cks])
    symbs = list(reversed([next(parametric_symbols) for _ in range(len(subst_keys))]))
    sol, = linsolve((A, zeros(len(cks), 1)), symbs)
    wi = Wild('wi', properties=[lambda k: not k.has(Symbol)])
    cd = reduce(gcd, [1] + [1/m[wi] for m in map(
        lambda n: n.match(symbs[-1]/wi), pre(sol)) if m is not None])
    sol = sol.func(*[arg/cd for arg in sol.args])

    def remove(cont, symb, remaining):
        subsd = dict(zip(remaining/symb, remaining))
        cont = cont.func(*[(arg/symb).expand().subs(subsd) for arg in cont.args])
        if cont.has(symb):
            raise ValueError("Bug, please report an issue at https://github.com/bjodah/chempy")
        return cont

    done = False
    for idx, symb in enumerate(symbs):
        for expr in sol:
            iterable = expr.args if expr.is_Add else [expr]
            for term in iterable:
                if term.is_number:
                    done = True
                    break
            if done:
                break
        if done:
            break
        for expr in sol:
            if (expr/symb).is_number:
                sol = remove(sol, symb, MutableDenseMatrix(symbs[idx+1:]))
                break
    for symb in symbs:
        cd = 1
        for expr in sol:
            iterable = expr.args if expr.is_Add else [expr]
            for term in iterable:
                if term.is_Mul and term.args[0].is_number and term.args[1] == symb:
                    cd = gcd(cd, term.args[0])
        if cd != 1:
            sol = sol.func(*[arg.subs(symb, symb/cd) for arg in sol.args])
    integer_one = 1  # need 'is' check, SyntaxWarning when checking against literal
    if underdetermined is integer_one:
        from ._release import __version__
        if int(__version__.split('.')[1]) > 6:
            warnings.warn(  # deprecated because comparison with ``1`` problematic (True==1)
                ("Pass underdetermined == None instead of ``1`` (depreacted since 0.7.0,"
                 " will_be_missing_in='0.9.0')"), ChemPyDeprecationWarning)
        underdetermined = None
    if underdetermined is None:
        sol = Tuple(*[Integer(x) for x in _solve_balancing_ilp_pulp(A)])

    fact = gcd(sol)
    sol = MutableDenseMatrix([e/fact for e in sol]).reshape(len(sol), 1)
    sol /= reduce(gcd, sol)
    if 0 in sol:
        raise ValueError("Superfluous species given.")
    if underdetermined:
        if any(x == sympy.nan for x in sol):
            raise ValueError("Failed to balance reaction")
    else:
        for x in sol:
            if len(x.free_symbols) != 0:
                raise ValueError("The system was under-determined")
        if not all(residual == 0 for residual in A * sol):
            raise ValueError("Failed to balance reaction")

    def _x(k):
        coeff = sol[subst_keys.index(k)]
        return int(coeff) if underdetermined is None else coeff

    return (
        OrderedDict([(k, _x(k)) for k in reactants]),
        OrderedDict([(k, _x(k)) for k in products])
    )
Esempio n. 35
0
def generate_mass_forcing_cython_code(filename_prefix, mass_matrix,
                                      forcing_vector, constants,
                                      coordinates, speeds, specified=None):
    """Generates a Cython shared object module with a function that
    evaluates the mass_matrix and the forcing vector.

    Parameters
    ----------
    filename_prefix : string
        The desired name of the created module.
    mass_matrix : sympy.matrices.dense.MutableDenseMatrix, shape(n,n)
        The symbolic mass matrix of the system.
    forcing_vector : sympy.matrices.dense.MutableDenseMatrix, shape(n,1)
        The symbolic forcing vector of the system.
    constants : list of sympy.core.symbol.Symbol
        The constants in the equations of motion.
    coordinates : list of sympy.core.function.Function
        The generalized coordinates of the system.
    speeds : list of sympy.core.function.Function
        The generalized speeds of the system.
    specified : list of sympy.core.function.Function, optional, default=None
        The specifed quantities of the system.

    """

    # TODO : Maybe, allow expressions to be passed in for the specified
    # quantities for computation inside the c file. Would need the value of
    # time in the mass_forcing function.

    c_filename = filename_prefix + '_c.c'
    header_filename = filename_prefix + '_c.h'
    pyx_filename = filename_prefix + '.pyx'
    setup_py_filename = filename_prefix + '_setup.py'

    # Comma separated lists of the input variables to the arrays, so that
    # you know which order you should supply them. These are used in the
    # comments in the C and header file.
    # TODO: Add these to the doc string of the imported cythonized function.
    comma_lists = {}
    for list_name, sym_list in zip(['constants', 'coordinates', 'speeds',
                                    'specified'],
                                   [constants, coordinates, speeds,
                                    specified]):
        if sym_list is not None:
            comma_lists[list_name] = ', '.join([str(s).split('(')[0] for s
                                                in sym_list])
        else:
            comma_lists[list_name] = ['None Supplied']

    rows, cols = mass_matrix.shape
    mass_matrix_list = mass_matrix.reshape(rows * cols, 1).tolist()
    forcing_vector_list = forcing_vector.tolist()

    # TODO: make common sub expressions optional
    sub_expressions, expressions = cse([entry[0] for entry in
                                        mass_matrix_list +
                                        forcing_vector_list],
                                       numbered_symbols('z_'))

    expressions = {'common_sub': sub_expressions,
                   'mass_matrix': expressions[:rows * cols],
                   'forcing_vector': expressions[rows * cols:]}

    PyDyCCodePrinter = pydy_c_printer(constants, coordinates, speeds,
                                      specified)

    code_blocks = {}
    for exp_type, expression_list in expressions.items():
        c_lines = []
        for i, exp in enumerate(expression_list):
            if exp_type == 'common_sub':
                code_str = PyDyCCodePrinter().doprint(exp[1])
                c_lines.append('double {} = {};'.format(str(exp[0]),
                                                        code_str))
            else:
                code_str = PyDyCCodePrinter().doprint(exp)
                c_lines.append('{} = {};'.format('{}[{}]'.format(exp_type,
                                                                 i),
                                                 code_str))
        code_blocks[exp_type] = '\n    '.join(c_lines)

    template_values = {
        'header_filename': header_filename,
        'constants_len': len(constants),
        'mass_matrix_len': rows * cols,
        'forcing_vector_len': len(forcing_vector),
        'coordinates_len': len(coordinates),
        'speeds_len': len(speeds),
        'specified_len': len(specified) if specified is not None else 0,
        'constants_list': comma_lists['constants'],
        'coordinates_list': comma_lists['coordinates'],
        'speeds_list': comma_lists['speeds'],
        'specified_list': comma_lists['specified'],
        'sub_expression_block': code_blocks['common_sub'],
        'mass_matrix_block': code_blocks['mass_matrix'],
        'forcing_vector_block': code_blocks['forcing_vector'],
        'prefix': filename_prefix,
        'c_filename': c_filename,
        'pyx_filename': pyx_filename,
    }

    if specified is not None:
        specified_template = {
            'specified_double': " " * 18 + "double specified[{specified_len}], // specified = [{specified_list}]".format(**template_values) + '\n',
            'specified_assert': "\n    assert len(specified) == {specified_len}\n".format(**template_values),
            'def_specified_arg': ",\n                          np.ndarray[np.double_t, ndim=1, mode='c'] specified",
            'cdef_specified_arg': "\n" + ' ' * 22 + "double* specified,",
            'call_specified_arg': "\n" + ' ' * 17 + "<double*> specified.data,"
        }
    else:
        specified_template = {
            'specified_double': "",
            'specified_assert': "",
            'def_specified_arg': "",
            'cdef_specified_arg': "",
            'call_specified_arg': ""
        }

    template_values.update(specified_template)

    files = {c_filename: c_template,
             header_filename: h_template,
             pyx_filename: pyx_template,
             setup_py_filename: setup_template}

    for filename, template in files.items():
        code = template.format(**template_values)
        with open(filename, 'w') as f:
            f.write(code)

    # This prevents output to stdout and waits till it is done.
    p = subprocess.Popen(['python', setup_py_filename, 'build_ext',
                          '--inplace'], stderr=subprocess.STDOUT,
                         stdout=subprocess.PIPE)
    p.wait()
Esempio n. 36
0
def balance_stoichiometry(reactants, products, substances=None,
                          substance_factory=Substance.from_formula,
                          parametric_symbols=None, underdetermined=True, allow_duplicates=False):
    """ Balances stoichiometric coefficients of a reaction

    Parameters
    ----------
    reactants : iterable of reactant keys
    products : iterable of product keys
    substances : OrderedDict or string or None
        Mapping reactant/product keys to instances of :class:`Substance`.
    substance_factory : callback
    parametric_symbols : generator of symbols
        Used to generate symbols for parametric solution for
        under-determined system of equations. Default is numbered "x-symbols" starting
        from 1.
    underdetermined : bool
        Allows to find a non-unique solution (in addition to a constant factor
        across all terms). Set to ``False`` to disallow (raise ValueError) on
        e.g. "C + O2 -> CO + CO2". Set to ``None`` if you want the symbols replaced
        so that the coefficients are the smallest possible positive (non-zero) integers.
    allow_duplicates : bool
        If False: raises an excpetion if keys appear in both ``reactants`` and ``products``.

    Examples
    --------
    >>> ref = {'C2H2': 2, 'O2': 3}, {'CO': 4, 'H2O': 2}
    >>> balance_stoichiometry({'C2H2', 'O2'}, {'CO', 'H2O'}) == ref
    True
    >>> ref2 = {'H2': 1, 'O2': 1}, {'H2O2': 1}
    >>> balance_stoichiometry('H2 O2'.split(), ['H2O2'], 'H2 O2 H2O2') == ref2
    True
    >>> reac, prod = 'CuSCN KIO3 HCl'.split(), 'CuSO4 KCl HCN ICl H2O'.split()
    >>> Reaction(*balance_stoichiometry(reac, prod)).string()
    '4 CuSCN + 7 KIO3 + 14 HCl -> 4 CuSO4 + 7 KCl + 4 HCN + 7 ICl + 5 H2O'
    >>> balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'}, underdetermined=False)
    Traceback (most recent call last):
        ...
    ValueError: The system was under-determined
    >>> r, p = balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'})
    >>> list(set.union(*[v.free_symbols for v in r.values()]))
    [x1]
    >>> b = balance_stoichiometry({'Fe', 'O2'}, {'FeO', 'Fe2O3'}, underdetermined=None)
    >>> b == ({'Fe': 3, 'O2': 2}, {'FeO': 1, 'Fe2O3': 1})
    True
    >>> d = balance_stoichiometry({'C', 'CO'}, {'C', 'CO', 'CO2'}, underdetermined=None, allow_duplicates=True)
    >>> d == ({'CO': 2}, {'C': 1, 'CO2': 1})
    True

    Returns
    -------
    balanced reactants : dict
    balanced products : dict

    """
    import sympy
    from sympy import (
        MutableDenseMatrix, gcd, zeros, linsolve, numbered_symbols, Wild, Symbol,
        Integer, Tuple, preorder_traversal as pre
    )

    _intersect = sorted(set.intersection(*map(set, (reactants, products))))
    if _intersect:
        if allow_duplicates:
            if underdetermined is not None:
                raise NotImplementedError("allow_duplicates currently requires underdetermined=None")
            if set(reactants) == set(products):
                raise ValueError("cannot balance: reactants and products identical")

            # For each duplicate, try to drop it completely:
            for dupl in _intersect:
                try:
                    result = balance_stoichiometry(
                        [sp for sp in reactants if sp != dupl],
                        [sp for sp in products if sp != dupl],
                        substances=substances, substance_factory=substance_factory,
                        underdetermined=underdetermined, allow_duplicates=True)
                except Exception:
                    continue
                else:
                    return result
            for perm in product(*[(False, True)]*len(_intersect)):  # brute force (naive)
                r = set(reactants)
                p = set(products)
                for remove_reac, dupl in zip(perm, _intersect):
                    if remove_reac:
                        r.remove(dupl)
                    else:
                        p.remove(dupl)
                try:
                    result = balance_stoichiometry(
                        r, p, substances=substances, substance_factory=substance_factory,
                        parametric_symbols=parametric_symbols, underdetermined=underdetermined,
                        allow_duplicates=False)
                except Exception:
                    continue
                else:
                    return result
            else:
                raise ValueError("Failed to remove duplicate keys: %s" % _intersect)
        else:
            raise ValueError("Substances on both sides: %s" % str(_intersect))
    if substances is None:
        substances = OrderedDict([(k, substance_factory(k)) for k in chain(reactants, products)])
    if isinstance(substances, str):
        substances = OrderedDict([(k, substance_factory(k)) for k in substances.split()])
    if type(reactants) == set:  # we don't want isinstance since it might be "OrderedSet"
        reactants = sorted(reactants)
    if type(products) == set:
        products = sorted(products)
    subst_keys = list(reactants) + list(products)

    cks = Substance.composition_keys(substances.values())

    if parametric_symbols is None:
        parametric_symbols = numbered_symbols('x', start=1, integer=True, positive=True)

    # ?C2H2 + ?O2 -> ?CO + ?H2O
    # Ax = 0
    #   A:                    x:
    #
    #   C2H2   O2  CO  H2O
    # C -2     0    1   0      x0    =   0
    # H -2     0    0   2      x1        0
    # O  0    -2    1   1      x2        0
    #                          x3

    def _get(ck, sk):
        return substances[sk].composition.get(ck, 0) * (-1 if sk in reactants else 1)

    for ck in cks:  # check that all components are present on reactant & product sides
        for rk in reactants:
            if substances[rk].composition.get(ck, 0) != 0:
                break
        else:
            raise ValueError("Component '%s' not among reactants" % ck)
        for pk in products:
            if substances[pk].composition.get(ck, 0) != 0:
                break
        else:
            raise ValueError("Component '%s' not among products" % ck)

    A = MutableDenseMatrix([[_get(ck, sk) for sk in subst_keys] for ck in cks])
    symbs = list(reversed([next(parametric_symbols) for _ in range(len(subst_keys))]))
    sol, = linsolve((A, zeros(len(cks), 1)), symbs)
    wi = Wild('wi', properties=[lambda k: not k.has(Symbol)])
    cd = reduce(gcd, [1] + [1/m[wi] for m in map(
        lambda n: n.match(symbs[-1]/wi), pre(sol)) if m is not None])
    sol = sol.func(*[arg/cd for arg in sol.args])

    def remove(cont, symb, remaining):
        subsd = dict(zip(remaining/symb, remaining))
        cont = cont.func(*[(arg/symb).expand().subs(subsd) for arg in cont.args])
        if cont.has(symb):
            raise ValueError("Bug, please report an issue at https://github.com/bjodah/chempy")
        return cont

    done = False
    for idx, symb in enumerate(symbs):
        for expr in sol:
            iterable = expr.args if expr.is_Add else [expr]
            for term in iterable:
                if term.is_number:
                    done = True
                    break
            if done:
                break
        if done:
            break
        for expr in sol:
            if (expr/symb).is_number:
                sol = remove(sol, symb, MutableDenseMatrix(symbs[idx+1:]))
                break
    for symb in symbs:
        cd = 1
        for expr in sol:
            iterable = expr.args if expr.is_Add else [expr]
            for term in iterable:
                if term.is_Mul and term.args[0].is_number and term.args[1] == symb:
                    cd = gcd(cd, term.args[0])
        if cd != 1:
            sol = sol.func(*[arg.subs(symb, symb/cd) for arg in sol.args])
    if underdetermined is 1:
        from ._release import __version__
        if int(__version__.split('.')[1]) > 6:
            warnings.warn(  # deprecated because comparison with ``1`` problematic (True==1)
                ("Pass underdetermined == None instead of ``1`` (depreacted since 0.7.0,"
                 " will_be_missing_in='0.9.0')"), ChemPyDeprecationWarning)
        underdetermined = None
    if underdetermined is None:
        sol = Tuple(*[Integer(x) for x in _solve_balancing_ilp_pulp(A)])

    fact = gcd(sol)
    sol = MutableDenseMatrix([e/fact for e in sol]).reshape(len(sol), 1)
    sol /= reduce(gcd, sol)
    if 0 in sol:
        raise ValueError("Superfluous species given.")
    if underdetermined:
        if any(x == sympy.nan for x in sol):
            raise ValueError("Failed to balance reaction")
    else:
        for x in sol:
            if len(x.free_symbols) != 0:
                raise ValueError("The system was under-determined")
        if not all(residual == 0 for residual in A * sol):
            raise ValueError("Failed to balance reaction")

    def _x(k):
        coeff = sol[subst_keys.index(k)]
        return int(coeff) if underdetermined is None else coeff

    return (
        OrderedDict([(k, _x(k)) for k in reactants]),
        OrderedDict([(k, _x(k)) for k in products])
    )
Esempio n. 37
0
# Form expressions for derivatives of the g's w.r.t. lean and pitch
dh = [0] * 8
for i in range(4):
    for j in range(2):
        dh[2 * i + j] = h[i].diff(q[j + 1])

# Subsitution dictionary to replace dynamic symbols with regular symbols
symbol_dict = {q[1]: Symbol('q1'), q[2]: Symbol('q2'), qd[0]: Symbol('qd0')}

for i in range(4):
    h[i] = h[i].subs(symbol_dict)
for i in range(8):
    dh[i] = dh[i].subs(symbol_dict)

# CSE on g's
z, h_red = cse(h, numbered_symbols('z'))

# Form output code for g's
output_code = "  // Intermediate variables for h's\n"
for zi_lhs, zi_rhs in z:
    output_code += "  {0} = {1};\n".format(zi_lhs, ccode(zi_rhs))

output_code += "\n  // h's\n"
for i in range(4):
    output_code += "  h[{0}] = {1};\n".format(i, ccode(h_red[i]))

# CSE on dh's
z, dh_red = cse(dh, numbered_symbols('z'))

# Form output code for dg's
output_code += "\n  // Intermediate variables for dh's\n"
Esempio n. 38
0
             qd[0]: Symbol('qd0'), qd[1]: Symbol('qd1'), qd[2]: Symbol('qd2'),
             u[0]: Symbol('u0'), u[1]: Symbol('u1'), u[2]: Symbol('u2'),
             ud[0]: Symbol('ud0'), ud[1]: Symbol('ud1'), ud[2]: Symbol('ud2')}

for i in range(len(exp_ode)):
  exp_ode[i] = exp_ode[i].subs(subs_dict)

for i in range(len(exp_output)):
  exp_output[i] = exp_output[i].subs(subs_dict)

for i in range(len(exp_jac)):
  exp_jac[i] = exp_jac[i].subs(subs_dict)

# CSE on all quantities needed for numerical integration of ordinary
# differential equations:  qd_rhs (5), M_dyn (9), f_dyn (3)
z, exp_ode_red = cse(exp_ode, numbered_symbols('z'))

output_code = "  // Intermediate variables for ODE function\n"
for zi_lhs, zi_rhs in z:
  output_code += "  {0} = {1};\n".format(zi_lhs, ccode(zi_rhs))

output_code += "\n  // Kinematic differential equations\n"
for i in range(5):
  output_code += "  dxdt[{0}] = {1};\n".format(i, ccode(exp_ode_red[i]))

output_code += "\n  // Mass matrix\n"
for i in range(3):
  for j in range(3):
    output_code += "  M_dyn({0}, {1}) = {2};\n".format(i, j,
                                ccode(exp_ode_red[5 + 3*i + j]))
Esempio n. 39
0
def sparse_solve_for_commuting_term(cvector, psi_lower, order, orders,
                                    matrixrows, subspace, norm = False,
                                    fvarname = 'A', iofvars = None, subs_rules = None, split_orders = None):
    fvar_gen = sympy.numbered_symbols('fvar')
    fvars = [next(fvar_gen) for i in range(len(subspace))]
    psi_order = [Ncproduct(-fvars[subspace[key]], list(key))
                 for i,key in enumerate(subspace)]
    if norm:
        psi_total = psi_lower + psi_order
        new_orders = orders.copy()
        new_orders.update(dict(zip(fvars, [order]*len(fvars))))
        sparse_normalise(psi_total, order, new_orders, fvars, cvector, matrixrows, split_orders, start_ind = len(fvars))
    sub_sub_spaces = find_sub_subspaces(matrixrows)
    print(sub_sub_spaces)
    solutions = {}
    if subs_rules is None:
        subs_rules = {}
    #deal with empty rows
    # for i, row in matrixrows.items():
    #     if not row:
    #         if sympy.simplify(cvector[i]) != 0:
    #             poss = False
    #             for iofvar in iofvars:
    #                 if iofvar in cvector[i].atoms(sympy.Symbol):
    #                     sub_sub_spaces.append([i])
    #                     print('Warning, new sspace: ' + str(i))
    #                     poss = True
    #             if not poss:
    #                 print(matrixrows)
    #                 print(cvector)
    #                 raise ValueError('Error term to cancel in null')
    length_ss = len(sub_sub_spaces)
    fvargen = sympy.numbered_symbols(fvarname)
    tempgen = sympy.numbered_symbols('temp')
    tempvars = []
    newfvars = []
    for i, ss_space in enumerate(sub_sub_spaces):
        #if i == 4:
        #ipdb.set_trace()
        solutions.update(solve_for_sub_subspace(matrixrows, ss_space,
                                                fvars, cvector, iofvars,
                                                subs_rules, fvargen, newfvars, tempgen, tempvars))
        print_progress(i, length_ss)
    solvector = []
    for fvar in fvars:
        try:
            solvector.append(solutions[fvar])
        except KeyError:
            newfvars.append(next(fvargen))
            solvector.append(newfvars[-1])
    for tempvar in tempvars:
        if tempvar not in subs_rules:
            newfvars.append(next(fvargen))
            subs_rules[tempvar] = newfvars[-1]
    if newfvars:
        if iofvars is not None:
            iofvars[:] = newfvars
    if not subs_rules:
        return simplify_group([Ncproduct(-solvector[i], list(key))
                           for i,key in enumerate(subspace)])
    else:
        ret = simplify_group([Ncproduct(-solvector[i].xreplace(subs_rules), list(key))
                               for i,key in enumerate(subspace)])
        for tempvar in tempvars:
            subs_rules.pop(tempvar, None)
        return ret
Esempio n. 40
0
# Form expressions for derivatives of the g's w.r.t. lean and pitch
dh = [0]*8
for i in range(4):
  for j in range(2):
    dh[2*i + j] = h[i].diff(q[j+1])

# Subsitution dictionary to replace dynamic symbols with regular symbols
symbol_dict = {q[1]: Symbol('q1'), q[2]: Symbol('q2'), qd[0]: Symbol('qd0')}

for i in range(4):
  h[i] = h[i].subs(symbol_dict)
for i in range(8):
  dh[i] = dh[i].subs(symbol_dict)

# CSE on g's
z, h_red = cse(h, numbered_symbols('z'))

# Form output code for g's
output_code = "  // Intermediate variables for h's\n"
for zi_lhs, zi_rhs in z:
  output_code += "  {0} = {1};\n".format(zi_lhs, ccode(zi_rhs))

output_code += "\n  // h's\n"
for i in range(4):
  output_code += "  h[{0}] = {1};\n".format(i, ccode(h_red[i]))

# CSE on dh's
z, dh_red = cse(dh, numbered_symbols('z'))

# Form output code for dg's
output_code += "\n  // Intermediate variables for dh's\n"
Esempio n. 41
0
def cse_lambdify(args, expr, **kwargs):
    '''
    Wrapper for sympy.lambdify which makes use of common subexpressions.
    '''

    # Note:
    # This was expected to speed up the evaluation of the created functions.
    # However performance gain is only at ca. 5%

    # check input expression
    if type(expr) == str:
        raise TypeError('Not implemented for string input expression!')

    # check given expression
    try:
        check_expression(expr)
    except TypeError as err:
        raise NotImplementedError("Only sympy expressions are allowed, yet")

    # get sequence of symbols from input arguments
    if type(args) == str:
        args = sp.symbols(args, seq=True)
    elif hasattr(args, '__iter__'):
        # this may kill assumptions
        args = [sp.Symbol(str(a)) for a in args]

    if not hasattr(args, '__iter__'):
        args = (args, )

    # get the common subexpressions
    cse_pairs, red_exprs = sp.cse(expr, symbols=sp.numbered_symbols('r'))
    if len(red_exprs) == 1:
        red_exprs = red_exprs[0]

    # check if sympy found any common subexpressions
    if not cse_pairs:
        # if not, use standard lambdify
        return sp.lambdify(args, expr, **kwargs)

    # now we are looking for those arguments that are part of the reduced expression(s)
    shortcuts = zip(*cse_pairs)[0]
    atoms = sp.Set(red_exprs).atoms()
    cse_args = [arg for arg in tuple(args) + tuple(shortcuts) if arg in atoms]

    # next, we create a function that evaluates the reduced expression
    cse_expr = red_exprs

    # if dummify is set to False then sympy.lambdify still returns a numpy.matrix
    # regardless of the possibly passed module dictionary {'ImmutableMatrix' : numpy.array}
    if kwargs.get('dummify') == False:
        kwargs['dummify'] = True

    reduced_exprs_fnc = sp.lambdify(args=cse_args, expr=cse_expr, **kwargs)

    # get the function that evaluates the replacement pairs
    modules = kwargs.get('modules')

    if modules is None:
        modules = ['math', 'numpy', 'sympy']

    namespaces = []
    if isinstance(modules, (dict, str)) or not hasattr(modules, '__iter__'):
        namespaces.append(modules)
    else:
        namespaces += list(modules)

    nspace = {}
    for m in namespaces[::-1]:
        nspace.update(_get_namespace(m))

    eval_pairs_fnc = make_cse_eval_function(input_args=args,
                                            replacement_pairs=cse_pairs,
                                            ret_filter=cse_args,
                                            namespace=nspace)

    # now we can wrap things together
    def cse_fnc(*args):
        cse_args_evaluated = eval_pairs_fnc(args)
        return reduced_exprs_fnc(*cse_args_evaluated)

    return cse_fnc
Esempio n. 42
0
             qd[0]: Symbol('qd0'), qd[1]: Symbol('qd1'), qd[2]: Symbol('qd2'),
             u[0]: Symbol('u0'), u[1]: Symbol('u1'), u[2]: Symbol('u2'),
             ud[0]: Symbol('ud0'), ud[1]: Symbol('ud1'), ud[2]: Symbol('ud2')}

for i in range(len(exp_ode)):
  exp_ode[i] = exp_ode[i].subs(subs_dict)

for i in range(len(exp_output)):
  exp_output[i] = exp_output[i].subs(subs_dict)

for i in range(len(exp_jac)):
  exp_jac[i] = exp_jac[i].subs(subs_dict)

# CSE on all quantities needed for numerical integration of ordinary
# differential equations:  qd_rhs (5), M_dyn (9), f_dyn (3)
z, exp_ode_red = cse(exp_ode, numbered_symbols('z'))

output_code = "  // Intermediate variables for ODE function\n"
for zi_lhs, zi_rhs in z:
  output_code += "  {0} = {1};\n".format(zi_lhs, ccode(zi_rhs))

output_code += "\n  // Kinematic differential equations\n"
for i in range(5):
  output_code += "  dxdt[{0}] = {1};\n".format(i, ccode(exp_ode_red[i]))

output_code += "\n  // Mass matrix\n"
for i in range(3):
  for j in range(3):
    output_code += "  M_dyn({0}, {1}) = {2};\n".format(i, j,
                                ccode(exp_ode_red[5 + 3*i + j]))
Esempio n. 43
0
    def variables(self):
        dummy_groups = (
            DummyGroup('depvdummies', self._fo_odesys.na_depv),
            DummyGroup('paramdummies', self._fo_odesys.param_and_sol_symbs),
        )

        arrayify_groups = (
            ArrayifyGroup('depvdummies', self.depv_tok, self.depv_offset),
            ArrayifyGroup('paramdummies', self.param_tok, self.param_offset)
        )

        code_func_cse, code_func_exprs = self.get_cse_code(
            self._fo_odesys.na_f.values(), 'csefunc', dummy_groups, arrayify_groups)

        y0 = ', '.join(['1.0'] * self.NY)
        y0_names = ', '.join(map(str, self._fo_odesys.na_depv))
        params = ', '.join(['1.0'] * len(self.prog_param_symbs))
        param_names = ', '.join(map(str, self.prog_param_symbs))

        indepv = self._fo_odesys.indepv

        # TODO: this is inefficient, implement linear scaling algo in
        # FirstOrderODESystem
        sparse_jac = OrderedDict(reduce(add, [
            [((i, j), expr) for j, expr in enumerate(row) if expr != 0]\
            for i, row in enumerate(self._fo_odesys.na_jac.tolist())
            ]))

        dfdt = self._fo_odesys.na_dfdt.values()

        code_jac_cse, code_jac_exprs = self.get_cse_code(
            sparse_jac.values() + dfdt, 'csejac', dummy_groups, arrayify_groups)

        code_pure_dfdt_cse, code_pure_dfdt_exprs = self.get_cse_code(
            dfdt, 'csedfdt', dummy_groups, arrayify_groups)

        code_dfdt_exprs = code_jac_exprs[len(sparse_jac):]
        code_jac_exprs = zip(sparse_jac.keys(),
                             code_jac_exprs[:len(sparse_jac)])


        # Populate ia, ja (sparse index specifiers using fortran indexing)
        # see documentation of LSODES in ODEPACK for definition
        # (Yale sparse matrix)
        # ja contains row indices of nonzero elements
        # ia contains index in ja where row i starts
        ia, ja = [1], []
        k = 1 # <--- index starts at 1 in fortran, not at 0 as in C/Python
        code_yale_jac_exprs = []
        code_yale_jac_cse = []
        for ci in range(self.NY):
            col_exprs = []
            cur_ja = []
            for ri in [sri for sri, sci in sparse_jac.keys() if sci == ci]:
                cur_ja.append(ri+1) # Fortran index
                k += 1
                col_exprs.append(sparse_jac[(ri,ci)])

            # Store indices in ja
            ja.extend(cur_ja)

            # Store indicies in ia
            ia.append(k)

            # Extract common subexpressions for this column
            cse_defs, cse_exprs = sympy.cse(
                col_exprs, symbols = sympy.numbered_symbols(
                    'csejaccol{}'.format(ci)))

            # Format code: expressions in cse terms
            code_exprs = zip(cur_ja, [
                self.as_arrayified_code(x, dummy_groups, arrayify_groups) for x \
                in cse_exprs
            ])
            code_yale_jac_exprs.append(code_exprs)

            # Format code: CSE definitions
            code_cse_defs=[]
            for var_name, var_expr in cse_defs:
                code_var_expr = self.as_arrayified_code(
                    var_expr, dummy_groups, arrayify_groups)
                code_cse_defs.append((var_name, code_var_expr))
            code_yale_jac_cse.append(code_cse_defs)
        ia = ia [:-1]

        return {'NY': self.NY,
                'NNZ': len(sparse_jac),
                'IA': ia,
                'JA': ja,
                'NPARAM': len(self.prog_param_symbs),
                'y0_comma_sep_str': y0,
                'y0_names': y0_names,
                'param_vals_comma_sep_str': params,
                'param_names': param_names,
                'cse_func': code_func_cse, 'f': code_func_exprs,
                'cse_jac': code_jac_cse, 'jac': code_jac_exprs,
                'yale_jac_exprs': code_yale_jac_exprs,
                'yale_jac_cse': code_yale_jac_cse,
                'dfdt': code_dfdt_exprs,
                'pure_dfdt_exprs': code_pure_dfdt_exprs,
                'pure_dfdt_cse': code_pure_dfdt_cse,
        }
Esempio n. 44
0
# substitute the algabrain equations in the ode system to generate the complete ode_subs
a1.gen_ode_subs()


# finding all the parameters
a1.eq_all_params(Zone = 'algebraic')
a1.eq_all_params(Zone = 'ode')
a1.eq_all_params(Zone = 'ode_subs')

# eample how to use the method replace_singe_var()
# a1.eq['algebraic']['delta_L1'] = a1.replace_singe_var(a1.eq['algebraic']['delta_L1'], 'x_1', 'x_11')


x_vec = a1.eq['ode_subs'].keys()
# fixme: the code below is not working as 'm_1' and 'm_2' are strings, but they should be sympy objects
# todo: change eq_all_params() to store and the sympy objects
par_vec = [a1.all_params_ode_subs['m1'], a1.all_params_ode_subs['m2']]


sens_sys_list = a1.sys_dict_to_list(a1.eq['ode_subs'])

sens_sys = a1.sens_ext_sys(sens_sys_list, x_vec, par_vec)
print sens_sys


sens_sys_list = a1.sys_dict_to_list(sens_sys)
from sympy import cse, numbered_symbols
a4=cse(sens_sys_list, symbols=numbered_symbols(prefix='t', start=0))

a1.print_Sys_optim(a4, func_name = 'r1')
Esempio n. 45
0
def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False):
    """Returns a function that evaluates a matrix of expressions in a tight
    loop.

    Parameters
    ----------
    args : iterable of sympy.Symbol
        A list of all symbols in expr in the desired order for the output
        function.
    expr : sympy.Matrix
        A matrix of expressions.
    const : tuple, optional
        This should include any of the symbols in args that should be
        constant with respect to the loop.
    tmp_dir : string, optional
        The path to a directory in which to store the generated files. If
        None then the files will be not be retained after the function is
        compiled.
    parallel : boolean, optional
        If True and openmp is installed, the generated code will be
        parallelized across threads. This is only useful when expr are
        extremely large.

    """

    # TODO : This is my first ever global variable in Python. It'd probably
    # be better if this was a class attribute of a Ufuncifier class. And I'm
    # not sure if this current version counts sequentially.
    global module_counter

    matrix_size = expr.shape[0] * expr.shape[1]

    file_prefix_base = 'ufuncify_matrix'
    file_prefix = '{}_{}'.format(file_prefix_base, module_counter)

    if tmp_dir is None:
        codedir = tempfile.mkdtemp(".ufuncify_compile")
    else:
        codedir = os.path.abspath(tmp_dir)

    if not os.path.exists(codedir):
        os.makedirs(codedir)

    taken = False

    while not taken:
        try:
            open(os.path.join(codedir, file_prefix + '.pyx'), 'r')
        except IOError:
            taken = True
        else:
            file_prefix = '{}_{}'.format(file_prefix_base, module_counter)
            module_counter += 1

    d = {
        'routine_name': 'eval_matrix',
        'file_prefix': file_prefix,
        'matrix_output_size': matrix_size,
        'num_rows': expr.shape[0],
        'num_cols': expr.shape[1]
    }

    if parallel:
        if openmp_installed():
            openmp = True
        else:
            openmp = False
            msg = ('openmp is not installed or not working properly, request '
                   'for parallel execution ignored.')
            warnings.warn(msg)

    if parallel and openmp:
        d['loop_sig'] = "prange(n, nogil=True)"
        d['head_gil'] = " nogil"
        d['compile_args'] = "'-fopenmp'"
        d['link_args'] = "'-fopenmp'"
    else:
        d['loop_sig'] = "range(n)"
        d['head_gil'] = ""
        d['compile_args'] = ""
        d['link_args'] = ""

    matrix_sym = sm.MatrixSymbol('matrix', expr.shape[0], expr.shape[1])

    sub_exprs, simple_mat = sm.cse(expr, sm.numbered_symbols('z_'))

    sub_expr_code = '\n'.join([
        'double ' + sm.ccode(sub_expr[1], sub_expr[0])
        for sub_expr in sub_exprs
    ])

    matrix_code = sm.ccode(simple_mat[0], matrix_sym)

    d['eval_code'] = '    ' + '\n    '.join(
        (sub_expr_code + '\n' + matrix_code).split('\n'))

    c_indent = len('void {routine_name}('.format(**d))
    c_arg_spacer = ',\n' + ' ' * c_indent

    input_args = ['double {}'.format(sm.ccode(a)) for a in args]
    d['input_args'] = c_arg_spacer.join(input_args)

    cython_input_args = []
    indexed_input_args = []
    for a in args:
        if const is not None and a in const:
            typ = 'double'
            idexy = '{}'
        else:
            typ = 'np.ndarray[np.double_t, ndim=1]'
            idexy = '{}[i]'

        cython_input_args.append('{} {}'.format(typ, sm.ccode(a)))
        indexed_input_args.append(idexy.format(sm.ccode(a)))

    cython_indent = len('def {routine_name}_loop('.format(**d))
    cython_arg_spacer = ',\n' + ' ' * cython_indent

    d['numpy_typed_input_args'] = cython_arg_spacer.join(cython_input_args)

    d['indexed_input_args'] = ',\n'.join(indexed_input_args)

    files = {}
    files[d['file_prefix'] + '_c.c'] = _c_template.format(**d)
    files[d['file_prefix'] + '_h.h'] = _h_template.format(**d)
    files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d)
    files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d)

    workingdir = os.getcwd()
    os.chdir(codedir)

    try:
        sys.path.append(codedir)
        for filename, code in files.items():
            with open(filename, 'w') as f:
                f.write(code)
        cmd = [
            sys.executable, d['file_prefix'] + '_setup.py', 'build_ext',
            '--inplace'
        ]
        subprocess.call(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
        cython_module = importlib.import_module(d['file_prefix'])
    finally:
        module_counter += 1
        sys.path.remove(codedir)
        os.chdir(workingdir)
        if tmp_dir is None:
            shutil.rmtree(codedir)

    return getattr(cython_module, d['routine_name'] + '_loop')
Esempio n. 46
0
def ufuncify_matrix(args, expr, const=None, tmp_dir=None):
    """Returns a function that evaluates a matrix of expressions in a tight
    loop.

    Parameters
    ----------
    args : iterable of sympy.Symbol
        A list of all symbols in expr in the desired order for the output
        function.
    expr : sympy.Matrix
        A matrix of expressions.
    const : tuple, optional
        This should include any of the symbols in args that should be
        constant with respect to the loop.
    tmp_dir : string, optional
        The path to a directory in which to store the generated files. If
        None then the files will be not be retained after the function is
        compiled.

    """

    # TODO : This is my first ever global variable in Python. It'd probably
    # be better if this was a class attribute of a Ufuncifier class. And I'm
    # not sure if this current version counts sequentially.
    global module_counter

    matrix_size = expr.shape[0] * expr.shape[1]

    file_prefix_base = 'ufuncify_matrix'
    file_prefix = '{}_{}'.format(file_prefix_base, module_counter)

    if tmp_dir is None:
        codedir = tempfile.mkdtemp(".ufuncify_compile")
    else:
        codedir = os.path.abspath(tmp_dir)

    if not os.path.exists(codedir):
        os.makedirs(codedir)

    taken = False

    while not taken:
        try:
            open(os.path.join(codedir, file_prefix + '.pyx'), 'r')
        except IOError:
            taken = True
        else:
            file_prefix = '{}_{}'.format(file_prefix_base, module_counter)
            module_counter += 1

    d = {'routine_name': 'eval_matrix',
         'file_prefix': file_prefix,
         'matrix_output_size': matrix_size,
         'num_rows': expr.shape[0],
         'num_cols': expr.shape[1]}

    matrix_sym = sy.MatrixSymbol('matrix', expr.shape[0], expr.shape[1])

    sub_exprs, simple_mat = sy.cse(expr, sy.numbered_symbols('z_'))

    sub_expr_code = '\n'.join(['double ' + sy.ccode(sub_expr[1], sub_expr[0])
                               for sub_expr in sub_exprs])

    matrix_code = sy.ccode(simple_mat[0], matrix_sym)

    d['eval_code'] = '    ' + '\n    '.join((sub_expr_code + '\n' + matrix_code).split('\n'))

    c_indent = len('void {routine_name}('.format(**d))
    c_arg_spacer = ',\n' + ' ' * c_indent

    input_args = ['double {}'.format(sy.ccode(a)) for a in args]
    d['input_args'] = c_arg_spacer.join(input_args)

    cython_input_args = []
    indexed_input_args = []
    for a in args:
        if const is not None and a in const:
            typ = 'double'
            idexy = '{}'
        else:
            typ = 'np.ndarray[np.double_t, ndim=1]'
            idexy = '{}[i]'

        cython_input_args.append('{} {}'.format(typ, sy.ccode(a)))
        indexed_input_args.append(idexy.format(sy.ccode(a)))

    cython_indent = len('def {routine_name}_loop('.format(**d))
    cython_arg_spacer = ',\n' + ' ' * cython_indent

    d['numpy_typed_input_args'] = cython_arg_spacer.join(cython_input_args)

    d['indexed_input_args'] = ',\n'.join(indexed_input_args)

    files = {}
    files[d['file_prefix'] + '_c.c'] = _c_template.format(**d)
    files[d['file_prefix'] + '_h.h'] = _h_template.format(**d)
    files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d)
    files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d)

    workingdir = os.getcwd()
    os.chdir(codedir)

    try:
        sys.path.append(codedir)
        for filename, code in files.items():
            with open(filename, 'w') as f:
                f.write(code)
        cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext',
               '--inplace']
        subprocess.call(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
        cython_module = importlib.import_module(d['file_prefix'])
    finally:
        module_counter += 1
        sys.path.remove(codedir)
        os.chdir(workingdir)
        if tmp_dir is None:
            shutil.rmtree(codedir)

    return getattr(cython_module, d['routine_name'] + '_loop')
Esempio n. 47
0
def outputC(sympyexpr, output_varname_str, filename = "stdout", params = "", prestring = "", poststring = ""):
    outCparams = parse_outCparams_string(params)
    preindent = outCparams.preindent
    TYPE = par.parval_from_str("PRECISION")

    if outCparams.enable_TYPE == "False":
        TYPE = ""

    # Step 0: Initialize
    #  commentblock: comment block containing the input SymPy string,
    #                set only if outCverbose==True
    #  outstring:    the output C code string
    commentblock = ""
    outstring = ""

    # Step 1: If enable_SIMD==True, then check if TYPE=="double". If not, error out.
    #         Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd
    #         within the C code. For example for AVX-256, the C code should have
    #         #define REAL_SIMD_ARRAY __m256d
    if outCparams.enable_SIMD == "True":
        if TYPE not in ('double', ''):
            print("SIMD output currently only supports double precision or typeless. Sorry!")
            sys.exit(1)
        if TYPE == "double":
            TYPE = "REAL_SIMD_ARRAY"

    # Step 2a: Apply sanity checks when either sympyexpr or
    #          output_varname_str is a list.
    if isinstance(output_varname_str, list) and not isinstance(sympyexpr, list):
        print("Error: Provided a list of output variable names, but only one SymPy expression.")
        sys.exit(1)
    if isinstance(sympyexpr, list):
        if not isinstance(output_varname_str, list):
            print("Error: Provided a list of SymPy expressions, but no corresponding list of output variable names")
            sys.exit(1)
        elif len(output_varname_str) != len(sympyexpr):
            print("Error: Length of SymPy expressions list ("+str(len(sympyexpr))+
                  ") != Length of corresponding output variable name list ("+str(len(output_varname_str))+")")
            sys.exit(1)
    # Step 2b: If sympyexpr and output_varname_str are not lists,
    #          convert them to lists of one element each, to
    #          simplify proceeding code.
    if not isinstance(output_varname_str, list) and not isinstance(sympyexpr, list):
        output_varname_strtmp = [output_varname_str]
        output_varname_str = output_varname_strtmp
        sympyexprtmp = [sympyexpr]
        sympyexpr = sympyexprtmp
    sympyexpr = sympyexpr[:]  # pass-by-value (copy list)


    # Step 3: If outCparams.verbose = True, then output the original SymPy
    #         expression(s) in code comments prior to actual C code
    if outCparams.outCverbose == "True":
        commentblock += preindent+"/*\n"+preindent+" *  Original SymPy expression"
        if len(output_varname_str)>1:
            commentblock += "s"
        commentblock += ":\n"
        for i, varname in enumerate(output_varname_str):
            if i == 0:
                if len(output_varname_str) != 1:
                    commentblock += preindent+" *  \"["
                else:
                    commentblock += preindent+" *  \""
            else:
                commentblock += preindent+" *    "
            commentblock += varname + " = " + str(sympyexpr[i])
            if i == len(output_varname_str)-1:
                if len(output_varname_str) != 1:
                    commentblock += "]\"\n"
                else:
                    commentblock += "\"\n"
            else:
                commentblock += ",\n"
        commentblock += preindent+" */\n"

    # Step 4: Add proper indentation of C code:
    if outCparams.includebraces == "True":
        indent = outCparams.preindent+"  "
    else:
        indent = outCparams.preindent+""

    # Step 5: Should the output variable, e.g., outvar, be declared?
    #         If so, start output line with e.g., "double outvar "
    outtypestring = ""
    if outCparams.declareoutputvars == "True":
        outtypestring = indent+TYPE + " "
    else:
        outtypestring = indent

    # Step 6a: If common subexpression elimination (CSE) disabled, then
    #         just output the SymPy string in the most boring way,
    #         nearly consistent with SymPy's ccode() function,
    #         though with support for float & long double types
    #         as well.
    SIMD_RATIONAL_decls = RATIONAL_decls = ""

    if outCparams.CSE_enable == "False":
        # If CSE is disabled:
        for i in range(len(sympyexpr)):
            outstring += outtypestring + ccode_postproc(sp.ccode(sympyexpr[i], output_varname_str[i],
                                                                 user_functions=custom_functions_for_SymPy_ccode))+"\n"
    # Step 6b: If CSE enabled, then perform CSE using SymPy and then
    #          resulting C code.
    else:
        # If CSE is enabled:
        SIMD_const_varnms = []
        SIMD_const_values = []

        varprefix = '' if outCparams.CSE_varprefix == 'tmp' else outCparams.CSE_varprefix
        if outCparams.CSE_preprocess == "True" or outCparams.enable_SIMD == "True":
            # If CSE_preprocess == True, then perform partial factorization
            # If enable_SIMD == True, then declare _NegativeOne_ in preprocessing
            factor_negative = eval(outCparams.enable_SIMD) and eval(outCparams.SIMD_find_more_subs)
            sympyexpr, map_sym_to_rat = cse_preprocess(sympyexpr, prefix=varprefix,
                declare=eval(outCparams.enable_SIMD), negative=factor_negative, factor=eval(outCparams.CSE_preprocess))
            for v in map_sym_to_rat:
                p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q)
                if outCparams.enable_SIMD == "False":
                    RATIONAL_decls += indent + 'const double ' + str(v) + ' = '
                    # Since Integer is a subclass of Rational in SymPy, we need only check whether
                    # the denominator q = 1 to determine if a rational is an integer.
                    if q != 1: RATIONAL_decls += str(p) + '/' + str(q) + ';\n'
                    else:      RATIONAL_decls += str(p) + ';\n'

        sympy_version = sp.__version__.replace('rc', '...').replace('b', '...')
        sympy_major_version = int(sympy_version.split(".")[0])
        sympy_minor_version = int(sympy_version.split(".")[1])
        if sympy_major_version < 1 or (sympy_major_version == 1 and sympy_minor_version < 3):
            print('Warning: SymPy version', sympy_version, 'does not support CSE postprocessing.')
            CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'),
                                 order=outCparams.CSE_sorting)
        else:
            CSE_results = cse_postprocess(sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'),
                                                 order=outCparams.CSE_sorting))

        for commonsubexpression in CSE_results[0]:
            FULLTYPESTRING = "const " + TYPE + " "
            if outCparams.enable_TYPE == "False":
                FULLTYPESTRING = ""

            if outCparams.enable_SIMD == "True":
                outstring += indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \
                             str(expr_convert_to_SIMD_intrins(commonsubexpression[1],map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\n"
            else:
                outstring += indent + FULLTYPESTRING + ccode_postproc(sp.ccode(commonsubexpression[1], commonsubexpression[0],
                                                                user_functions=custom_functions_for_SymPy_ccode)) + "\n"

        for i, result in enumerate(CSE_results[1]):
            if outCparams.enable_SIMD == "True":
                outstring += outtypestring + output_varname_str[i] + " = " + \
                             str(expr_convert_to_SIMD_intrins(result,map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\n"
            else:
                outstring += outtypestring+ccode_postproc(sp.ccode(result,output_varname_str[i],
                                                                   user_functions=custom_functions_for_SymPy_ccode))+"\n"
        # Complication: SIMD functions require numerical constants to be stored in SIMD arrays
        # Resolution: This function extends lists "SIMD_const_varnms" and "SIMD_const_values",
        #             which store the name of each constant SIMD array (e.g., _Integer_1) and
        #             the value of each variable (e.g., 1.0).
        if outCparams.enable_SIMD == "True":
            for v in map_sym_to_rat:
                p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q)
                SIMD_const_varnms.extend([str(v)])
                if q != 1: SIMD_const_values.extend([str(p) + '/' + str(q)])
                else:      SIMD_const_values.extend([str(p)])

        # Step 6b.i: If enable_SIMD == True , and
        #            there is at least one SIMD const variable,
        #            then declare the SIMD_const_varnms and SIMD_const_values arrays
        if outCparams.enable_SIMD == "True" and len(SIMD_const_varnms) != 0:
            # Step 6a) Sort the list of definitions. Idea from:
            # https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
            SIMD_const_varnms, SIMD_const_values = \
                (list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values))))
            # Step 6b) Remove duplicates
            uniq_varnms = superfast_uniq(SIMD_const_varnms)
            uniq_values = superfast_uniq(SIMD_const_values)
            SIMD_const_varnms = uniq_varnms
            SIMD_const_values = uniq_values
            if len(SIMD_const_varnms) != len(SIMD_const_values):
                print("Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!")
                sys.exit(1)

            for i in range(len(SIMD_const_varnms)):
                if outCparams.enable_TYPE == "False":
                    SIMD_RATIONAL_decls += indent + SIMD_const_varnms[i] + " = " + SIMD_const_values[i]+";"
                else:
                    SIMD_RATIONAL_decls += indent + "const double " + "tmp" + SIMD_const_varnms[i] + " = " + SIMD_const_values[i] + ";\n"
                    SIMD_RATIONAL_decls += indent + "const REAL_SIMD_ARRAY " + SIMD_const_varnms[i] + " = ConstSIMD(" + "tmp" + SIMD_const_varnms[i] + ");\n"
                SIMD_RATIONAL_decls += "\n"

    # Step 7: Construct final output string
    final_Ccode_output_str = commentblock
    # Step 7a: Output C code in indented curly brackets if
    #          outCparams.includebraces = True
    if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"{\n"
    final_Ccode_output_str += prestring + RATIONAL_decls + SIMD_RATIONAL_decls + outstring + poststring
    if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"}\n"

    # Step 8: If filename == "stdout", then output
    #         C code to standard out (useful for copy-paste or interactive
    #         mode). Otherwise output to file specified in variable name.
    if filename == "stdout":
        # Output to standard out (stdout; "the screen")
        print(final_Ccode_output_str)
    elif filename == "returnstring":
        return final_Ccode_output_str
    else:
        # Output to the file specified by the function input parameter string 'filename':
        with open(filename, outCparams.outCfileaccess) as file:
            file.write(final_Ccode_output_str)
        successstr = ""
        if outCparams.outCfileaccess == "a":
            successstr = "Appended "
        elif outCparams.outCfileaccess == "w":
            successstr = "Wrote "
        print(successstr + "to file \"" + filename + "\"")
Esempio n. 48
0
    def generate(self, expressions, functionname, const_function=True, callbacks=None):
        """Given a numpy nd-array of sympy expressions, return a function which
        will generate a C-style function that will compute the entries of the
        array.  The array is flattened out into a contiguous array with the
        last index varying fastest (row-major for matrices).

        """

        orig_shape = expressions.shape
        s =  "\n/** Computes the n-d array of shape ("
        for d in expressions.shape[:-1]:
            s += "{0}, ".format(d)
        s += "{0})\n *\n".format(expressions.shape[-1])
        s += " * @param[out] ar a C-array of with {0}".format(expressions.size)
        s += " elements\n */\n"

        expressions_flat = np.zeros((expressions.size,), dtype=object)
        for i, ai in enumerate(expressions.flat):
            if isinstance(ai, int):
                expressions_flat[i] = S(0)
            else:
                expressions_flat[i] = ai.subs(self.subs_dict)

        if self._class_name != "":
            member_function_name = self._class_name + "::" + functionname
            function_signature = "void {0}(double ar[{1}])".format(member_function_name, expressions_flat.size)
        else:
            function_signature = "void {0}(double ar[{1}])".format(functionname, expressions_flat.size)

        if const_function:
            function_signature += " const"
        s += "//  " + function_signature + ";\n"
        s += function_signature + "\n{\n"

        repl, redu = cse(expressions_flat, symbols=numbered_symbols("z"))

        if len(repl):
            s += "  double z[{0}];\n\n".format(len(repl))

        if callbacks is not None:
            for c in callbacks:
                cname, symbolname = c
                s += "  " + cname + "(" + symbolname + ");\n"

        for i, r in enumerate(repl):
            s += "  " + re.sub(r'z(\d+)', r'z[\1]', str(r[0])) + " = "
            tmp = re.sub(r'z(\d+)', r'z[\1]', ccode(r[1])) + ";\n"
            if self.state_prefix:
                tmp = re.sub(self.state_prefix + r'(\d+)',
                             self.state_prefix + r'[\1]', tmp)
            for p, r in self.regex_list:
                test = re.compile(p)
                tmp = test.sub(r, tmp)
            s += tmp

        s += "\n"
        for i, red_i in enumerate(redu):
            s += "  ar[{0}] = ".format(i)
            tmp = re.sub(r'z(\d+)', r'z[\1]', ccode(red_i))
            if self.state_prefix:
                tmp = re.sub(self.state_prefix + r'(\d+)',
                             self.state_prefix + r'[\1]', tmp)
            for p, r in self.regex_list:
                test = re.compile(p)
                tmp = test.sub(r, tmp)

            s += tmp
            s += ";\n"

        self.s += s + "}\n"

        return s
Esempio n. 49
0
def dse_cse(expr):
    """
    Perform common subexpression elimination on sympy expressions.

    :param expr: sympy equation or list of equations on which CSE is performed.

    :return: A list of the resulting equations after performing CSE
    """
    expr = expr if isinstance(expr, list) else [expr]

    temps, stencils = cse(expr, numbered_symbols("temp"))

    # Restores the LHS
    for i in range(len(expr)):
        stencils[i] = Eq(expr[i].lhs, stencils[i].rhs)

    to_revert = {}
    to_keep = []

    # Restores IndexedBases if they are collected by CSE and
    # reverts changes to simple index operations (eg: t - 1)
    for temp, value in temps:
        if isinstance(value, IndexedBase):
            to_revert[temp] = value
        elif isinstance(value, Indexed):
            to_revert[temp] = value
        elif isinstance(value, Add) and not \
                set([t, x, y, z]).isdisjoint(set(value.args)):
            to_revert[temp] = value
        else:
            to_keep.append((temp, value))

    # Restores the IndexedBases and the Indexes in the assignments to revert
    for temp, value in to_revert.items():
        s_dict = {}
        for arg in preorder_traversal(value):
            if isinstance(arg, Indexed):
                new_indices = []
                for index in arg.indices:
                    if index in to_revert:
                        new_indices.append(to_revert[index])
                    else:
                        new_indices.append(index)
                if arg.base.label in to_revert:
                    s_dict[arg] = Indexed(to_revert[value.base.label],
                                          *new_indices)
        to_revert[temp] = value.xreplace(s_dict)

    subs_dict = {}

    # Builds a dictionary of the replacements
    for expr in stencils + [assign for temp, assign in to_keep]:
        for arg in preorder_traversal(expr):
            if isinstance(arg, Indexed):
                new_indices = []
                for index in arg.indices:
                    if index in to_revert:
                        new_indices.append(to_revert[index])
                    else:
                        new_indices.append(index)
                if arg.base.label in to_revert:
                    subs_dict[arg] = Indexed(to_revert[arg.base.label],
                                             *new_indices)
                elif tuple(new_indices) != arg.indices:
                    subs_dict[arg] = Indexed(arg.base, *new_indices)
            if arg in to_revert:
                subs_dict[arg] = to_revert[arg]

    stencils = [stencil.xreplace(subs_dict) for stencil in stencils]

    to_keep = [Eq(temp[0], temp[1].xreplace(subs_dict)) for temp in to_keep]

    # If the RHS of a temporary variable is the LHS of a stencil,
    # update the value of the temporary variable after the stencil

    new_stencils = []

    for stencil in stencils:
        new_stencils.append(stencil)

        for temp in to_keep:
            if stencil.lhs in preorder_traversal(temp.rhs):
                new_stencils.append(temp)
                break

    return to_keep + new_stencils
def solve_motion_equations(M, B, state_vars=[], input_vars=[], parameters_values=dict()):
    '''
    Solves the motion equations given by the mass matrix and right hand side
    to define a callable function for the vector field of the respective
    control system.
    
    Parameters
    ----------
    
    M : sympy.Matrix
        A sympy.Matrix containing sympy expressions and symbols that represents
        the mass matrix of the control system.
    
    B : sympy.Matrix
        A sympy.Matrix containing sympy expressions and symbols that represents
        the right hand site of the motion equations.
    
    state_vars : list
        A list with sympy.Symbols's for each state variable.
    
    input_vars : list
        A list with sympy.Symbols's for each input variable.
    
    parameter_values : dict
        A dictionary with a key:value pair for each system parameter.
    
    Returns
    -------
    
    callable
    '''
    
    M_shape = M.shape
    B_shape = B.shape
    assert(M_shape[0] == B_shape[0])
    
    # at first we create a buffer for the string that we complete and execute 
    # to dynamically define a function and return it
    fnc_str_buffer ='''
def f(x, u):
    # System variables
    %s  # x_str
    %s  # u_str
    
    # Parameters
    %s  # par_str
    
    # Sympy Common Expressions
    %s # cse_str

    # Vectorfield
    %s  # ff_str
    
    return ff
'''

    #################################
    # handle system state variables #
    #################################
    # --> leads to x_str which shows how to unpack the state variables
    x_str = ''
    for var in state_vars:
        x_str += '%s, '%str(var)
        
    # as a last we remove the trailing '; ' to avoid syntax erros
    x_str = x_str + '= x'
    
    ##########################
    # handle input variables #
    ##########################
    # --> leads to u_str which will show how to unpack the inputs of the control system
    u_str = ''
    for var in input_vars:
        u_str += '%s, '%str(var)
    
    # after we remove the trailing '; ' to avoid syntax errors x_str will look like:
    #   'u1, u2, ... , um = u'
    u_str = u_str + '= u'
    
    ############################
    # handle system parameters #
    ############################
    # --> leads to par_str
    par_str = ''
    for k, v in parameters_values.items():
        # 'k' is the name of a system parameter such as mass or gravitational acceleration
        # 'v' is its value in SI units
        par_str += '%s = %s; '%(str(k), str(v))
    
    # as a last we remove the trailing '; ' from par_str to avoid syntax errors
    par_str = par_str[:-2]
    
    # now solve the motion equations w.r.t. the accelerations
    sol = M.solve(B)
    
    # use SymPy's Common Subexpression Elimination
    cse_list, cse_res = sp.cse(sol, symbols=sp.numbered_symbols('q'))
    
    ################################
    # handle common subexpressions #
    ################################
    # --> leads to cse_str
    cse_str = ''
    #cse_list = [(str(l), str(r)) for l, r in cse_list]
    for cse_pair in cse_list:
        cse_str += '%s = %s; '%(str(cse_pair[0]), str(cse_pair[1]))
    
    # add result of cse
    for i in xrange(M_shape[0]):
        cse_str += 'q%d_dd = %s; '%(i, str(cse_res[0][i]))
    
    cse_str = cse_str[:-2]
    
    ######################
    # create vectorfield #
    ######################
    # --> leads to ff_str
    ff_str = 'ff = ['
    
    for i in xrange(M_shape[0]):
        ff_str += '%s, '%str(state_vars[2*i+1])
        ff_str += 'q%s_dd, '%(i)
    
    # remove trailing ',' and add closing brackets
    ff_str = ff_str[:-2] + ']'
    
    ############################
    # Create callable function #
    ############################
    # now we can replace all placeholders in the function string buffer
    fnc_str = fnc_str_buffer%(x_str, u_str, par_str, cse_str, ff_str)
    # and finally execute it which will create a python function 'f'
    exec(fnc_str)
    
    # now we have defined a callable function that can be used within PyTrajectory
    return f
Esempio n. 51
0
def outputC(sympyexpr,
            output_varname_str,
            filename="stdout",
            params="",
            prestring="",
            poststring=""):
    outCparams = parse_outCparams_string(params)
    preindent = outCparams.preindent
    TYPE = par.parval_from_str("PRECISION")

    if outCparams.enable_TYPE == "False":
        TYPE = ""

    # Step 0: Initialize
    #  commentblock: comment block containing the input SymPy string,
    #                set only if outCverbose==True
    #  outstring:    the output C code string
    commentblock = ""
    outstring = ""

    # Step 1: If SIMD_enable==True, then check if TYPE=="double". If not, error out.
    #         Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd
    #         within the C code. For example for AVX-256, the C code should have
    #         #define REAL_SIMD_ARRAY __m256d
    if outCparams.SIMD_enable == "True":
        if not (TYPE == "double" or TYPE == ""):
            print(
                "SIMD output currently only supports double precision or typeless. Sorry!"
            )
            exit(1)
        if TYPE == "double":
            TYPE = "REAL_SIMD_ARRAY"
        else:
            TYPE = ""

    # Step 2a: Apply sanity checks when either sympyexpr or
    #          output_varname_str is a list.
    if type(output_varname_str) is list and type(sympyexpr) is not list:
        print(
            "Error: Provided a list of output variable names, but only one SymPy expression."
        )
        exit(1)
    if type(sympyexpr) is list:
        if type(output_varname_str) is not list:
            print(
                "Error: Provided a list of SymPy expressions, but no corresponding list of output variable names"
            )
            exit(1)
        elif len(output_varname_str) != len(sympyexpr):
            print("Error: Length of SymPy expressions list (" +
                  str(len(sympyexpr)) +
                  ") != Length of corresponding output variable name list (" +
                  str(len(output_varname_str)) + ")")
            exit(1)
    # Step 2b: If sympyexpr and output_varname_str are not lists,
    #          convert them to lists of one element each, to
    #          simplify proceeding code.
    if type(output_varname_str) is not list and type(sympyexpr) is not list:
        output_varname_strtmp = [output_varname_str]
        output_varname_str = output_varname_strtmp
        sympyexprtmp = [sympyexpr]
        sympyexpr = sympyexprtmp

    # Step 3: If outCparams.verbose = True, then output the original SymPy
    #         expression(s) in code comments prior to actual C code
    if outCparams.outCverbose == "True":
        commentblock += preindent + "/*\n" + preindent + " *  Original SymPy expression"
        if len(output_varname_str) > 1:
            commentblock += "s"
        commentblock += ":\n"
        for i in range(len(output_varname_str)):
            if i == 0:
                if len(output_varname_str) != 1:
                    commentblock += preindent + " *  \"["
                else:
                    commentblock += preindent + " *  \""
            else:
                commentblock += preindent + " *    "
            commentblock += output_varname_str[i] + " = " + str(sympyexpr[i])
            if i == len(output_varname_str) - 1:
                if len(output_varname_str) != 1:
                    commentblock += "]\"\n"
                else:
                    commentblock += "\"\n"
            else:
                commentblock += ",\n"
        commentblock += preindent + " */\n"

    # Step 4: Add proper indentation of C code:
    if outCparams.includebraces == "True":
        indent = outCparams.preindent + "   "
    else:
        indent = outCparams.preindent + ""

    # Step 5: Should the output variable, e.g., outvar, be declared?
    #         If so, start output line with e.g., "double outvar "
    outtypestring = ""
    if outCparams.declareoutputvars == "True":
        outtypestring = outCparams.preindent + indent + TYPE + " "
    else:
        outtypestring = outCparams.preindent + indent

    # Step 6a: If common subexpression elimination (CSE) disabled, then
    #         just output the SymPy string in the most boring way,
    #         nearly consistent with SymPy's ccode() function,
    #         though with support for float & long double types
    #         as well.
    SIMD_decls = ""

    if outCparams.CSE_enable == "False":
        # If CSE is disabled:
        for i in range(len(sympyexpr)):
            outstring += outtypestring + ccode_postproc(
                sp.ccode(sympyexpr[i], output_varname_str[i])) + "\n"
    # Step 6b: If CSE enabled, then perform CSE using SymPy and then
    #          resulting C code.
    else:
        # If CSE is enabled:
        SIMD_const_varnms = []
        SIMD_const_values = []

        CSE_results = sp.cse(sympyexpr,
                             sp.numbered_symbols(outCparams.CSE_varprefix),
                             order='canonical')
        for commonsubexpression in CSE_results[0]:
            FULLTYPESTRING = "const " + TYPE + " "
            if outCparams.enable_TYPE == "False":
                FULLTYPESTRING = ""

            if outCparams.SIMD_enable == "True":
                outstring += outCparams.preindent + indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \
                             str(expr_convert_to_SIMD_intrins(commonsubexpression[1],SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_debug)) + ";\n"
            else:
                outstring += outCparams.preindent + indent + FULLTYPESTRING + ccode_postproc(
                    sp.ccode(commonsubexpression[1],
                             commonsubexpression[0])) + "\n"
        for i, result in enumerate(CSE_results[1]):
            if outCparams.SIMD_enable == "True":
                outstring += outtypestring + output_varname_str[i] + " = " + \
                             str(expr_convert_to_SIMD_intrins(result,SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_debug)) + ";\n"
            else:
                outstring += outtypestring + ccode_postproc(
                    sp.ccode(result, output_varname_str[i])) + "\n"

        # Step 6b.i: If SIMD_enable == True , and
        #            there is at least one SIMD const variable,
        #            then declare the SIMD_const_varnms and SIMD_const_values arrays
        if outCparams.SIMD_enable == "True" and len(SIMD_const_varnms) != 0:
            # Step 6a) Sort the list of definitions. Idea from:
            # https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
            SIMD_const_varnms, SIMD_const_values = \
                (list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values))))
            # Step 6b) Remove duplicates
            uniq_varnms = superfast_uniq(SIMD_const_varnms)
            uniq_values = superfast_uniq(SIMD_const_values)
            SIMD_const_varnms = uniq_varnms
            SIMD_const_values = uniq_values
            if len(SIMD_const_varnms) != len(SIMD_const_values):
                print(
                    "Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!"
                )
                exit(1)

            for i in range(len(SIMD_const_varnms)):
                if outCparams.enable_TYPE == "False":
                    SIMD_decls += outCparams.preindent + indent + SIMD_const_varnms[
                        i] + " = " + SIMD_const_values[i] + ";"
                else:
                    SIMD_decls += outCparams.preindent + indent + "const double " + outCparams.CSE_varprefix + SIMD_const_varnms[
                        i] + " = " + SIMD_const_values[i] + ";\n"
                    SIMD_decls += outCparams.preindent + indent + "const REAL_SIMD_ARRAY " + SIMD_const_varnms[
                        i] + " = ConstSIMD(" + outCparams.CSE_varprefix + SIMD_const_varnms[
                            i] + ");\n"
                SIMD_decls += "\n"

    # Step 7: Construct final output string
    final_Ccode_output_str = commentblock
    # Step 7a: Output C code in indented curly brackets if
    #          outCparams.includebraces = True
    if outCparams.includebraces == "True":
        final_Ccode_output_str += outCparams.preindent + "{\n"
    final_Ccode_output_str += prestring + SIMD_decls + outstring + poststring
    if outCparams.includebraces == "True":
        final_Ccode_output_str += outCparams.preindent + "}\n"

    # Step 8: If filename == "stdout", then output
    #         C code to standard out (useful for copy-paste or interactive
    #         mode). Otherwise output to file specified in variable name.
    if filename == "stdout":
        # Output to standard out (stdout; "the screen")
        print(final_Ccode_output_str)
    elif filename == "returnstring":
        return final_Ccode_output_str
    else:
        # Output to the file specified by the function input parameter string 'filename':
        with open(filename, outCparams.outCfileaccess) as file:
            file.write(final_Ccode_output_str)
        successstr = ""
        if outCparams.outCfileaccess == "a":
            successstr = "Appended "
        elif outCparams.outCfileaccess == "w":
            successstr = "Wrote "
        print(successstr + "to file \"" + filename + "\"")
Esempio n. 52
0
import commutator as comm
from sympy import I, symbols, latex, numbered_symbols

p = comm.print_group
c = comm.calculate_commutator
N = comm.Ncproduct


X_gen = numbered_symbols('X')
Y_gen = numbered_symbols('Y')
L = 20
X = [next(X_gen) for i in range(L+1)]
Y = [next(Y_gen) for i in range(L+1)]
orders = dict(zip(X+Y,[1]*2*(L+1)))
p.orders = orders
Ypart = [N(I*Y[j+1], [2*j+1,2*j+4]) for j in range(L-2)]
Xpart = [N(-X[j+1], [2*j+1,2*j+2,2*j+3,2*j+4]) for j in range(L-2)]
small = Xpart+Ypart
Zpart = [N(-I, [2*j+2,2*j+3]) for j in range(L-1)]
Jpart = Zpart
H = small +Jpart

START_ORDER = 1
END_ORDER = 8

START_PSI = N(1, 'a1')

START_IOFVARS = []
START_SPLIT_ORDERS = [0, 1]
START_NORMDICT = {}
Esempio n. 53
0
def cse_lambdify(args, expr, **kwargs):
    '''
    Wrapper for sympy.lambdify which makes use of common subexpressions.
    '''
    
    # Note:
    # This was expected to speed up the evaluation of the created functions.
    # However performance gain is only at ca. 5%
    
    
    # check input expression
    if type(expr) == str:
        raise TypeError('Not implemented for string input expression!')

    # check given expression
    try:
        check_expression(expr)
    except TypeError as err:
        raise NotImplementedError("Only sympy expressions are allowed, yet")
    
    # get sequence of symbols from input arguments
    if type(args) == str:
        args = sp.symbols(args, seq=True)
    elif hasattr(args, '__iter__'):
        # this may kill assumptions
        args = [sp.Symbol(str(a)) for a in args]
        
    if not hasattr(args, '__iter__'):
        args = (args,)

    # get the common subexpressions
    cse_pairs, red_exprs = sp.cse(expr, symbols=sp.numbered_symbols('r'))
    if len(red_exprs) == 1:
        red_exprs = red_exprs[0]

    # check if sympy found any common subexpressions
    if not cse_pairs:
        # if not, use standard lambdify
        return sp.lambdify(args, expr, **kwargs)
    
    # now we are looking for those arguments that are part of the reduced expression(s)
    shortcuts = zip(*cse_pairs)[0]
    atoms = sp.Set(red_exprs).atoms()
    cse_args = [arg for arg in tuple(args) + tuple(shortcuts) if arg in atoms]

    # next, we create a function that evaluates the reduced expression
    cse_expr = red_exprs

    # if dummify is set to False then sympy.lambdify still returns a numpy.matrix
    # regardless of the possibly passed module dictionary {'ImmutableMatrix' : numpy.array}
    if kwargs.get('dummify') == False:
        kwargs['dummify'] = True

    reduced_exprs_fnc = sp.lambdify(args=cse_args, expr=cse_expr, **kwargs)
    
    # get the function that evaluates the replacement pairs
    modules = kwargs.get('modules')

    if modules is None:
        modules = ['math', 'numpy', 'sympy']
    
    namespaces = []
    if isinstance(modules, (dict, str)) or not hasattr(modules, '__iter__'):
        namespaces.append(modules)
    else:
        namespaces += list(modules)

    nspace = {}
    for m in namespaces[::-1]:
        nspace.update(_get_namespace(m))
    
    eval_pairs_fnc = make_cse_eval_function(input_args=args,
                                            replacement_pairs=cse_pairs,
                                            ret_filter=cse_args,
                                            namespace=nspace)

    # now we can wrap things together
    def cse_fnc(*args):
        cse_args_evaluated = eval_pairs_fnc(args)
        return reduced_exprs_fnc(*cse_args_evaluated)

    return cse_fnc
Esempio n. 54
0
from sympy import I, symbols, numbered_symbols, S
import commutator as comm

p = comm.print_group
N = comm.SigmaProduct

L = 20
V, J, J2, f = symbols('V J J2 f', real=True)
h_gen = numbered_symbols('h', real=True)
hs = [next(h_gen) for i in range(L + 1)]
vardict = {'J2': J2, 'f': f, 'V': V, 'J': J}
vardict.update(zip([str(h) for h in hs], hs))
orders = {f: 1}
Js = [J, J2]
p.orders = orders

fpart = [N(-f, ("x", [j])) for j in range(1, L + 1)]
hpart = [N(-hs[j], ("z", [j])) for j in range(1, L + 1)]
Jpart = [N(-J, ("zz", [j, j + 1])) for j in range(1, L)]

small = fpart
large = Jpart + hpart
H = large + small

zeroth_order = [N(1, "z10")]

print("Hamiltonian:")
p(H, breaks=False)
Esempio n. 55
0
    ("rear_.c", ((w + c)*N.x + ((rR + tR)*N.z & R.x)*R.x) & R.x),
    ("front_.Ixx", R.x & IFront & R.x),
    ("front_.Iyy", R.y & IFront & R.y),
    ("front_.Izz", R.z & IFront & R.z),
    ("front_.Ixz", R.x & IFront & R.z),
    ("front_.J", IFyy),
    ("front_.m", mH + mF),
    ("front_.R", rF),
    ("front_.r", tF),
    ("front_.a", FO_HO_mc.pos_from(FO) & R.x),
    ("front_.b", FO_HO_mc.pos_from(FO) & R.z),
    ("front_.c", (c*N.x + (rF + tF)*N.z) & R.x),
    ("ls_", ((rR + tR)*N.z + w * N.x - (rF + tF)*N.z) & R.z)]

eqns_to_cse = [rhs for lhs, rhs in expressions]
repl, redu = cse(eqns_to_cse, symbols=numbered_symbols("z"))

s  = '#include "bicycle.h"\n'
s += '#include "whipple.h"\n\n'
s += 'namespace bicycle {\n\n'
s += 'void Bicycle::set_parameters(const Whipple & w)\n{\n'
s += '  double * z = new double[{0}];\n\n'.format(len(repl))
for i, r in enumerate(repl):
    s += "  " + re.sub(r'z(\d+)', r'z[\1]', str(r[0])) + " = "
    s += re.sub(r'z(\d+)', r'z[\1]', ccode(r[1])) + ";\n"

s += "\n"
for i, (ex_i, red_i) in enumerate(zip(expressions, redu)):
    s += "  " + ex_i[0] + " = "
    s += re.sub(r'z(\d+)', r'z[\1]', ccode(red_i))
    s += ";\n"