예제 #1
0
    def split_stochastic(self):
        '''
        Split the expression into a stochastic and non-stochastic part.
        
        Splits the expression into a tuple of one `Expression` objects f (the
        non-stochastic part) and a dictionary mapping stochastic variables
        to `Expression` objects. For example, an expression of the form 
        ``f + g * xi_1 + h * xi_2`` would be returned as:
        ``(f, {'xi_1': g, 'xi_2': h})``
        Note that the `Expression` objects for the stochastic parts do not
        include the stochastic variable itself. 
        
        Returns
        -------
        (f, d) : (`Expression`, dict)
            A tuple of an `Expression` object and a dictionary, the first
            expression being the non-stochastic part of the equation and 
            the dictionary mapping stochastic variables (``xi`` or starting
            with ``xi_``) to `Expression` objects. If no stochastic variable
            is present in the code string, a tuple ``(self, None)`` will be
            returned with the unchanged `Expression` object.
        '''
        stochastic_variables = []
        for identifier in self.identifiers:
            if identifier == 'xi' or identifier.startswith('xi_'):
                stochastic_variables.append(identifier)

        # No stochastic variable
        if not len(stochastic_variables):
            return (self, None)

        s_expr = self.sympy_expr.expand()

        stochastic_symbols = [
            sympy.Symbol(variable, real=True)
            for variable in stochastic_variables
        ]

        f = sympy.Wild('f', exclude=stochastic_symbols)  # non-stochastic part
        match_objects = [
            sympy.Wild('w_' + variable, exclude=stochastic_symbols)
            for variable in stochastic_variables
        ]
        match_expression = f
        for symbol, match_object in zip(stochastic_symbols, match_objects):
            match_expression += match_object * symbol
        matches = s_expr.match(match_expression)

        if matches is None:
            raise ValueError(
                ('Expression "%s" cannot be separated into stochastic '
                 'and non-stochastic term') % self.code)

        f_expr = Expression(sympy_to_str(matches[f]))
        stochastic_expressions = dict(
            (variable, Expression(sympy_to_str(matches[match_object])))
            for (variable,
                 match_object) in zip(stochastic_variables, match_objects))

        return (f_expr, stochastic_expressions)
예제 #2
0
    def __call__(self, equations, variables=None):
        system = get_conditionally_linear_system(equations)

        code = []
        for var, (A, B) in system.iteritems():
            s_var = sp.Symbol(var)
            s_dt = sp.Symbol('dt')
            if A == 0:
                update_expression = s_var + s_dt * B
            elif B != 0:
                BA = B / A
                # Avoid calculating B/A twice
                BA_name = '_BA_' + var
                s_BA = sp.Symbol(BA_name)
                code += [BA_name + ' = ' + sympy_to_str(BA)]
                update_expression = (s_var + s_BA) * sp.exp(A * s_dt) - s_BA
            else:
                update_expression = s_var * sp.exp(A * s_dt)

            # The actual update step
            update = '_{var} = {expr}'
            code += [
                update.format(var=var, expr=sympy_to_str(update_expression))
            ]

        # Replace all the variables with their updated value
        for var in system:
            code += ['{var} = _{var}'.format(var=var)]

        return '\n'.join(code)
예제 #3
0
 def __call__(self, equations, variables=None):
     system = get_conditionally_linear_system(equations)
     
     code = []
     for var, (A, B) in system.iteritems():
         s_var = sp.Symbol(var)
         s_dt = sp.Symbol('dt')
         if A == 0:
             update_expression = s_var + s_dt * B
         elif B != 0:
             BA = B / A
             # Avoid calculating B/A twice
             BA_name = '_BA_' + var
             s_BA = sp.Symbol(BA_name)
             code += [BA_name + ' = ' + sympy_to_str(BA)]
             update_expression = (s_var + s_BA)*sp.exp(A*s_dt) - s_BA
         else:
             update_expression = s_var*sp.exp(A*s_dt)
             
         # The actual update step
         update = '_{var} = {expr}'
         code += [update.format(var=var, expr=sympy_to_str(update_expression))]
     
     # Replace all the variables with their updated value
     for var in system:
         code += ['{var} = _{var}'.format(var=var)]
         
     return '\n'.join(code)
예제 #4
0
파일: exact.py 프로젝트: yayyme/brian2
    def __call__(self, equations, variables=None):
        
        if variables is None:
            variables = {}
        
        # Get a representation of the ODE system in the form of
        # dX/dt = M*X + B
        varnames, matrix, constants = get_linear_system(equations)

        # Make sure that the matrix M is constant, i.e. it only contains
        # external variables or constant variables
        symbols = set.union(*(el.atoms() for el in matrix))
        non_constant = _non_constant_symbols(symbols, variables)
        if len(non_constant):
            raise ValueError(('The coefficient matrix for the equations '
                              'contains the symbols %s, which are not '
                              'constant.') % str(non_constant))
        
        symbols = [Symbol(variable, real=True) for variable in varnames]
        solution = sp.solve_linear_system(matrix.row_join(constants), *symbols)
        b = sp.ImmutableMatrix([solution[symbol] for symbol in symbols]).transpose()
        
        # Solve the system
        dt = Symbol('dt', real=True, positive=True)
        A = (matrix * dt).exp()                
        C = sp.ImmutableMatrix([A.dot(b)]) - b
        _S = sp.MatrixSymbol('_S', len(varnames), 1)
        updates = A * _S + C.transpose()
        try:
            # In sympy 0.7.3, we have to explicitly convert it to a single matrix
            # In sympy 0.7.2, it is already a matrix (which doesn't have an
            # is_explicit method)
            updates = updates.as_explicit()
        except AttributeError:
            pass
        
        # The solution contains _S[0, 0], _S[1, 0] etc. for the state variables,
        # replace them with the state variable names 
        abstract_code = []
        for idx, (variable, update) in enumerate(zip(varnames, updates)):
            rhs = update.subs(_S[idx, 0], variable)
            identifiers = get_identifiers(sympy_to_str(rhs))
            for identifier in identifiers:
                if identifier in variables:
                    var = variables[identifier]
                    if var is None:
                        print identifier, variables
                    if var.scalar and var.constant:
                        float_val = var.get_value()
                        rhs = rhs.xreplace({Symbol(identifier, real=True): Float(float_val)})

            # Do not overwrite the real state variables yet, the update step
            # of other state variables might still need the original values
            abstract_code.append('_' + variable + ' = ' + sympy_to_str(rhs))
        
        # Update the state variables
        for variable in varnames:
            abstract_code.append('{variable} = _{variable}'.format(variable=variable))
        return '\n'.join(abstract_code)
예제 #5
0
    def split_stochastic(self):
        '''
        Split the expression into a stochastic and non-stochastic part.
        
        Splits the expression into a tuple of one `Expression` objects f (the
        non-stochastic part) and a dictionary mapping stochastic variables
        to `Expression` objects. For example, an expression of the form 
        ``f + g * xi_1 + h * xi_2`` would be returned as:
        ``(f, {'xi_1': g, 'xi_2': h})``
        Note that the `Expression` objects for the stochastic parts do not
        include the stochastic variable itself. 
        
        Returns
        -------
        (f, d) : (`Expression`, dict)
            A tuple of an `Expression` object and a dictionary, the first
            expression being the non-stochastic part of the equation and 
            the dictionary mapping stochastic variables (``xi`` or starting
            with ``xi_``) to `Expression` objects. If no stochastic variable
            is present in the code string, a tuple ``(self, None)`` will be
            returned with the unchanged `Expression` object.
        '''
        stochastic_variables = []
        for identifier in self.identifiers:
            if identifier == 'xi' or identifier.startswith('xi_'):
                stochastic_variables.append(identifier)
        
        # No stochastic variable
        if not len(stochastic_variables):
            return (self, None)
        
        s_expr = self.sympy_expr.expand()
        
        stochastic_symbols = [sympy.Symbol(variable, real=True)
                              for variable in stochastic_variables]

        f = sympy.Wild('f', exclude=stochastic_symbols)  # non-stochastic part
        match_objects = [sympy.Wild('w_'+variable, exclude=stochastic_symbols)
                         for variable in stochastic_variables]
        match_expression = f
        for symbol, match_object in zip(stochastic_symbols, match_objects):
            match_expression += match_object * symbol
        matches = s_expr.match(match_expression)
        
        if matches is None:
            raise ValueError(('Expression "%s" cannot be separated into stochastic '
                              'and non-stochastic term') % self.code)

        f_expr = Expression(sympy_to_str(matches[f]))
        stochastic_expressions = dict((variable, Expression(sympy_to_str(matches[match_object])))
                                        for (variable, match_object) in
                                        zip(stochastic_variables, match_objects))

        return (f_expr, stochastic_expressions)
예제 #6
0
 def __str__(self):
     s = '%s\n' % self.__class__.__name__
     
     if len(self.statements) > 0:
         s += 'Intermediate statements:\n'
         s += '\n'.join([(var + ' = ' + sympy_to_str(expr))
                         for var, expr in self.statements])
         s += '\n'
         
     s += 'Output:\n'
     s += sympy_to_str(self.output)
     return s
예제 #7
0
    def _get_substituted_expressions(self):
        '''
        Return a list of ``(varname, expr)`` tuples, containing all
        differential equations with all the static equation variables
        substituted with the respective expressions.
        
        Returns
        -------
        expr_tuples : list of (str, `CodeString`)
            A list of ``(varname, expr)`` tuples, where ``expr`` is a
            `CodeString` object with all static equation variables substituted
            with the respective expression.
        '''
        subst_exprs = []
        substitutions = {}
        for eq in self.ordered:
            # Skip parameters
            if eq.expr is None:
                continue

            new_sympy_expr = eq.expr.sympy_expr.subs(substitutions)
            new_str_expr = sympy_to_str(new_sympy_expr)
            expr = Expression(new_str_expr)

            if eq.type == STATIC_EQUATION:
                substitutions.update(
                    {sympy.Symbol(eq.varname, real=True): expr.sympy_expr})
            elif eq.type == DIFFERENTIAL_EQUATION:
                #  a differential equation that we have to check
                subst_exprs.append((eq.varname, expr))
            else:
                raise AssertionError('Unknown equation type %s' % eq.type)

        return subst_exprs
예제 #8
0
파일: equations.py 프로젝트: yayyme/brian2
    def _get_substituted_expressions(self):
        '''
        Return a list of ``(varname, expr)`` tuples, containing all
        differential equations with all the static equation variables
        substituted with the respective expressions.
        
        Returns
        -------
        expr_tuples : list of (str, `CodeString`)
            A list of ``(varname, expr)`` tuples, where ``expr`` is a
            `CodeString` object with all static equation variables substituted
            with the respective expression.
        '''
        subst_exprs = []
        substitutions = {}
        for eq in self.ordered:
            # Skip parameters
            if eq.expr is None:
                continue

            new_sympy_expr = eq.expr.sympy_expr.subs(substitutions)
            new_str_expr = sympy_to_str(new_sympy_expr)
            expr = Expression(new_str_expr)

            if eq.type == STATIC_EQUATION:
                substitutions.update({sympy.Symbol(eq.varname, real=True): expr.sympy_expr})
            elif eq.type == DIFFERENTIAL_EQUATION:
                #  a differential equation that we have to check
                subst_exprs.append((eq.varname, expr))
            else:
                raise AssertionError('Unknown equation type %s' % eq.type)

        return subst_exprs
예제 #9
0
파일: exact.py 프로젝트: yayyme/brian2
    def __call__(self, equations, variables=None):
        if variables is None:
            variables = {}

        if equations.is_stochastic:
            raise ValueError('Cannot solve stochastic equations with this state updater')

        diff_eqs = equations.substituted_expressions

        t = Symbol('t', real=True, positive=True)
        dt = Symbol('dt', real=True, positive=True)
        t0 = Symbol('t0', real=True, positive=True)
        f0 = Symbol('f0', real=True)
        # TODO: Shortcut for simple linear equations? Is all this effort really
        #       worth it?

        code = []
        for name, expression in diff_eqs:
            rhs = expression.sympy_expr
            non_constant = _non_constant_symbols(rhs.atoms(),
                                                 variables) - set([name])
            if len(non_constant):
                raise ValueError(('Equation for %s referred to non-constant '
                                  'variables %s') % (name, str(non_constant)))
            # We have to be careful and use the real=True assumption as well,
            # otherwise sympy doesn't consider the symbol a match to the content
            # of the equation
            var = Symbol(name, real=True)
            f = sp.Function(name)
            rhs = rhs.subs(var, f(t))
            derivative = sp.Derivative(f(t), t)
            diff_eq = sp.Eq(derivative, rhs)
            general_solution = sp.dsolve(diff_eq, f(t))
            # Check whether this is an explicit solution
            if not getattr(general_solution, 'lhs', None) == f(t):
                raise ValueError('Cannot explicitly solve: ' + str(diff_eq))
            # Solve for C1 (assuming "var" as the initial value and "t0" as time)
            if Symbol('C1') in general_solution:
                if Symbol('C2') in general_solution:
                    raise ValueError('Too many constants in solution: %s' % str(general_solution))
                constant_solution = sp.solve(general_solution, Symbol('C1'))
                if len(constant_solution) != 1:
                    raise ValueError(("Couldn't solve for the constant "
                                      "C1 in : %s ") % str(general_solution))
                constant = constant_solution[0].subs(t, t0).subs(f(t0), var)
                solution = general_solution.rhs.subs('C1', constant)
            else:
                solution = general_solution.rhs.subs(t, t0).subs(f(t0), var)
            # Evaluate the expression for one timestep
            solution = solution.subs(t, t + dt).subs(t0, t)
            # only try symplifying it -- it sometimes raises an error
            try:
                solution = solution.simplify()
            except ValueError:
                pass

            code.append(name + ' = ' + sympy_to_str(solution))

        return '\n'.join(code)
예제 #10
0
    def __call__(self, equations, variables=None):

        if variables is None:
            variables = {}

        # Get a representation of the ODE system in the form of
        # dX/dt = M*X + B
        varnames, matrix, constants = get_linear_system(equations)

        # Make sure that the matrix M is constant, i.e. it only contains
        # external variables or constant variables
        symbols = set.union(*(el.atoms() for el in matrix))
        non_constant = _non_constant_symbols(symbols, variables)
        if len(non_constant):
            raise ValueError(('The coefficient matrix for the equations '
                              'contains the symbols %s, which are not '
                              'constant.') % str(non_constant))

        symbols = [Symbol(variable, real=True) for variable in varnames]
        solution = sp.solve_linear_system(matrix.row_join(constants), *symbols)
        b = sp.ImmutableMatrix([solution[symbol]
                                for symbol in symbols]).transpose()

        # Solve the system
        dt = Symbol('dt', real=True, positive=True)
        A = (matrix * dt).exp()
        C = sp.ImmutableMatrix([A.dot(b)]) - b
        _S = sp.MatrixSymbol('_S', len(varnames), 1)
        updates = A * _S + C.transpose()
        try:
            # In sympy 0.7.3, we have to explicitly convert it to a single matrix
            # In sympy 0.7.2, it is already a matrix (which doesn't have an
            # is_explicit method)
            updates = updates.as_explicit()
        except AttributeError:
            pass

        # The solution contains _S[0, 0], _S[1, 0] etc. for the state variables,
        # replace them with the state variable names
        abstract_code = []
        for idx, (variable, update) in enumerate(zip(varnames, updates)):
            rhs = update.subs(_S[idx, 0], variable)
            identifiers = get_identifiers(sympy_to_str(rhs))
            for identifier in identifiers:
                if identifier in variables:
                    var = variables[identifier]
                    if var is None:
                        print identifier, variables
                    if var.scalar and var.constant:
                        float_val = var.get_value()
                        rhs = rhs.xreplace(
                            {Symbol(identifier, real=True): Float(float_val)})

            # Do not overwrite the real state variables yet, the update step
            # of other state variables might still need the original values
            abstract_code.append('_' + variable + ' = ' + sympy_to_str(rhs))

        # Update the state variables
        for variable in varnames:
            abstract_code.append(
                '{variable} = _{variable}'.format(variable=variable))
        return '\n'.join(abstract_code)
예제 #11
0
    def __call__(self, equations, variables=None):
        if variables is None:
            variables = {}

        if equations.is_stochastic:
            raise ValueError(
                'Cannot solve stochastic equations with this state updater')

        diff_eqs = equations.substituted_expressions

        t = Symbol('t', real=True, positive=True)
        dt = Symbol('dt', real=True, positive=True)
        t0 = Symbol('t0', real=True, positive=True)
        f0 = Symbol('f0', real=True)
        # TODO: Shortcut for simple linear equations? Is all this effort really
        #       worth it?

        code = []
        for name, expression in diff_eqs:
            rhs = expression.sympy_expr
            non_constant = _non_constant_symbols(rhs.atoms(), variables) - set(
                [name])
            if len(non_constant):
                raise ValueError(('Equation for %s referred to non-constant '
                                  'variables %s') % (name, str(non_constant)))
            # We have to be careful and use the real=True assumption as well,
            # otherwise sympy doesn't consider the symbol a match to the content
            # of the equation
            var = Symbol(name, real=True)
            f = sp.Function(name)
            rhs = rhs.subs(var, f(t))
            derivative = sp.Derivative(f(t), t)
            diff_eq = sp.Eq(derivative, rhs)
            general_solution = sp.dsolve(diff_eq, f(t))
            # Check whether this is an explicit solution
            if not getattr(general_solution, 'lhs', None) == f(t):
                raise ValueError('Cannot explicitly solve: ' + str(diff_eq))
            # Solve for C1 (assuming "var" as the initial value and "t0" as time)
            if Symbol('C1') in general_solution:
                if Symbol('C2') in general_solution:
                    raise ValueError('Too many constants in solution: %s' %
                                     str(general_solution))
                constant_solution = sp.solve(general_solution, Symbol('C1'))
                if len(constant_solution) != 1:
                    raise ValueError(("Couldn't solve for the constant "
                                      "C1 in : %s ") % str(general_solution))
                constant = constant_solution[0].subs(t, t0).subs(f(t0), var)
                solution = general_solution.rhs.subs('C1', constant)
            else:
                solution = general_solution.rhs.subs(t, t0).subs(f(t0), var)
            # Evaluate the expression for one timestep
            solution = solution.subs(t, t + dt).subs(t0, t)
            # only try symplifying it -- it sometimes raises an error
            try:
                solution = solution.simplify()
            except ValueError:
                pass

            code.append(name + ' = ' + sympy_to_str(solution))

        return '\n'.join(code)
예제 #12
0
 def evaluator(expr, ns):
     expr = sympy_to_str(expr)
     return eval(expr, ns)
예제 #13
0
    def _generate_RHS(self, eqs, var, eq_symbols, temp_vars, expr,
                      non_stochastic_expr, stochastic_expr):
        '''
        Helper function used in `__call__`. Generates the right hand side of
        an abstract code statement by appropriately replacing f, g and t.
        For example, given a differential equation ``dv/dt = -(v + I) / tau``
        (i.e. `var` is ``v` and `expr` is ``(-v + I) / tau``) together with
        the `rk2` step ``return x + dt*f(x +  k/2, t + dt/2)``
        (i.e. `non_stochastic_expr` is
        ``x + dt*f(x +  k/2, t + dt/2)`` and `stochastic_expr` is ``None``),
        produces ``v + dt*(-v - _k_v/2 + I + _k_I/2)/tau``.
                
        '''
        
        def replace_func(x, t, expr, temp_vars):
            '''
            Used to replace a single occurance of ``f(x, t)`` or ``g(x, t)``:
            `expr` is the non-stochastic (in the case of ``f``) or stochastic
            part (``g``) of the expression defining the right-hand-side of the
            differential equation describing `var`. It replaces the variable
            `var` with the value given as `x` and `t` by the value given for
            `t. Intermediate variables will be replaced with the appropriate
            replacements as well.
            
            For example, in the `rk2` integrator, the second step involves the
            calculation of ``f(k/2 + x, dt/2 + t)``.  If `var` is ``v`` and
            `expr` is ``-v / tau``, this will result in ``-(_k_v/2 + v)/tau``.
            
            Note that this deals with only one state variable `var`, given as
            an argument to the surrounding `_generate_RHS` function.
            '''

            try:
                s_expr = str_to_sympy(str(expr))
            except SympifyError as ex:
                raise ValueError('Error parsing the expression "%s": %s' %
                                 (expr, str(ex)))

            for var in eq_symbols:
                # Generate specific temporary variables for the state variable,
                # e.g. '_k_v' for the state variable 'v' and the temporary
                # variable 'k'.
                temp_var_replacements = dict(((self.symbols[temp_var],
                                               _symbol('_'+temp_var+'_'+var))
                                              for temp_var in temp_vars))
                # In the expression given as 'x', replace 'x' by the variable
                # 'var' and all the temporary variables by their
                # variable-specific counterparts.
                x_replacement = x.subs(self.symbols['x'], eq_symbols[var])
                x_replacement = x_replacement.subs(temp_var_replacements)
                
                # Replace the variable `var` in the expression by the new `x`
                # expression
                s_expr = s_expr.subs(eq_symbols[var], x_replacement)
                
            # Directly substitute the 't' expression for the symbol t, there
            # are no temporary variables to consider here.             
            s_expr = s_expr.subs(self.symbols['t'], t)
            
            return s_expr
        
        # Note: in the following we are silently ignoring the case that a
        # state updater does not care about either the non-stochastic or the
        # stochastic part of an equation. We do trust state updaters to
        # correctly specify their own abilities (i.e. they do not claim to
        # support stochastic equations but actually just ignore the stochastic
        # part). We can't really check the issue here, as we are only dealing
        # with one line of the state updater description. It is perfectly valid
        # to write the euler update as:
        #     non_stochastic = dt * f(x, t)
        #     stochastic = dt**.5 * g(x, t) * xi
        #     return x + non_stochastic + stochastic
        #
        # In the above case, we'll deal with lines which do not define either
        # the stochastic or the non-stochastic part.
        
        non_stochastic, stochastic = expr.split_stochastic()
        # We do have a non-stochastic part in our equation and in the state
        # updater description 
        if not (non_stochastic is None or non_stochastic_expr is None):
            # Replace the f(x, t) part
            replace_f = lambda x, t:replace_func(x, t, non_stochastic,
                                                 temp_vars)
            non_stochastic_result = non_stochastic_expr.replace(self.symbols['f'],
                                                                replace_f)
            # Replace x by the respective variable
            non_stochastic_result = non_stochastic_result.subs(self.symbols['x'],
                                                               eq_symbols[var])
            # Replace intermediate variables
            temp_var_replacements = dict((self.symbols[temp_var],
                                           _symbol('_'+temp_var+'_'+var))
                                         for temp_var in temp_vars)
            non_stochastic_result = non_stochastic_result.subs(temp_var_replacements)
        else:
            non_stochastic_result = None

        # We do have a stochastic part in our equation and in the state updater
        # description
        if not (stochastic is None or stochastic_expr is None):
            stochastic_results = []
            
            # We potentially have more than one stochastic variable
            for xi in stochastic:
                # Replace the g(x, t)*xi part
                replace_g = lambda x, t:replace_func(x, t, stochastic[xi],
                                                     temp_vars)
                stochastic_result = stochastic_expr.replace(self.symbols['g'],
                                                            replace_g)
                
                # Replace x and xi by the respective variables
                stochastic_result = stochastic_result.subs(self.symbols['x'],
                                                           eq_symbols[var])
                stochastic_result = stochastic_result.subs(self.symbols['dW'], xi)   

                # Replace intermediate variables
                temp_var_replacements = dict((self.symbols[temp_var],
                                               _symbol('_'+temp_var+'_'+var))
                                             for temp_var in temp_vars)
                
                stochastic_result = stochastic_result.subs(temp_var_replacements)

                stochastic_results.append(stochastic_result)                        
        else:
            stochastic_results = []
        
        RHS = []
        # All the parts (one non-stochastic and potentially more than one
        # stochastic part) are combined with addition
        if non_stochastic_result is not None:
            RHS.append(sympy_to_str(non_stochastic_result))
        for stochastic_result in stochastic_results:
            RHS.append(sympy_to_str(stochastic_result))
        
        RHS = ' + '.join(RHS)
        return RHS
예제 #14
0
 def evaluator(expr, ns):
     expr = sympy_to_str(expr)
     return eval(expr, ns)