コード例 #1
0
ファイル: separate_equations.py プロジェクト: yzerlaut/brian
def separate_equations(eqs, additional_dependencies=[]):
    eqs.prepare()
    all_vars = eqs._string.keys()
    # Construct a list of dependency sets, where each variable in each
    # dependency set induces a dependency on each other variable in each
    # dependency set
    depsets = []
    for var in all_vars:
        ids = set(get_identifiers(eqs._string[var]))
        ids = ids.intersection(all_vars)
        ids.add(var)
        depsets.append(ids)
    for expr in additional_dependencies:
        ids = set(get_identifiers(expr))
        ids = ids.intersection(all_vars)
        depsets.append(ids)
    # Construct a graph deps which indicates what variable depends on which
    # other variables (or is depended on by other variables).
    deps = defaultdict(set)
    for ids in depsets:
        for id1 in ids:
            for id2 in ids:
                deps[id1].add(id2)
    # Extract all the independent subgraphs
    ind_graphs = []
    while len(deps):
        ind_graphs.append(set(next_independent_subgraph(deps).keys()))
    if len(ind_graphs) == 1:
        return [eqs]
    # Finally, we construct an Equations object for each of the subgraphs
    ind_eqs = []
    for G in ind_graphs:
        neweqs = Equations()
        for var in G:
            if var in eqs._eq_names:
                neweqs.add_eq(var,
                              eqs._string[var],
                              eqs._units[var],
                              local_namespace=eqs._namespace[var])
            elif var in eqs._diffeq_names:
                nonzero = var in eqs._diffeq_names_nonzero
                neweqs.add_diffeq(var,
                                  eqs._string[var],
                                  eqs._units[var],
                                  local_namespace=eqs._namespace[var],
                                  nonzero=nonzero)
            elif var in eqs._alias.keys():
                neweqs.add_alias(var, eqs._string[var].strip())
            else:
                assert False
        ind_eqs.append(neweqs)
    return ind_eqs
コード例 #2
0
ファイル: separate_equations.py プロジェクト: JoErNanO/brian
def separate_equations(eqs, additional_dependencies=[]):
    eqs.prepare()
    all_vars = eqs._string.keys()
    # Construct a list of dependency sets, where each variable in each
    # dependency set induces a dependency on each other variable in each
    # dependency set
    depsets = []
    for var in all_vars:
        ids = set(get_identifiers(eqs._string[var]))
        ids = ids.intersection(all_vars)
        ids.add(var)
        depsets.append(ids)
    for expr in additional_dependencies:
        ids = set(get_identifiers(expr))
        ids = ids.intersection(all_vars)
        depsets.append(ids)
    # Construct a graph deps which indicates what variable depends on which
    # other variables (or is depended on by other variables).
    deps = defaultdict(set)
    for ids in depsets:
        for id1 in ids:
            for id2 in ids:
                deps[id1].add(id2)
    # Extract all the independent subgraphs
    ind_graphs = []
    while len(deps):
        ind_graphs.append(set(next_independent_subgraph(deps).keys()))
    if len(ind_graphs) == 1:
        return [eqs]
    # Finally, we construct an Equations object for each of the subgraphs
    ind_eqs = []
    for G in ind_graphs:
        neweqs = Equations()
        for var in G:
            if var in eqs._eq_names:
                neweqs.add_eq(var, eqs._string[var], eqs._units[var],
                              local_namespace=eqs._namespace[var])
            elif var in eqs._diffeq_names:
                nonzero = var in eqs._diffeq_names_nonzero
                neweqs.add_diffeq(var, eqs._string[var], eqs._units[var],
                                  local_namespace=eqs._namespace[var],
                                  nonzero=nonzero)
            elif var in eqs._alias.keys():
                neweqs.add_alias(var, eqs._string[var].strip())
            else:
                assert False
        ind_eqs.append(neweqs)
    return ind_eqs
コード例 #3
0
ファイル: model_documentation.py プロジェクト: yzerlaut/brian
 def to_sympy_expression(eq_string):
     """
     Simple helper function for converting an Equation string `eq_string` 
     (only the right hand side of an equation) into a `sympy` expression by
     calling `x = Symbol('x')` for every variable `x` in the equation.
     """
     l_namespace = {'Symbol': Symbol}
     # add all variables as sympy symbols to namespace
     for identifier in get_identifiers(eq_string):
         exec '%s = Symbol("%s")' % (identifier,
                                     identifier) in {}, l_namespace
     return eval(eq_string, {}, l_namespace)
コード例 #4
0
ファイル: model_documentation.py プロジェクト: JoErNanO/brian
 def to_sympy_expression(eq_string):
     """
     Simple helper function for converting an Equation string `eq_string` 
     (only the right hand side of an equation) into a `sympy` expression by
     calling `x = Symbol('x')` for every variable `x` in the equation.
     """
     l_namespace = {'Symbol': Symbol}
     # add all variables as sympy symbols to namespace        
     for identifier in get_identifiers(eq_string):
         exec '%s = Symbol("%s")' % (identifier, 
                                     identifier) in {}, l_namespace
     return eval(eq_string, {}, l_namespace)
コード例 #5
0
ファイル: threshold.py プロジェクト: JoErNanO/brian
def select_threshold(expr, eqs, level=0):
    '''
    Automatically selects the appropriate Threshold object from a string.
    
    Matches the following patterns:
    
    var_name > or >= const : Threshold
    var_name > or >= var_name : VariableThreshold
    others : StringThreshold
    '''
    global CThreshold, PythonThreshold
    use_codegen = (get_global_preference('usecodegen') and
                   get_global_preference('usecodegenthreshold'))
    use_weave = (get_global_preference('useweave') and
                 get_global_preference('usecodegenweave'))
    if use_codegen:
        if CThreshold is None:
            from brian.experimental.codegen.threshold import (CThreshold,
                                                              PythonThreshold)
        if use_weave:
            log_warn('brian.threshold', 'Using codegen CThreshold')
            return CThreshold(expr, level=level + 1)
        else:
            log_warn('brian.threshold', 'Using codegen PythonThreshold')
            return PythonThreshold(expr, level=level + 1)
    # plan:
    # - see if it matches A > B or A >= B, if not select StringThreshold
    # - check if A, B both match diffeq variable names, and if so
    #   select VariableThreshold
    # - check that A is a variable name, if not select StringThreshold
    # - extract all the identifiers from B, and if none of them are
    #   callable, assume it is a constant, try to eval it and then use
    #   Threshold. If not, or if eval fails, use StringThreshold.
    # This misses the case of e.g. V>10*mV*exp(1) because exp will be
    # callable, but in general a callable means that it could be
    # non-constant.
    expr = expr.strip()
    eqs.prepare()
    ns = namespace(expr, level=level + 1)
    s = re.search(r'^\s*(\w+)\s*>=?(.+)', expr)
    if not s:
        return StringThreshold(expr, level=level + 1)
    A = s.group(1)
    B = s.group(2).strip()
    if A not in eqs._diffeq_names:
        return StringThreshold(expr, level=level + 1)
    if B in eqs._diffeq_names:
        return VariableThreshold(B, A)
    try:
        vars = get_identifiers(B)
    except SyntaxError:
        return StringThreshold(expr, level=level + 1)
    all_vars = eqs._eq_names + eqs._diffeq_names + eqs._alias.keys() + ['t']
    for v in vars:
        if v not in ns or v in all_vars or callable(ns[v]):
            return StringThreshold(expr, level=level + 1)
    try:
        val = eval(B, ns)
    except:
        return StringThreshold(expr, level=level + 1)
    return Threshold(val, A)
コード例 #6
0
ファイル: synapses.py プロジェクト: hackolite/brian
    def __init__(self, source, target = None, model = None, pre = None, post = None,
             max_delay = 0*ms,
             level = 0,
             clock = None,code_namespace=None,
             unit_checking = True, method = None, freeze = False, implicit = False, order = 1): # model (state updater) related
        
        target=target or source # default is target=source

        # Check clocks. For the moment we enforce the same clocks for all objects
        clock = clock or source.clock
        if source.clock!=target.clock:
            raise ValueError,"Source and target groups must have the same clock"

        if pre is None:
            pre_list=[]
        elif isSequenceType(pre) and not isinstance(pre,str): # a list of pre codes
            pre_list=pre
        else:
            pre_list=[pre]

        pre_list=[flattened_docstring(pre) for pre in pre_list]
        if post is not None:
            post=flattened_docstring(post)

        # Pre and postsynaptic indexes (synapse -> pre/post)
        self.presynaptic=DynamicArray1D(0,dtype=smallest_inttype(len(source))) # this should depend on number of neurons
        self.postsynaptic=DynamicArray1D(0,dtype=smallest_inttype(len(target))) # this should depend on number of neurons

        if not isinstance(model,SynapticEquations):
            model=SynapticEquations(model,level=level+1)
        # Insert the lastupdate variable if necessary (if it is mentioned in pre/post, or if there is event-driven code)
        expr=re.compile(r'\blastupdate\b')
        if (len(model._eventdriven)>0) or \
           any([expr.search(pre) for pre in pre_list]) or \
           (post is not None and expr.search(post) is not None):
            model+='\nlastupdate : second\n'
            pre_list=[pre+'\nlastupdate=t\n' for pre in pre_list]
            if post is not None:
                post=post+'\nlastupdate=t\n'
        
        # Identify pre and post variables in the model string
        # They are identified by _pre and _post suffixes
        # or no suffix for postsynaptic variables
        ids=set()
        for RHS in model._string.itervalues():
            ids.update(get_identifiers(RHS))
        pre_ids = [id[:-4] for id in ids if id[-4:]=='_pre']
        post_ids = [id[:-5] for id in ids if id[-5:]=='_post']
        post_vars = [var for var in source.var_index if isinstance(var,str)] # postsynaptic variables
        post_ids2 = list(ids.intersection(set(post_vars))) # post variables without the _post suffix

        # remember whether our equations refer to any variables in the pre- or
        # postsynaptic group. This is important for the state-updater, e.g. the
        # equations can no longer be solved as linear equations.
        model.refers_others = (len(pre_ids) + len(post_ids) + len(post_ids2) > 0)

        # Insert static equations for pre and post variables
        S=self
        for name in pre_ids:
            model.add_eq(name+'_pre', 'S.source.'+name+'[S.presynaptic[:]]', source.unit(name),
                         global_namespace={'S':S})
        for name in post_ids:
            model.add_eq(name+'_post', 'S.target.'+name+'[S.postsynaptic[:]]', target.unit(name),
                         global_namespace={'S':S})
        for name in post_ids2: # we have to change the name of the variable to avoid problems with equation processing
            if name not in model._string: # check that it is not already defined
                model.add_eq(name, 'S.target.state_(__'+name+')[S.postsynaptic[:]]', target.unit(name),
                             global_namespace={'S':S,'__'+name:name})

        self.source=source
        self.target=target
        
        NeuronGroup.__init__(self, 0,model=model,clock=clock,level=level+1,unit_checking=unit_checking,method=method,freeze=freeze,implicit=implicit,order=order)
        '''
        At this point we have:
        * a state matrix _S with all variables
        * units, state dictionary with each value being a row of _S + the static equations
        * subgroups of synapses
        * link_var (i.e. we can link two synapses objects)
        * __len__
        * __setattr__: we can write S.w=array of values
        * var_index is a dictionary from names to row index in _S
        * num_states()
        
        Things we have that we don't want:
        * LS structure (but it will not be filled since the object does not spike)
        * (from Group) __getattr_ needs to be rewritten
        * a complete state updater, but we need to extract parameters and event-driven parts
        * The state matrix is not dynamic
        
        Things we may need to add:
        * _pre and _post suffixes
        '''       
        self._iscompressed=False # True if compress() has already been called
        
        # Look for event-driven code in the differential equations
        if use_sympy:
            eqs=self._eqs # an Equations object
            #vars=eqs._diffeq_names_nonzero # Dynamic variables
            vars=eqs._eventdriven.keys()
            var_set=set(vars)
            for var,RHS in eqs._eventdriven.iteritems():
                ids=get_identifiers(RHS)
                if len(set(list(ids)+[var]).intersection(var_set))==1:
                    # no external dynamic variable
                    # Now we test if it is a linear equation
                    _namespace=dict.fromkeys(ids,1.) # there is a possibility of problems here (division by zero)
                    # plus units problems? (maybe not since these are identifiers too)
                    # another option is to use random numbers, but that doesn't solve all problems
                    _namespace[var]=AffineFunction()
                    try:
                        eval(RHS,eqs._namespace[var],_namespace)
                    except: # not linear
                        raise TypeError,"Cannot turn equation for "+var+" into event-driven code"
                    z=symbolic_eval(RHS)
                    symbol_var=sympy.Symbol(var)
                    symbol_t=sympy.Symbol('t')-sympy.Symbol('lastupdate')
                    b=z.subs(symbol_var,0)
                    a=sympy.simplify(z.subs(symbol_var,1)-b)
                    if a==0:
                        expr=symbol_var+b*symbol_t
                    else:
                        expr=-b/a+sympy.exp(a*symbol_t)*(symbol_var+b/a)
                    expr=var+'='+str(expr)
                    # Replace pre and post code
                    # N.B.: the differential equations are kept, we will probably want to remove them!
                    pre_list=[expr+'\n'+pre for pre in pre_list]
                    if post is not None:
                        post=expr+'\n'+post
                else:
                    raise TypeError,"Cannot turn equation for "+var+" into event-driven code"
        elif len(self._eqs._eventdriven)>0:
            raise TypeError,"The Sympy package must be installed to produce event-driven code"

        if len(self._eqs._diffeq_names_nonzero)==0:
            self._state_updater=None
        
        # Set last spike to -infinity
        if 'lastupdate' in self.var_index:
            self.lastupdate=-1e6
        # _S is turned to a dynamic array - OK this is probably not good! we may lose references at this point
        S=self._S
        self._S=DynamicArray(S.shape)
        self._S[:]=S

        # Pre and postsynaptic delays (synapse -> delay_pre/delay_post)
        self._delay_pre=[DynamicArray1D(len(self),dtype=np.int16) for _ in pre_list] # max 32767 delays
        self._delay_post=DynamicArray1D(len(self),dtype=np.int16) # Actually only useful if there is a post code!
        
        # Pre and postsynaptic synapses (i->synapse indexes)
        max_synapses=2147483647 # it could be explicitly reduced by a keyword
        # We use a loop instead of *, otherwise only 1 dynamic array is created
        self.synapses_pre=[DynamicArray1D(0,dtype=smallest_inttype(max_synapses)) for _ in range(len(self.source))]
        self.synapses_post=[DynamicArray1D(0,dtype=smallest_inttype(max_synapses)) for _ in range(len(self.target))]

        # Code generation
        self._binomial = lambda n,p:np.random.binomial(np.array(n,dtype=int),p)

        self.contained_objects = []
        self.codes=[]
        self.namespaces=[]
        self.queues=[]
        for i,pre in enumerate(pre_list):
            code,_namespace=self.generate_code(pre,level+1,code_namespace=code_namespace)
            self.codes.append(code)
            self.namespaces.append(_namespace)
            self.queues.append(SpikeQueue(self.source, self.synapses_pre, self._delay_pre[i], max_delay = max_delay))
        
        if post is not None:
            code,_namespace=self.generate_code(post,level+1,direct=True,code_namespace=code_namespace)
            self.codes.append(code)
            self.namespaces.append(_namespace)
            self.queues.append(SpikeQueue(self.target, self.synapses_post, self._delay_post, max_delay = max_delay))

        self.queues_namespaces_codes = zip(self.queues,
                                           self.namespaces,
                                           self.codes)

        self.contained_objects+=self.queues
コード例 #7
0
def select_threshold(expr, eqs, level=0):
    '''
    Automatically selects the appropriate Threshold object from a string.
    
    Matches the following patterns:
    
    var_name > or >= const : Threshold
    var_name > or >= var_name : VariableThreshold
    others : StringThreshold
    '''
    global CThreshold, PythonThreshold
    use_codegen = (get_global_preference('usecodegen') and
                   get_global_preference('usecodegenthreshold'))
    use_weave = (get_global_preference('useweave') and
                 get_global_preference('usecodegenweave'))
    if use_codegen:
        if CThreshold is None:
            from brian.experimental.codegen.threshold import (CThreshold,
                                                              PythonThreshold)
        if use_weave:
            log_warn('brian.threshold', 'Using codegen CThreshold')
            return CThreshold(expr, level=level + 1)
        else:
            log_warn('brian.threshold', 'Using codegen PythonThreshold')
            return PythonThreshold(expr, level=level + 1)
    # plan:
    # - see if it matches A > B or A >= B, if not select StringThreshold
    # - check if A, B both match diffeq variable names, and if so
    #   select VariableThreshold
    # - check that A is a variable name, if not select StringThreshold
    # - extract all the identifiers from B, and if none of them are
    #   callable, assume it is a constant, try to eval it and then use
    #   Threshold. If not, or if eval fails, use StringThreshold.
    # This misses the case of e.g. V>10*mV*exp(1) because exp will be
    # callable, but in general a callable means that it could be
    # non-constant.
    expr = expr.strip()
    eqs.prepare()
    ns = namespace(expr, level=level + 1)
    s = re.search(r'^\s*(\w+)\s*>=?(.+)', expr)
    if not s:
        return StringThreshold(expr, level=level + 1)
    A = s.group(1)
    B = s.group(2).strip()
    if A not in eqs._diffeq_names:
        return StringThreshold(expr, level=level + 1)
    if B in eqs._diffeq_names:
        return VariableThreshold(B, A)
    try:
        vars = get_identifiers(B)
    except SyntaxError:
        return StringThreshold(expr, level=level + 1)
    all_vars = eqs._eq_names + eqs._diffeq_names + eqs._alias.keys() + ['t']
    for v in vars:
        if v not in ns or v in all_vars or callable(ns[v]):
            return StringThreshold(expr, level=level + 1)
    try:
        val = eval(B, ns)
    except:
        return StringThreshold(expr, level=level + 1)
    return Threshold(val, A)
コード例 #8
0
ファイル: integrodiff.py プロジェクト: JoErNanO/brian
def integral2differential(expr, T=20 * ms, level=0, N=20, suffix=None, matrix_output=False):
    '''
    Example:
      eqs,w=integral2differential('g(t)=t*exp(-t/tau)')
      M,nvar,w=integral2differential('g(t)=t*exp(-t/tau)',matrix_output=True)
      
    Returns an Equations object corresponding to the time-invariant linear system specified
    by the impulse response g(t), and the value w to generate the impulse response:
    g_in->g_in+w.
    
    If matrix_output is True, returns the matrix of the corresponding differential system, the
    index nvar of the variable and the initial condition w=x_nvar(0).
    
    T is the interval over which the function is calculated.
    N is the number of points chosen in that interval.
    level is the frame level where the expression is defined.
    suffix is a string added to internal variable names (default: unique string).
    '''
    # Expression matching
    varname, time, RHS = re.search('\s*(\w+)\s*\(\s*(\w+)\s*\)\s*=\s*(.+)\s*', expr).groups()

    # Build the namespace
    frame = inspect.stack()[level + 1][0]
    global_namespace, local_namespace = frame.f_globals, frame.f_locals
    # Find external objects
    vars = list(get_identifiers(RHS))
    namespace = {}
    for var in vars:
        if var == time: # time variable
            pass
        elif var in local_namespace: #local
            namespace[var] = local_namespace[var]
        elif var in global_namespace: #global
            namespace[var] = global_namespace[var]
        elif var in globals(): # typically units
            namespace[var] = globals()[var]

    # Convert to a function
    f = eval('lambda ' + time + ':' + RHS, namespace)

    # Unit
    unit = get_unit(f(rand()*second)).name

    # Pick N points
    t = rand(N) * T

    # Calculate derivatives and find rank
    n = 0
    rank = 0
    M = f(t).reshape(N, 1)
    while rank == n:
        n += 1
        dfn = differentiate(f, t, order=n).reshape(N, 1)
        x, _, rank, _ = linalg.lstsq(M, dfn)
        if rank == n:
            M = hstack([M, dfn])
            oldx = x
    # oldx expresses dfn as a function of df0,..,dfn-1 (n=rank)

    # Find initial condition
    X0 = array([differentiate(f, 0 * ms, order=n) for n in range(rank)])

    # Rescaling DOES NOT WORK
    #R=ones(rank)
    #for i in range(rank):
    #    if X0[i]!=0.:
    #        R[i]=1./X0[i]
    #    else:
    #        R[i]=1.
    #R=diag(R)
    #X0=dot(R,X0)
    #oldx=dot(R,oldx)

    # Build A
    A = diag(ones(rank - 1), 1)
    A[-1, :] = oldx.reshape(1, rank)

    # Find Q=P^{-1}
    Q = eye(rank)
    if X0[0] == 0.: # continuous g, spikes act on last variable: x->x+1
        Q[:, -1] = X0
        nvar = rank - 1
        w = 1.
        # Exact inversion
        P = eye(rank)
        P[:-1, -1] = -X0[:-1] / X0[-1] # Has to be !=0 !!
        P[-1, -1] = 1. / X0[-1]
    else: # discontinuous g, spikes act on first variable: x->x+g(0)
        Q[:, 0] = X0
        nvar = 0
        w = X0[0]
        P = linalg.inv(Q)

    M = dot(dot(P, A), Q)
    #M=dot(linalg.inv(R),dot(M,R))

    # Turn into string
    # Set variable names
    if rank < 5:
        names = [varname] + ['x', 'y', 'z'][:rank - 1]
    else:
        names = [varname] + ['x' + str(i) for i in range(rank - 1)]

    # Add suffix
    if suffix is None:
        suffix = unique_id()
    names[1:] = [name + suffix for name in names[1:]]

    # Build string
    eqs = []
    for i in range(rank):
        eqs.append('d' + names[i] + '/dt=' + '+'.join([str(x) + '*' + name for x, name in zip(M[i, :], names) if x != 0.]) +
                   ' : ' + str(unit))
    eqs.append(varname + '_in=' + names[nvar]) # alias
    eq_string = '\n'.join(eqs).replace('+-', '-')

    if matrix_output:
        return M, nvar, w
    else:
        return Equations(eq_string), w
コード例 #9
0
def integral2differential(expr,
                          T=20 * ms,
                          level=0,
                          N=20,
                          suffix=None,
                          matrix_output=False):
    '''
    Example:
      eqs,w=integral2differential('g(t)=t*exp(-t/tau)')
      M,nvar,w=integral2differential('g(t)=t*exp(-t/tau)',matrix_output=True)
      
    Returns an Equations object corresponding to the time-invariant linear system specified
    by the impulse response g(t), and the value w to generate the impulse response:
    g_in->g_in+w.
    
    If matrix_output is True, returns the matrix of the corresponding differential system, the
    index nvar of the variable and the initial condition w=x_nvar(0).
    
    T is the interval over which the function is calculated.
    N is the number of points chosen in that interval.
    level is the frame level where the expression is defined.
    suffix is a string added to internal variable names (default: unique string).
    '''
    # Expression matching
    varname, time, RHS = re.search('\s*(\w+)\s*\(\s*(\w+)\s*\)\s*=\s*(.+)\s*',
                                   expr).groups()

    # Build the namespace
    frame = inspect.stack()[level + 1][0]
    global_namespace, local_namespace = frame.f_globals, frame.f_locals
    # Find external objects
    vars = list(get_identifiers(RHS))
    namespace = {}
    for var in vars:
        if var == time:  # time variable
            pass
        elif var in local_namespace:  #local
            namespace[var] = local_namespace[var]
        elif var in global_namespace:  #global
            namespace[var] = global_namespace[var]
        elif var in globals():  # typically units
            namespace[var] = globals()[var]

    # Convert to a function
    f = eval('lambda ' + time + ':' + RHS, namespace)

    # Unit
    unit = get_unit(f(rand() * second)).name

    # Pick N points
    t = rand(N) * T

    # Calculate derivatives and find rank
    n = 0
    rank = 0
    M = f(t).reshape(N, 1)
    while rank == n:
        n += 1
        dfn = differentiate(f, t, order=n).reshape(N, 1)
        x, _, rank, _ = linalg.lstsq(M, dfn)
        if rank == n:
            M = hstack([M, dfn])
            oldx = x
    # oldx expresses dfn as a function of df0,..,dfn-1 (n=rank)

    # Find initial condition
    X0 = array([differentiate(f, 0 * ms, order=n) for n in range(rank)])

    # Rescaling DOES NOT WORK
    #R=ones(rank)
    #for i in range(rank):
    #    if X0[i]!=0.:
    #        R[i]=1./X0[i]
    #    else:
    #        R[i]=1.
    #R=diag(R)
    #X0=dot(R,X0)
    #oldx=dot(R,oldx)

    # Build A
    A = diag(ones(rank - 1), 1)
    A[-1, :] = oldx.reshape(1, rank)

    # Find Q=P^{-1}
    Q = eye(rank)
    if X0[0] == 0.:  # continuous g, spikes act on last variable: x->x+1
        Q[:, -1] = X0
        nvar = rank - 1
        w = 1.
        # Exact inversion
        P = eye(rank)
        P[:-1, -1] = -X0[:-1] / X0[-1]  # Has to be !=0 !!
        P[-1, -1] = 1. / X0[-1]
    else:  # discontinuous g, spikes act on first variable: x->x+g(0)
        Q[:, 0] = X0
        nvar = 0
        w = X0[0]
        P = linalg.inv(Q)

    M = dot(dot(P, A), Q)
    #M=dot(linalg.inv(R),dot(M,R))

    # Turn into string
    # Set variable names
    if rank < 5:
        names = [varname] + ['x', 'y', 'z'][:rank - 1]
    else:
        names = [varname] + ['x' + str(i) for i in range(rank - 1)]

    # Add suffix
    if suffix is None:
        suffix = unique_id()
    names[1:] = [name + suffix for name in names[1:]]

    # Build string
    eqs = []
    for i in range(rank):
        eqs.append('d' + names[i] + '/dt=' + '+'.join([
            str(x) + '*' + name for x, name in zip(M[i, :], names) if x != 0.
        ]) + ' : ' + str(unit))
    eqs.append(varname + '_in=' + names[nvar])  # alias
    eq_string = '\n'.join(eqs).replace('+-', '-')

    if matrix_output:
        return M, nvar, w
    else:
        return Equations(eq_string), w