def generate_code(self,code,level,direct=False,code_namespace=None): ''' Generates pre and post code. ``code'' The code as a string. ``level'' The namespace level in which the code is executed. ``direct=False'' If True, the code is generated assuming that postsynaptic variables are not modified. This makes the code faster. ``code_namespace'' Additional namespace (highest priority) TODO: * include static variables (substitution) * have a list of variable names ''' # Handle multi-line pre, post equations and multi-statement equations separated by ; # (this should probably be factored) if '\n' in code: code = flattened_docstring(code) elif ';' in code: code = '\n'.join([line.strip() for line in code.split(';')]) # Create namespaces _namespace = namespace(code, level = level + 1) if code_namespace is not None: _namespace.update(code_namespace) _namespace['target'] = self.target # maybe we could save one indirection here _namespace['unique'] = np.unique _namespace['nonzero'] = np.nonzero _namespace['empty'] = np.empty _namespace['logical_not'] = np.logical_not _namespace['not_equal'] = np.not_equal _namespace['take'] = np.take _namespace['extract'] = np.extract _namespace['add'] = np.add _namespace['hstack'] = np.hstack code = re.sub(r'\b' + 'rand\(\)', 'rand(n)', code) code = re.sub(r'\b' + 'randn\(\)', 'randn(n)', code) # Generate the code def update_code(code, indices, postinds): res = code # given the synapse indices, write the update code, # this is here because in the code we generate we need to write this twice (because of the multiple presyn spikes for the same postsyn neuron problem) # Replace synaptic variables by their value for var in self.var_index: # static variables are not included here if isinstance(var, str): res = re.sub(r'\b' + var + r'\b', var + '['+indices+']', res) # synaptic variable, indexed by the synapse number # Replace postsynaptic variables by their value for postsyn_var in self.target.var_index: # static variables are not included here if isinstance(postsyn_var, str): #res = re.sub(r'\b' + postsyn_var + r'_post\b', 'target.' + postsyn_var + '['+postinds+']', res)# postsyn variable, indexed by post syn neuron numbers #res = re.sub(r'\b' + postsyn_var + r'\b', 'target.' + postsyn_var + '['+postinds+']', res)# postsyn variable, indexed by post syn neuron numbers res = re.sub(r'\b' + postsyn_var + r'_post\b', '_target_' + postsyn_var + '['+postinds+']', res)# postsyn variable, indexed by post syn neuron numbers res = re.sub(r'\b' + postsyn_var + r'\b', '_target_' + postsyn_var + '['+postinds+']', res)# postsyn variable, indexed by post syn neuron numbers _namespace['_target_' + postsyn_var] = self.target.state_(postsyn_var) # Replace presynaptic variables by their value for presyn_var in self.source.var_index: # static variables are not included here if isinstance(presyn_var, str): #res = re.sub(r'\b' + presyn_var + r'_pre\b', 'source.' + presyn_var + '[_pre['+indices+']]', res)# postsyn variable, indexed by post syn neuron numbers res = re.sub(r'\b' + presyn_var + r'_pre\b', '_source_' + presyn_var + '[_pre['+indices+']]', res)# postsyn variable, indexed by post syn neuron numbers _namespace['_source_' + presyn_var] = self.source.state_(presyn_var) # Replace n by number of synapses being updated res = re.sub(r'\bn\b','len('+indices+')', res) return res if direct: # direct update code, not caring about multiple accesses to postsynaptic variables code_str = '_post_neurons = _post[_synapses]\n'+update_code(code, '_synapses', '_post_neurons') + "\n" else: algo = 3 if algo==0: ## Old version using numpy's unique() code_str = "_post_neurons = _post[_synapses]\n" # not necessary to do a copy because _synapses is not a slice code_str += "_u, _i = unique(_post_neurons, return_index = True)\n" #code_str += update_code(code, '_synapses[_i]', '_u') + "\n" code_str += update_code(code, '_synapses[_i]', '_post[_synapses[_i]]') + "\n" code_str += "if len(_u) < len(_post_neurons):\n" code_str += " _post_neurons[_i] = -1\n" code_str += " while (len(_u) < len(_post_neurons)) & (_post_neurons>-1).any():\n" # !! the any() is time consuming (len(u)>=1??) #code_str += " while (len(_u) < len(_post_neurons)) & (len(_u)>1):\n" # !! the any() is time consuming (len(u)>=1??) code_str += " _u, _i = unique(_post_neurons, return_index = True)\n" code_str += indent(update_code(code, '_synapses[_i[1:]]', '_post[_synapses[_i[1:]]]'),2) + "\n" code_str += " _post_neurons[_i[1:]] = -1 \n" elif algo==1: code_str = "_post_neurons = _post[_synapses]\n" # not necessary to do a copy because _synapses is not a slice code_str += "_perm = _post_neurons.argsort()\n" code_str += "_aux = _post_neurons[_perm]\n" code_str += "_flag = empty(len(_aux) + 1, dtype = bool)\n" code_str += "_flag[0] = _flag[-1] = True\n" code_str += "not_equal(_aux[1:], _aux[:-1], _flag[1:-1])\n" code_str += "_F = _flag.nonzero()[0][:-1]\n" code_str += "logical_not(_flag, _flag)\n" code_str += "while len(_F):\n" code_str += " _u = _aux[_F]\n" code_str += " _i = _perm[_F]\n" code_str += indent(update_code(code, '_synapses[_i]', '_u'), 1) + "\n" code_str += " _F += 1\n" code_str += " _F = _F[_flag[_F]]\n" elif algo==2: code_str = ''' _post_neurons = _post.data.take(_synapses) _perm = _post_neurons.argsort() _aux = _post_neurons.take(_perm) _flag = empty(len(_aux)+1, dtype=bool) _flag[0] = _flag[-1] = 1 not_equal(_aux[1:], _aux[:-1], _flag[1:-1]) if 0:#_flag.sum()==len(_aux)+1: %(code1)s else: _F = _flag.nonzero()[0][:-1] logical_not(_flag, _flag) while len(_F): _u = _aux.take(_F) _i = _perm.take(_F) %(code2)s _F += 1 _F = extract(_flag.take(_F), _F) ''' code_str = flattened_docstring(code_str) % {'code1': indent(update_code(code, '_synapses','_post_neurons'), 1), 'code2': indent(update_code(code, '_synapses[_i]', '_u'), 2)} elif algo==3: code_str = ''' _post_neurons = _post.data.take(_synapses) _perm = _post_neurons.argsort() _aux = _post_neurons.take(_perm) _flag = empty(len(_aux)+1, dtype=bool) _flag[0] = _flag[-1] = 1 not_equal(_aux[1:], _aux[:-1], _flag[1:-1]) _F = _flag.nonzero()[0][:-1] logical_not(_flag, _flag) while len(_F): _u = _aux.take(_F) _i = _perm.take(_F) %(code)s _F += 1 _F = extract(_flag.take(_F), _F) ''' code_str = flattened_docstring(code_str) % {'code': indent(update_code(code, '_synapses[_i]', '_u'), 1)} elif algo==4: code_str = ''' _post_neurons = _post[_synapses] _perm = _post_neurons.argsort() _aux = _post_neurons[_perm] _flag = empty(len(_aux)+1, dtype=bool) _flag[0] = _flag[-1] = 1 not_equal(_aux[1:], _aux[:-1], _flag[1:-1]) _F = _flag.nonzero()[0][:-1] logical_not(_flag, _flag) while len(_F): _u = _aux[_F] _i = _perm[_F] %(code)s _F += 1 _F = _F[_flag[_F]] ''' code_str = flattened_docstring(code_str) % {'code': indent(update_code(code, '_synapses[_i]', '_u'), 1)} # print code_str log_debug('brian.synapses', '\nCODE:\n'+code_str) # Compile compiled_code = compile(code_str, "Synaptic code", "exec") _namespace['_original_code_string'] = code_str return compiled_code,_namespace
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
def __init__(self, source, target = None, clock = None, max_delay = 0, level = 0, model = None, check_units = True, method = None, freeze = False, implicit = False, order = 1, # model (state updater) related pre = '', post = ''): NetworkOperation.__init__(self, lambda:None, clock=clock) # Arguments parsing if target is None: self.source = self.target = source else: self.source = source self.target = target ########### CODE PARSING # model equations parsing if isinstance(model, Equations): model_obj = model else: if '\n' in model: model = flattened_docstring(model) elif ';' in model: model = '\n'.join([line.strip() for line in model.split(';')]) model_obj = Equations(model, level = level + 1) # stolen from NeuronGroup # !! This will not work with postsynaptic variables in the model eqs !! if isinstance(model_obj, StateUpdater): self._state_updater = model_obj self._all_units = defaultdict() # what is that elif isinstance(model_obj, Equations): self._eqs = model_obj self._state_updater, var_names = magic_state_updater(model_obj, clock=clock, order=order, check_units=check_units, implicit=implicit, compile=compile, freeze=freeze, method=method) self._state_updater_varnames = var_names # TODO: # - Maybe stateupdaters should be aware of the dtype of the data (or else my state vectors thing is sort of useless # - or ensure fast access to different data types in a vectorized way? # handle multi-line pre, post equations and multi-statement equations separated by ; if '\n' in pre: pre = flattened_docstring(pre) elif ';' in pre: pre = '\n'.join([line.strip() for line in pre.split(';')]) if '\n' in post: post = flattened_docstring(post) elif ';' in post: post = '\n'.join([line.strip() for line in post.split(';')]) # Check units model_obj.compile_functions() #model_obj.check_units() # Get variable names self.vars = model_obj._diffeq_names # !! there are also static variables and aliases !! ############# Setting up the data structure # Mandatory fields: 3 for pre/post/delay_pre, all int32 TODO: finer pick of dtype! # REPLACE THAT BY MORE SENSIBLE DATA STRUCTURE dtypes = (np.int32, ) * 3 default_labels = ['_pre', '_post', 'delay'] # Equation defined fields (float32) dtypes += (np.float32, ) * len(self.vars) # construction of the structure self._statevector = ConstructionSparseStateVector(len(dtypes), dtype = dtypes, labels = default_labels+self.vars) ############# Code!!! # create namespace pre_namespace = namespace(pre, level = level + 1) pre_namespace['target'] = self.target pre_namespace['unique'] = np.unique pre_namespace['nonzero'] = np.nonzero pre_namespace['_pre'] = self._statevector._pre pre_namespace['_post'] = self._statevector._post for var in self.vars: pre_namespace[var] = self._statevector[var] # !! also add postsynaptic variables !! def update_code(pre, indices): res = pre # given the synapse indices, write the update code, # this is here because in the code we generate we need to write this twice (because of the multiple presyn spikes for the same postsyn neuron problem) for postsyn_var in self.target.var_index: if isinstance(postsyn_var, str): res = re.sub(r'\b' + postsyn_var + r'\b', 'target.' + postsyn_var + '[_post['+indices+']]', res)# postsyn variable, indexed by post syn neuron numbers for var in self.vars: res = re.sub(r'\b' + var + r'\b', var + '['+indices+']', res) # synaptic variable, indexed by the synapse number return res # pre code pre_code = "_post_neurons = _post[_synapses]\n" # which post syn neurons pre_code += "_u, _i = unique(_post_neurons, return_index = True)\n" pre_code += update_code(pre, '_synapses[_i]') + "\n" pre_code += "if len(_u) < len(_post_neurons):\n" pre_code += " _post_neurons[_i] = -1\n" pre_code += " while (len(_u) < len(_post_neurons)) & (_post_neurons>-1).any():\n" # !! the any() is time consuming (len(u)>=1??) pre_code += " _u, _i = unique(_post_neurons, return_index = True)\n" pre_code += " " + update_code(pre, '_synapses[_i[1:]]') + "\n" pre_code += " _post_neurons[_i[1:]] = -1 \n" log_debug('brian.synapses', '\nPRE CODE:\n'+pre_code) pre_code = compile(pre_code, "Presynaptic code", "exec") self.pre_namespace = pre_namespace self.pre_code = pre_code self.pre_queue = SpikeQueue(self.source, self, max_delay = max_delay) self.contained_objects = [self.pre_queue] # wtf is this for: so that pre_queue.propagate is called self._pre_to_synapse = np.zeros(len(self.source), dtype = object) self._post_to_synapse = np.zeros(len(self.target), dtype = object)
def __init__(self, resolved, content, tabs=0): self.resolved = resolved if isinstance(content, str) and '\n' in content: content = [flattened_docstring(content)] self.content = content self.tabs = tabs
def __init__( self, source, target=None, clock=None, max_delay=0, level=0, model=None, check_units=True, method=None, freeze=False, implicit=False, order=1, # model (state updater) related pre='', post=''): NetworkOperation.__init__(self, lambda: None, clock=clock) # Arguments parsing if target is None: self.source = self.target = source else: self.source = source self.target = target ########### CODE PARSING # model equations parsing if isinstance(model, Equations): model_obj = model else: if '\n' in model: model = flattened_docstring(model) elif ';' in model: model = '\n'.join([line.strip() for line in model.split(';')]) model_obj = Equations(model, level=level + 1) # stolen from NeuronGroup # !! This will not work with postsynaptic variables in the model eqs !! if isinstance(model_obj, StateUpdater): self._state_updater = model_obj self._all_units = defaultdict() # what is that elif isinstance(model_obj, Equations): self._eqs = model_obj self._state_updater, var_names = magic_state_updater( model_obj, clock=clock, order=order, check_units=check_units, implicit=implicit, compile=compile, freeze=freeze, method=method) self._state_updater_varnames = var_names # TODO: # - Maybe stateupdaters should be aware of the dtype of the data (or else my state vectors thing is sort of useless # - or ensure fast access to different data types in a vectorized way? # handle multi-line pre, post equations and multi-statement equations separated by ; if '\n' in pre: pre = flattened_docstring(pre) elif ';' in pre: pre = '\n'.join([line.strip() for line in pre.split(';')]) if '\n' in post: post = flattened_docstring(post) elif ';' in post: post = '\n'.join([line.strip() for line in post.split(';')]) # Check units model_obj.compile_functions() #model_obj.check_units() # Get variable names self.vars = model_obj._diffeq_names # !! there are also static variables and aliases !! ############# Setting up the data structure # Mandatory fields: 3 for pre/post/delay_pre, all int32 TODO: finer pick of dtype! # REPLACE THAT BY MORE SENSIBLE DATA STRUCTURE dtypes = (np.int32, ) * 3 default_labels = ['_pre', '_post', 'delay'] # Equation defined fields (float32) dtypes += (np.float32, ) * len(self.vars) # construction of the structure self._statevector = ConstructionSparseStateVector( len(dtypes), dtype=dtypes, labels=default_labels + self.vars) ############# Code!!! # create namespace pre_namespace = namespace(pre, level=level + 1) pre_namespace['target'] = self.target pre_namespace['unique'] = np.unique pre_namespace['nonzero'] = np.nonzero pre_namespace['_pre'] = self._statevector._pre pre_namespace['_post'] = self._statevector._post for var in self.vars: pre_namespace[var] = self._statevector[var] # !! also add postsynaptic variables !! def update_code(pre, indices): res = pre # given the synapse indices, write the update code, # this is here because in the code we generate we need to write this twice (because of the multiple presyn spikes for the same postsyn neuron problem) for postsyn_var in self.target.var_index: if isinstance(postsyn_var, str): res = re.sub( r'\b' + postsyn_var + r'\b', 'target.' + postsyn_var + '[_post[' + indices + ']]', res ) # postsyn variable, indexed by post syn neuron numbers for var in self.vars: res = re.sub( r'\b' + var + r'\b', var + '[' + indices + ']', res) # synaptic variable, indexed by the synapse number return res # pre code pre_code = "_post_neurons = _post[_synapses]\n" # which post syn neurons pre_code += "_u, _i = unique(_post_neurons, return_index = True)\n" pre_code += update_code(pre, '_synapses[_i]') + "\n" pre_code += "if len(_u) < len(_post_neurons):\n" pre_code += " _post_neurons[_i] = -1\n" pre_code += " while (len(_u) < len(_post_neurons)) & (_post_neurons>-1).any():\n" # !! the any() is time consuming (len(u)>=1??) pre_code += " _u, _i = unique(_post_neurons, return_index = True)\n" pre_code += " " + update_code(pre, '_synapses[_i[1:]]') + "\n" pre_code += " _post_neurons[_i[1:]] = -1 \n" log_debug('brian.synapses', '\nPRE CODE:\n' + pre_code) pre_code = compile(pre_code, "Presynaptic code", "exec") self.pre_namespace = pre_namespace self.pre_code = pre_code self.pre_queue = SpikeQueue(self.source, self, max_delay=max_delay) self.contained_objects = [ self.pre_queue ] # wtf is this for: so that pre_queue.propagate is called self._pre_to_synapse = np.zeros(len(self.source), dtype=object) self._post_to_synapse = np.zeros(len(self.target), dtype=object)