def __init__(self, variables, namespace=None): self.variables = variables self.namespace = namespace # We use a unique name to get repeated warnings Group.__init__(self, name='simplegroup_' + str(uuid.uuid4()).replace('-', '_'))
def __init__(self, N, rates, dt=None, clock=None, when='thresholds', order=0, namespace=None, name='poissongroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, namespace=namespace, name=name) self.codeobj_class = codeobj_class self._N = N = int(N) # TODO: In principle, it would be nice to support Poisson groups with # refactoriness, but we can't currently, since the refractoriness # information is reset in the state updater which we are not using # We could either use a specific template or simply not bother and make # users write their own NeuronGroup (with threshold rand() < rates*dt) # for more complex use cases. self.variables = Variables(self) # standard variables self.variables.add_constant('N', value=self._N) self.variables.add_arange('i', self._N, constant=True, read_only=True) self.variables.add_array('_spikespace', size=N + 1, dtype=np.int32) self.variables.create_clock_variables(self._clock) # The firing rates if isinstance(rates, str): self.variables.add_subexpression('rates', dimensions=Hz.dim, expr=rates) else: self.variables.add_array('rates', size=N, dimensions=Hz.dim) self._rates = rates self.start = 0 self.stop = N self._refractory = False self.events = {'spike': 'rand() < rates * dt'} self.thresholder = {'spike': Thresholder(self)} self.contained_objects.append(self.thresholder['spike']) self._enable_group_attributes() if not isinstance(rates, str): self.rates = rates
def __init__(self, N, rates, dt=None, clock=None, when='thresholds', order=0, name='poissongroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) self.codeobj_class = codeobj_class self._N = N = int(N) # TODO: In principle, it would be nice to support Poisson groups with # refactoriness, but we can't currently, since the refractoriness # information is reset in the state updater which we are not using # We could either use a specific template or simply not bother and make # users write their own NeuronGroup (with threshold rand() < rates*dt) # for more complex use cases. self.variables = Variables(self) # standard variables self.variables.add_constant('N', unit=Unit(1), value=self._N) self.variables.add_arange('i', self._N, constant=True, read_only=True) self.variables.add_array('_spikespace', size=N + 1, unit=Unit(1), dtype=np.int32) self.variables.create_clock_variables(self._clock) # The firing rates self.variables.add_array('rates', size=N, unit=Hz) self.start = 0 self.stop = N self._refractory = False # To avoid a warning about the local variable rates, we set the real # threshold condition only after creating the object self.events = {'spike': 'False'} self.thresholder = {'spike': Thresholder(self)} self.events = {'spike': 'rand() < rates * dt'} self.contained_objects.append(self.thresholder['spike']) self._enable_group_attributes() # Here we want to use the local namespace, but at the level where the # constructor was called self.rates.set_item(slice(None), rates, level=2)
def __init__(self, source, start, stop, name=None): # First check if the source is itself a Subgroup # If so, then make this a Subgroup of the original Group if isinstance(source, Subgroup): source = source.source start = start + source.start stop = stop + source.start self.source = source else: self.source = weakproxy_with_fallback(source) if name is None: name = source.name + '_subgroup*' # We want to update the spikes attribute after it has been updated # by the parent, we do this in slot 'thresholds' with an order # one higher than the parent order to ensure it takes place after the # parent threshold operation schedule = Scheduler(clock=source.clock, when='thresholds', order=source.order + 1) Group.__init__(self, when=schedule, name=name) self._N = stop - start self.start = start self.stop = stop # All the variables have to go via the _sub_idx to refer to the # appropriate values in the source group self.variables = Variables(self, default_index='_sub_idx') # overwrite the meaning of N and i self.variables.add_constant('_offset', unit=Unit(1), value=self.start) self.variables.add_reference('_source_i', source, 'i') self.variables.add_subexpression('i', unit=Unit(1), dtype=source.variables['i'].dtype, expr='_source_i - _offset') self.variables.add_constant('N', unit=Unit(1), value=self._N) # add references for all variables in the original group self.variables.add_references(source, source.variables.keys()) # Only the variable _sub_idx itself is stored in the subgroup # and needs the normal index for this group self.variables.add_arange('_sub_idx', size=self._N, start=self.start, index='_idx') for key, value in self.source.variables.indices.iteritems(): if value not in ('_idx', '0'): raise ValueError( ('Do not know how to deal with variable %s ' 'using index %s in a subgroup') % (key, value)) self.namespace = self.source.namespace self.codeobj_class = self.source.codeobj_class self._enable_group_attributes()
def __init__(self, synapses, code, prepost, objname=None): self.code = code if prepost == 'pre': self.source = synapses.source self.target = synapses.target self.synapse_indices = synapses.item_mapping.pre_synaptic elif prepost == 'post': self.source = synapses.target self.target = synapses.source self.synapse_indices = synapses.item_mapping.post_synaptic else: raise ValueError('prepost argument has to be either "pre" or ' '"post"') self.synapses = synapses if objname is None: objname = prepost + '*' GroupCodeRunner.__init__(self, synapses, 'synapses', code=code, when=(synapses.clock, 'synapses'), name=synapses.name + '_' + objname) self._delays = DynamicArray1D(synapses.N, dtype=np.float64) # Register the object with the `SynapticIndex` object so it gets # automatically resized synapses.item_mapping.register_variable(self._delays) self.queue = SpikeQueue() self.spiking_synapses = [] self.variables = {'_spiking_synapses': AttributeVariable(Unit(1), self, 'spiking_synapses', constant=False), '_source_offset': Variable(Unit(1), self.source.offset, constant=True), '_target_offset': Variable(Unit(1), self.target.offset, constant=True), 'delay': DynamicArrayVariable('delay', second, self._delays, group_name=self.name, constant=True)} # Re-extract the last part of the name from the full name self.objname = self.name[len(synapses.name) + 1:] #: The simulation dt (necessary for the delays) self.dt = self.synapses.clock.dt_ self.item_mapping = synapses.item_mapping self.indices = self.synapses.indices # Enable access to the delay attribute via the specifier Group.__init__(self)
def __init__(self, source, start, stop, name=None): # First check if the source is itself a Subgroup # If so, then make this a Subgroup of the original Group if isinstance(source, Subgroup): source = source.source start = start + source.start stop = stop + source.start self.source = source else: self.source = weakproxy_with_fallback(source) if name is None: name = source.name + '_subgroup*' # We want to update the spikes attribute after it has been updated # by the parent, we do this in slot 'thresholds' with an order # one higher than the parent order to ensure it takes place after the # parent threshold operation schedule = Scheduler(clock=source.clock, when='thresholds', order=source.order+1) Group.__init__(self, when=schedule, name=name) self._N = stop-start self.start = start self.stop = stop # All the variables have to go via the _sub_idx to refer to the # appropriate values in the source group self.variables = Variables(self, default_index='_sub_idx') # overwrite the meaning of N and i self.variables.add_constant('_offset', unit=Unit(1), value=self.start) self.variables.add_reference('_source_i', source, 'i') self.variables.add_subexpression('i', unit=Unit(1), dtype=source.variables['i'].dtype, expr='_source_i - _offset') self.variables.add_constant('N', unit=Unit(1), value=self._N) # add references for all variables in the original group self.variables.add_references(source, source.variables.keys()) # Only the variable _sub_idx itself is stored in the subgroup # and needs the normal index for this group self.variables.add_arange('_sub_idx', size=self._N, start=self.start, index='_idx') for key, value in self.source.variables.indices.iteritems(): if value not in ('_idx', '0'): raise ValueError(('Do not know how to deal with variable %s ' 'using index %s in a subgroup') % (key, value)) self.namespace = self.source.namespace self.codeobj_class = self.source.codeobj_class self._enable_group_attributes()
def __init__(self, N, rates, dt=None, clock=None, when='thresholds', order=0, namespace=None, name='poissongroup*', codeobj_class=None): if namespace is None: namespace = {} #: The group-specific namespace self.namespace = namespace Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) self.codeobj_class = codeobj_class self._N = N = int(N) # TODO: In principle, it would be nice to support Poisson groups with # refactoriness, but we can't currently, since the refractoriness # information is reset in the state updater which we are not using # We could either use a specific template or simply not bother and make # users write their own NeuronGroup (with threshold rand() < rates*dt) # for more complex use cases. self.variables = Variables(self) # standard variables self.variables.add_constant('N', unit=Unit(1), value=self._N) self.variables.add_arange('i', self._N, constant=True, read_only=True) self.variables.add_array('_spikespace', size=N+1, unit=Unit(1), dtype=np.int32) self.variables.create_clock_variables(self._clock) # The firing rates if isinstance(rates, basestring): self.variables.add_subexpression('rates', unit=Hz, expr=rates) else: self.variables.add_array('rates', size=N, unit=Hz) self._rates = rates self.start = 0 self.stop = N self._refractory = False self.events = {'spike': 'rand() < rates * dt'} self.thresholder = {'spike': Thresholder(self)} self.contained_objects.append(self.thresholder['spike']) self._enable_group_attributes() if not isinstance(rates, basestring): self.rates = rates
def __init__(self, N, rates, dt=None, clock=None, when='thresholds', order=0, name='poissongroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) self.codeobj_class = codeobj_class self._N = N = int(N) # TODO: In principle, it would be nice to support Poisson groups with # refactoriness, but we can't currently, since the refractoriness # information is reset in the state updater which we are not using # We could either use a specific template or simply not bother and make # users write their own NeuronGroup (with threshold rand() < rates*dt) # for more complex use cases. self.variables = Variables(self) # standard variables self.variables.add_constant('N', unit=Unit(1), value=self._N) self.variables.add_arange('i', self._N, constant=True, read_only=True) self.variables.add_array('_spikespace', size=N+1, unit=Unit(1), dtype=np.int32) self.variables.create_clock_variables(self._clock) # The firing rates self.variables.add_array('rates', size=N, unit=Hz) self.start = 0 self.stop = N self._refractory = False # To avoid a warning about the local variable rates, we set the real # threshold condition only after creating the object self.events = {'spike': 'False'} self.thresholder = {'spike': Thresholder(self)} self.events = {'spike': 'rand() < rates * dt'} self.contained_objects.append(self.thresholder['spike']) self._enable_group_attributes() # Here we want to use the local namespace, but at the level where the # constructor was called self.rates.set_item(slice(None), rates, level=2)
def __init__(self, source, start, end, name=None): self.source = weakref.proxy(source) if name is None: name = source.name + '_subgroup*' # We want to update the spikes attribute after it has been updated # by the parent, we do this in slot 'thresholds' with an order # one higher than the parent order to ensure it takes place after the # parent threshold operation schedule = Scheduler(clock=source.clock, when='thresholds', order=source.order+1) BrianObject.__init__(self, when=schedule, name=name) self.N = end-start self.start = start self.end = end self.offset = start self.variables = self.source.variables self.variable_indices = self.source.variable_indices self.namespace = self.source.namespace self.codeobj_class = self.source.codeobj_class Group.__init__(self)
def __init__(self, source, start, end, name=None): self.source = weakref.proxy(source) if name is None: name = source.name + '_subgroup*' # We want to update the spikes attribute after it has been updated # by the parent, we do this in slot 'thresholds' with an order # one higher than the parent order to ensure it takes place after the # parent threshold operation schedule = Scheduler(clock=source.clock, when='thresholds', order=source.order + 1) BrianObject.__init__(self, when=schedule, name=name) self.N = end - start self.start = start self.end = end self.offset = start self.variables = self.source.variables self.variable_indices = self.source.variable_indices self.namespace = self.source.namespace self.codeobj_class = self.source.codeobj_class Group.__init__(self)
def __init__(self, source, variables, record=None, when=None, name=None): self.source = weakref.proxy(source) # run by default on source clock at the end scheduler = Scheduler(when) if not scheduler.defined_clock: scheduler.clock = source.clock if not scheduler.defined_when: scheduler.when = 'end' BrianObject.__init__(self, when=scheduler, name=name) # variables should always be a list of strings if variables is True: variables = source.units.keys() elif isinstance(variables, str): variables = [variables] self.variables = variables # record should always be an array of ints if record is None or record is False: record = array([], dtype=int) elif record is True: record = arange(len(source)) else: record = array(record, dtype=int) #: The array of recorded indices self.indices = record # create data structures self.reinit() # initialise Group access self.units = dict((var, source.units[var]) for var in variables) self.arrays = {} Group.__init__(self)
def __init__(self, N, indices, times, dt=None, clock=None, period=1e100*second, when='thresholds', order=0, sorted=False, name='spikegeneratorgroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) # Let other objects know that we emit spikes events self.events = {'spike': None} self.codeobj_class = codeobj_class if N < 1 or int(N) != N: raise ValueError('N has to be an integer >=1.') if len(indices) != len(times): raise ValueError(('Length of the indices and times array must ' 'match, but %d != %d') % (len(indices), len(times))) if period < 0*second: raise ValueError('The period cannot be negative.') elif len(times) and period <= np.max(times): raise ValueError('The period has to be greater than the maximum of ' 'the spike times') self.start = 0 self.stop = N if not sorted: # sort times and indices first by time, then by indices rec = np.rec.fromarrays([times, indices], names=['t', 'i']) rec.sort() times = np.ascontiguousarray(rec.t) indices = np.ascontiguousarray(rec.i) self.variables = Variables(self) # We store the indices and times also directly in the Python object, # this way we can use them for checks in `before_run` even in standalone # TODO: Remove this when the checks in `before_run` have been moved to the template self._spike_time = times self._neuron_index = indices # standard variables self.variables.add_constant('N', unit=Unit(1), value=N) self.variables.add_array('period', unit=second, size=1, constant=True, read_only=True, scalar=True) self.variables.add_arange('i', N) self.variables.add_dynamic_array('spike_number', values=np.arange(len(indices)), size=len(indices), unit=Unit(1), dtype=np.int32, read_only=True, constant=True, constant_size=True, unique=True) self.variables.add_dynamic_array('neuron_index', values=indices, size=len(indices), unit=Unit(1), dtype=np.int32, index='spike_number', read_only=True, constant=True, constant_size=True) self.variables.add_dynamic_array('spike_time', values=times, size=len(times), unit=second, index='spike_number', read_only=True, constant=True, constant_size=True) self.variables.add_array('_spikespace', size=N+1, unit=Unit(1), dtype=np.int32) self.variables.add_array('_lastindex', size=1, values=0, unit=Unit(1), dtype=np.int32, read_only=True, scalar=True) self.variables.create_clock_variables(self._clock) #: Remember the dt we used the last time when we checked the spike bins #: to not repeat the work for multiple runs with the same dt self._previous_dt = None #: "Dirty flag" that will be set when spikes are changed after the #: `before_run` check self._spikes_changed = True CodeRunner.__init__(self, self, code='', template='spikegenerator', clock=self._clock, when=when, order=order, name=None) # Activate name attribute access self._enable_group_attributes() self.variables['period'].set_value(period)
def __init__(self, synapses, code, prepost, objname=None): self.code = code if prepost == 'pre': self.source = synapses.source self.target = synapses.target self.synapse_indices = synapses.item_mapping.pre_synaptic elif prepost == 'post': self.source = synapses.target self.target = synapses.source self.synapse_indices = synapses.item_mapping.post_synaptic else: raise ValueError('prepost argument has to be either "pre" or ' '"post"') self.synapses = synapses if objname is None: objname = prepost + '*' GroupCodeRunner.__init__(self, synapses, 'synapses', code=code, when=(synapses.clock, 'synapses'), name=synapses.name + '_' + objname) self._delays = DynamicArray1D(synapses.N, dtype=np.float64) # Register the object with the `SynapticIndex` object so it gets # automatically resized synapses.item_mapping.register_variable(self._delays) self.queue = SpikeQueue() self.spiking_synapses = [] self.variables = { '_spiking_synapses': AttributeVariable(Unit(1), self, 'spiking_synapses', constant=False), '_source_offset': Variable(Unit(1), self.source.offset, constant=True), '_target_offset': Variable(Unit(1), self.target.offset, constant=True), 'delay': DynamicArrayVariable('delay', second, self._delays, group_name=self.name, constant=True) } # Re-extract the last part of the name from the full name self.objname = self.name[len(synapses.name) + 1:] #: The simulation dt (necessary for the delays) self.dt = self.synapses.clock.dt_ self.item_mapping = synapses.item_mapping self.indices = self.synapses.indices # Enable access to the delay attribute via the specifier Group.__init__(self)
def __init__(self, source, target=None, model=None, pre=None, post=None, connect=False, delay=None, namespace=None, dtype=None, codeobj_class=None, clock=None, method=None, name='synapses*'): self._N = 0 Group.__init__(self, when=clock, name=name) self.codeobj_class = codeobj_class self.source = weakref.proxy(source) if target is None: self.target = self.source else: self.target = weakref.proxy(target) ##### Prepare and validate equations if model is None: model = '' if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Check flags model.check_flags({DIFFERENTIAL_EQUATION: ['event-driven'], STATIC_EQUATION: ['summed'], PARAMETER: ['constant']}) # Separate the equations into event-driven and continuously updated # equations event_driven = [] continuous = [] for single_equation in model.itervalues(): if 'event-driven' in single_equation.flags: event_driven.append(single_equation) else: continuous.append(single_equation) # Add the lastupdate variable, used by event-driven equations continuous.append(SingleEquation(PARAMETER, 'lastupdate', second)) if len(event_driven): self.event_driven = Equations(event_driven) else: self.event_driven = None self.equations = Equations(continuous) # Setup the namespace self._given_namespace = namespace self.namespace = create_namespace(namespace) self._queues = {} self._delays = {} # Setup variables self._create_variables() #: Set of `Variable` objects that should be resized when the #: number of synapses changes self._registered_variables = set() for varname, var in self.variables.iteritems(): if isinstance(var, DynamicArrayVariable): # Register the array with the `SynapticItemMapping` object so # it gets automatically resized self.register_variable(var) #: List of names of all updaters, e.g. ['pre', 'post'] self._synaptic_updaters = [] #: List of all `SynapticPathway` objects self._pathways = [] for prepost, argument in zip(('pre', 'post'), (pre, post)): if not argument: continue if isinstance(argument, basestring): self._add_updater(argument, prepost) elif isinstance(argument, collections.Mapping): for key, value in argument.iteritems(): if not isinstance(key, basestring): err_msg = ('Keys for the "{}" argument' 'have to be strings, got ' '{} instead.').format(prepost, type(key)) raise TypeError(err_msg) self._add_updater(value, prepost, objname=key) # If we have a pathway called "pre" (the most common use case), provide # direct access to its delay via a delay attribute (instead of having # to use pre.delay) if 'pre' in self._synaptic_updaters: self.variables.add_reference('delay', self.pre.variables['delay']) if delay is not None: if isinstance(delay, Quantity): if not 'pre' in self._synaptic_updaters: raise ValueError(('Cannot set delay, no "pre" pathway exists.' 'Use a dictionary if you want to set the ' 'delay for a pathway with a different name.')) delay = {'pre': delay} if not isinstance(delay, collections.Mapping): raise TypeError('Delay argument has to be a quantity or a ' 'dictionary, is type %s instead.' % type(delay)) for pathway, pathway_delay in delay.iteritems(): if not pathway in self._synaptic_updaters: raise ValueError(('Cannot set the delay for pathway ' '"%s": unknown pathway.') % pathway) if not isinstance(pathway_delay, Quantity): raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a quantity, got %s instead.') % (pathway, type(pathway_delay))) if pathway_delay.size != 1: raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a scalar quantity, got a ' 'quantity with shape %s instead.') % str(pathway_delay.shape)) fail_for_dimension_mismatch(pathway_delay, second, ('Delay has to be ' 'specified in units ' 'of seconds')) updater = getattr(self, pathway) # For simplicity, store the delay as a one-element array # so that for example updater._delays[:] works. updater._delays.resize(1) updater._delays.set_value(float(pathway_delay)) updater._delays.scalar = True # Do not resize the scalar delay variable when adding synapses self.unregister_variable(updater._delays) #: Performs numerical integration step self.state_updater = StateUpdater(self, method) self.contained_objects.append(self.state_updater) #: "Summed variable" mechanism -- sum over all synapses of a #: pre-/postsynaptic target self.summed_updaters = {} # We want to raise an error if the same variable is updated twice # using this mechanism. This could happen if the Synapses object # connected a NeuronGroup to itself since then all variables are # accessible as var_pre and var_post. summed_targets = set() for single_equation in self.equations.itervalues(): if 'summed' in single_equation.flags: varname = single_equation.varname if not (varname.endswith('_pre') or varname.endswith('_post')): raise ValueError(('The summed variable "%s" does not end ' 'in "_pre" or "_post".') % varname) if not varname in self.variables: raise ValueError(('The summed variable "%s" does not refer' 'do any known variable in the ' 'target group.') % varname) if varname.endswith('_pre'): summed_target = self.source orig_varname = varname[:-4] else: summed_target = self.target orig_varname = varname[:-5] target_eq = getattr(summed_target, 'equations', {}).get(orig_varname, None) if target_eq is None or target_eq.type != PARAMETER: raise ValueError(('The summed variable "%s" needs a ' 'corresponding parameter "%s" in the ' 'target group.') % (varname, orig_varname)) fail_for_dimension_mismatch(self.variables['_summed_'+varname].unit, self.variables[varname].unit, ('Summed variables need to have ' 'the same units in Synapses ' 'and the target group')) if self.variables[varname] in summed_targets: raise ValueError(('The target variable "%s" is already ' 'updated by another summed ' 'variable') % orig_varname) summed_targets.add(self.variables[varname]) updater = SummedVariableUpdater(single_equation.expr, varname, self, summed_target) self.summed_updaters[varname] = updater self.contained_objects.append(updater) # Do an initial connect, if requested if not isinstance(connect, (bool, basestring)): raise TypeError(('"connect" keyword has to be a boolean value or a ' 'string, is type %s instead.' % type(connect))) self._initial_connect = connect if not connect is False: self.connect(connect, level=1) # Activate name attribute access self._enable_group_attributes()
def __init__(self, N, indices, times, dt=None, clock=None, period=1e100 * second, when='thresholds', order=0, sorted=False, name='spikegeneratorgroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) # Let other objects know that we emit spikes events self.events = {'spike': None} self.codeobj_class = codeobj_class times = Quantity(times) if N < 1 or int(N) != N: raise TypeError('N has to be an integer >=1.') N = int( N) # Make sure that it is an integer, values such as 10.0 would # otherwise make weave compilation fail if len(indices) != len(times): raise ValueError( ('Length of the indices and times array must ' 'match, but %d != %d') % (len(indices), len(times))) if period < 0 * second: raise ValueError('The period cannot be negative.') elif len(times) and period <= np.max(times): raise ValueError( 'The period has to be greater than the maximum of ' 'the spike times') if len(times) and np.min(times) < 0 * second: raise ValueError('Spike times cannot be negative') if len(indices) and (np.min(indices) < 0 or np.max(indices) >= N): raise ValueError('Indices have to lie in the interval [0, %d[' % N) self.start = 0 self.stop = N if not sorted: # sort times and indices first by time, then by indices rec = np.rec.fromarrays([times, indices], names=['t', 'i']) rec.sort() times = np.ascontiguousarray(rec.t) indices = np.ascontiguousarray(rec.i) self.variables = Variables(self) # We store the indices and times also directly in the Python object, # this way we can use them for checks in `before_run` even in standalone # TODO: Remove this when the checks in `before_run` have been moved to the template self._spike_time = times self._neuron_index = indices # standard variables self.variables.add_constant('N', unit=Unit(1), value=N) self.variables.add_array('period', unit=second, size=1, constant=True, read_only=True, scalar=True) self.variables.add_arange('i', N) self.variables.add_dynamic_array('spike_number', values=np.arange(len(indices)), size=len(indices), unit=Unit(1), dtype=np.int32, read_only=True, constant=True, index='spike_number', unique=True) self.variables.add_dynamic_array('neuron_index', values=indices, size=len(indices), unit=Unit(1), dtype=np.int32, index='spike_number', read_only=True, constant=True) self.variables.add_dynamic_array('spike_time', values=times, size=len(times), unit=second, index='spike_number', read_only=True, constant=True) self.variables.add_array('_spikespace', size=N + 1, unit=Unit(1), dtype=np.int32) self.variables.add_array('_lastindex', size=1, values=0, unit=Unit(1), dtype=np.int32, read_only=True, scalar=True) self.variables.create_clock_variables(self._clock) #: Remember the dt we used the last time when we checked the spike bins #: to not repeat the work for multiple runs with the same dt self._previous_dt = None #: "Dirty flag" that will be set when spikes are changed after the #: `before_run` check self._spikes_changed = True CodeRunner.__init__(self, self, code='', template='spikegenerator', clock=self._clock, when=when, order=order, name=None) # Activate name attribute access self._enable_group_attributes() self.variables['period'].set_value(period)
def __init__(self, N, indices, times, dt=None, clock=None, period=0*second, when='thresholds', order=0, sorted=False, name='spikegeneratorgroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) # We store the indices and times also directly in the Python object, # this way we can use them for checks in `before_run` even in standalone # TODO: Remove this when the checks in `before_run` have been moved to the template #: Array of spiking neuron indices. self._neuron_index = None #: Array of spiking neuron times. self._spike_time = None #: "Dirty flag" that will be set when spikes are changed after the #: `before_run` check self._spikes_changed = True # Let other objects know that we emit spikes events self.events = {'spike': None} self.codeobj_class = codeobj_class if N < 1 or int(N) != N: raise TypeError('N has to be an integer >=1.') N = int(N) # Make sure that it is an integer, values such as 10.0 would # otherwise make weave compilation fail self.start = 0 self.stop = N self.variables = Variables(self) self.variables.create_clock_variables(self._clock) indices, times = self._check_args(indices, times, period, N, sorted, self._clock.dt) self.variables.add_constant('N', value=N) self.variables.add_array('period', dimensions=second.dim, size=1, constant=True, read_only=True, scalar=True, dtype=self._clock.variables['t'].dtype) self.variables.add_arange('i', N) self.variables.add_dynamic_array('spike_number', values=np.arange(len(indices)), size=len(indices), dtype=np.int32, read_only=True, constant=True, index='spike_number', unique=True) self.variables.add_dynamic_array('neuron_index', values=indices, size=len(indices), dtype=np.int32, index='spike_number', read_only=True, constant=True) self.variables.add_dynamic_array('spike_time', values=times, size=len(times), dimensions=second.dim, index='spike_number', read_only=True, constant=True, dtype=self._clock.variables['t'].dtype) self.variables.add_dynamic_array('_timebins', size=len(times), index='spike_number', read_only=True, constant=True, dtype=np.int32) self.variables.add_array('_period_bins', size=1, constant=True, read_only=True, scalar=True, dtype=np.int32) self.variables.add_array('_spikespace', size=N+1, dtype=np.int32) self.variables.add_array('_lastindex', size=1, values=0, dtype=np.int32, read_only=True, scalar=True) #: Remember the dt we used the last time when we checked the spike bins #: to not repeat the work for multiple runs with the same dt self._previous_dt = None CodeRunner.__init__(self, self, code='', template='spikegenerator', clock=self._clock, when=when, order=order, name=None) # Activate name attribute access self._enable_group_attributes() self.variables['period'].set_value(period)
def __init__(self, N, indices, times, dt=None, clock=None, period=0 * second, when='thresholds', order=0, sorted=False, name='spikegeneratorgroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) # We store the indices and times also directly in the Python object, # this way we can use them for checks in `before_run` even in standalone # TODO: Remove this when the checks in `before_run` have been moved to the template #: Array of spiking neuron indices. self._neuron_index = None #: Array of spiking neuron times. self._spike_time = None #: "Dirty flag" that will be set when spikes are changed after the #: `before_run` check self._spikes_changed = True # Let other objects know that we emit spikes events self.events = {'spike': None} self.codeobj_class = codeobj_class if N < 1 or int(N) != N: raise TypeError('N has to be an integer >=1.') N = int( N) # Make sure that it is an integer, values such as 10.0 would # otherwise make weave compilation fail self.start = 0 self.stop = N self.variables = Variables(self) self.variables.create_clock_variables(self._clock) indices, times = self._check_args(indices, times, period, N, sorted, self._clock.dt) self.variables.add_constant('N', value=N) self.variables.add_array('period', dimensions=second.dim, size=1, constant=True, read_only=True, scalar=True, dtype=self._clock.variables['t'].dtype) self.variables.add_arange('i', N) self.variables.add_dynamic_array('spike_number', values=np.arange(len(indices)), size=len(indices), dtype=np.int32, read_only=True, constant=True, index='spike_number', unique=True) self.variables.add_dynamic_array('neuron_index', values=indices, size=len(indices), dtype=np.int32, index='spike_number', read_only=True, constant=True) self.variables.add_dynamic_array( 'spike_time', values=times, size=len(times), dimensions=second.dim, index='spike_number', read_only=True, constant=True, dtype=self._clock.variables['t'].dtype) self.variables.add_dynamic_array('_timebins', size=len(times), index='spike_number', read_only=True, constant=True, dtype=np.int32) self.variables.add_array('_period_bins', size=1, constant=True, read_only=True, scalar=True, dtype=np.int32) self.variables.add_array('_spikespace', size=N + 1, dtype=np.int32) self.variables.add_array('_lastindex', size=1, values=0, dtype=np.int32, read_only=True, scalar=True) #: Remember the dt we used the last time when we checked the spike bins #: to not repeat the work for multiple runs with the same dt self._previous_dt = None CodeRunner.__init__(self, self, code='', template='spikegenerator', clock=self._clock, when=when, order=order, name=None) # Activate name attribute access self._enable_group_attributes() self.variables['period'].set_value(period)
def __init__(self, source, target=None, model=None, pre=None, post=None, connect=False, delay=None, namespace=None, dtype=None, codeobj_class=None, clock=None, method=None, name='synapses*'): self._N = 0 Group.__init__(self, when=clock, name=name) self.codeobj_class = codeobj_class self.source = weakref.proxy(source) if target is None: self.target = self.source else: self.target = weakref.proxy(target) ##### Prepare and validate equations if model is None: model = '' if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Check flags model.check_flags({ DIFFERENTIAL_EQUATION: ['event-driven'], SUBEXPRESSION: ['summed', 'scalar'], PARAMETER: ['constant', 'scalar'] }) # Add the lastupdate variable, needed for event-driven updates if 'lastupdate' in model._equations: raise SyntaxError('lastupdate is a reserved name.') model._equations['lastupdate'] = SingleEquation( PARAMETER, 'lastupdate', second) self._create_variables(model) # Separate the equations into event-driven equations, # continuously updated equations and summed variable updates event_driven = [] continuous = [] summed_updates = [] for single_equation in model.itervalues(): if 'event-driven' in single_equation.flags: event_driven.append(single_equation) elif 'summed' in single_equation.flags: summed_updates.append(single_equation) else: continuous.append(single_equation) if len(event_driven): self.event_driven = Equations(event_driven) else: self.event_driven = None self.equations = Equations(continuous) if namespace is None: namespace = {} #: The group-specific namespace self.namespace = namespace #: Set of `Variable` objects that should be resized when the #: number of synapses changes self._registered_variables = set() for varname, var in self.variables.iteritems(): if isinstance(var, DynamicArrayVariable): # Register the array with the `SynapticItemMapping` object so # it gets automatically resized self.register_variable(var) if delay is None: delay = {} if isinstance(delay, Quantity): delay = {'pre': delay} elif not isinstance(delay, collections.Mapping): raise TypeError('Delay argument has to be a quantity or a ' 'dictionary, is type %s instead.' % type(delay)) #: List of names of all updaters, e.g. ['pre', 'post'] self._synaptic_updaters = [] #: List of all `SynapticPathway` objects self._pathways = [] for prepost, argument in zip(('pre', 'post'), (pre, post)): if not argument: continue if isinstance(argument, basestring): pathway_delay = delay.get(prepost, None) self._add_updater(argument, prepost, delay=pathway_delay) elif isinstance(argument, collections.Mapping): for key, value in argument.iteritems(): if not isinstance(key, basestring): err_msg = ('Keys for the "{}" argument' 'have to be strings, got ' '{} instead.').format(prepost, type(key)) raise TypeError(err_msg) pathway_delay = delay.get(key, None) self._add_updater(value, prepost, objname=key, delay=pathway_delay) # Check whether any delays were specified for pathways that don't exist for pathway in delay: if not pathway in self._synaptic_updaters: raise ValueError(('Cannot set the delay for pathway ' '"%s": unknown pathway.') % pathway) # If we have a pathway called "pre" (the most common use case), provide # direct access to its delay via a delay attribute (instead of having # to use pre.delay) if 'pre' in self._synaptic_updaters: self.variables.add_reference('delay', self.pre.variables['delay']) #: Performs numerical integration step self.state_updater = None # We only need a state update if we have differential equations if len(self.equations.diff_eq_names): self.state_updater = StateUpdater(self, method) self.contained_objects.append(self.state_updater) #: "Summed variable" mechanism -- sum over all synapses of a #: pre-/postsynaptic target self.summed_updaters = {} # We want to raise an error if the same variable is updated twice # using this mechanism. This could happen if the Synapses object # connected a NeuronGroup to itself since then all variables are # accessible as var_pre and var_post. summed_targets = set() for single_equation in summed_updates: varname = single_equation.varname if not (varname.endswith('_pre') or varname.endswith('_post')): raise ValueError(('The summed variable "%s" does not end ' 'in "_pre" or "_post".') % varname) if not varname in self.variables: raise ValueError(('The summed variable "%s" does not refer' 'do any known variable in the ' 'target group.') % varname) if varname.endswith('_pre'): summed_target = self.source orig_varname = varname[:-4] else: summed_target = self.target orig_varname = varname[:-5] target_eq = getattr(summed_target, 'equations', {}).get(orig_varname, None) if target_eq is None or target_eq.type != PARAMETER: raise ValueError(('The summed variable "%s" needs a ' 'corresponding parameter "%s" in the ' 'target group.') % (varname, orig_varname)) fail_for_dimension_mismatch( self.variables['_summed_' + varname].unit, self.variables[varname].unit, ('Summed variables need to have ' 'the same units in Synapses ' 'and the target group')) if self.variables[varname] in summed_targets: raise ValueError(('The target variable "%s" is already ' 'updated by another summed ' 'variable') % orig_varname) summed_targets.add(self.variables[varname]) updater = SummedVariableUpdater(single_equation.expr, varname, self, summed_target) self.summed_updaters[varname] = updater self.contained_objects.append(updater) # Do an initial connect, if requested if not isinstance(connect, (bool, basestring)): raise TypeError( ('"connect" keyword has to be a boolean value or a ' 'string, is type %s instead.' % type(connect))) self._initial_connect = connect if not connect is False: self.connect(connect, level=1) # Activate name attribute access self._enable_group_attributes()
def __init__(self, source, target=None, model=None, pre=None, post=None, connect=False, delay=None, namespace=None, dtype=None, codeobj_class=None, clock=None, method=None, name='synapses*'): BrianObject.__init__(self, when=clock, name=name) self.codeobj_class = codeobj_class self.source = weakref.proxy(source) if target is None: self.target = self.source else: self.target = weakref.proxy(target) ##### Prepare and validate equations if model is None: model = '' if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Check flags model.check_flags({ DIFFERENTIAL_EQUATION: ['event-driven', 'lumped'], STATIC_EQUATION: ['lumped'], PARAMETER: ['constant', 'lumped'] }) # Separate the equations into event-driven and continuously updated # equations event_driven = [] continuous = [] for single_equation in model.itervalues(): if 'event-driven' in single_equation.flags: if 'lumped' in single_equation.flags: raise ValueError( ('Event-driven variable %s cannot be ' 'a lumped variable.') % single_equation.varname) event_driven.append(single_equation) else: continuous.append(single_equation) # Add the lastupdate variable, used by event-driven equations continuous.append(SingleEquation(PARAMETER, 'lastupdate', second)) if len(event_driven): self.event_driven = Equations(event_driven) else: self.event_driven = None self.equations = Equations(continuous) ##### Setup the memory self.arrays = self._allocate_memory(dtype=dtype) # Setup the namespace self._given_namespace = namespace self.namespace = create_namespace(namespace) self._queues = {} self._delays = {} self.item_mapping = SynapticItemMapping(self) self.indices = { '_idx': self.item_mapping, '_presynaptic_idx': self.item_mapping.synaptic_pre, '_postsynaptic_idx': self.item_mapping.synaptic_post } # Allow S.i instead of S.indices.i, etc. self.i = self.item_mapping.i self.j = self.item_mapping.j self.k = self.item_mapping.k # Setup variables self.variables = self._create_variables() #: List of names of all updaters, e.g. ['pre', 'post'] self._updaters = [] for prepost, argument in zip(('pre', 'post'), (pre, post)): if not argument: continue if isinstance(argument, basestring): self._add_updater(argument, prepost) elif isinstance(argument, collections.Mapping): for key, value in argument.iteritems(): if not isinstance(key, basestring): err_msg = ('Keys for the "{}" argument' 'have to be strings, got ' '{} instead.').format(prepost, type(key)) raise TypeError(err_msg) self._add_updater(value, prepost, objname=key) # If we have a pathway called "pre" (the most common use case), provide # direct access to its delay via a delay attribute (instead of having # to use pre.delay) if 'pre' in self._updaters: self.variables['delay'] = self.pre.variables['delay'] if delay is not None: if isinstance(delay, Quantity): if not 'pre' in self._updaters: raise ValueError( ('Cannot set delay, no "pre" pathway exists.' 'Use a dictionary if you want to set the ' 'delay for a pathway with a different name.')) delay = {'pre': delay} if not isinstance(delay, collections.Mapping): raise TypeError('Delay argument has to be a quantity or a ' 'dictionary, is type %s instead.' % type(delay)) for pathway, pathway_delay in delay.iteritems(): if not pathway in self._updaters: raise ValueError(('Cannot set the delay for pathway ' '"%s": unknown pathway.') % pathway) if not isinstance(pathway_delay, Quantity): raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a quantity, got %s instead.') % (pathway, type(pathway_delay))) if pathway_delay.size != 1: raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a scalar quantity, got a ' 'quantity with shape %s instead.') % str(pathway_delay.shape)) fail_for_dimension_mismatch(pathway_delay, second, ('Delay has to be ' 'specified in units ' 'of seconds')) updater = getattr(self, pathway) self.item_mapping.unregister_variable(updater._delays) del updater._delays # For simplicity, store the delay as a one-element array # so that for example updater._delays[:] works. updater._delays = np.array([float(pathway_delay)]) variable = ArrayVariable('delay', second, updater._delays, group_name=self.name, scalar=True) updater.variables['delay'] = variable if pathway == 'pre': self.variables['delay'] = variable #: Performs numerical integration step self.state_updater = StateUpdater(self, method) self.contained_objects.append(self.state_updater) #: "Lumped variable" mechanism -- sum over all synapses of a #: postsynaptic target self.lumped_updaters = {} for single_equation in self.equations.itervalues(): if 'lumped' in single_equation.flags: varname = single_equation.varname # For a lumped variable, we need an equivalent parameter in the # target group if not varname in self.target.variables: raise ValueError( ('The lumped variable %s needs a variable ' 'of the same name in the target ' 'group ') % single_equation.varname) fail_for_dimension_mismatch(self.variables[varname].unit, self.target.variables[varname], ('Lumped variables need to have ' 'the same units in Synapses ' 'and the target group')) # TODO: Add some more stringent check about the type of # variable in the target group updater = LumpedUpdater(varname, self, self.target) self.lumped_updaters[varname] = updater self.contained_objects.append(updater) # Do an initial connect, if requested if not isinstance(connect, (bool, basestring)): raise TypeError( ('"connect" keyword has to be a boolean value or a ' 'string, is type %s instead.' % type(connect))) self._initial_connect = connect if not connect is False: self.connect(connect, level=1) # Activate name attribute access Group.__init__(self)
def __init__(self, N, equations, method=euler, threshold=None, reset=None, dtype=None, language=None, clock=None, name=None, level=0): BrianObject.__init__(self, when=clock, name=name) ##### VALIDATE ARGUMENTS AND STORE ATTRIBUTES self.method = method self.level = level = int(level) try: self.N = N = int(N) except ValueError: if isinstance(N, str): raise TypeError("First NeuronGroup argument should be size, not equations.") raise if N<1: raise ValueError("NeuronGroup size should be at least 1, was "+str(N)) # Validate equations if isinstance(equations, basestring): equations = Equations(equations, level=level+1) if not isinstance(equations, Equations): raise ValueError(('equations has to be a string or an Equations ' 'object, is "%s" instead.') % type(equations)) # add refractoriness equations = add_refractoriness(equations) self.equations = equations logger.debug("Creating NeuronGroup of size {self.N}, " "equations {self.equations}.".format(self=self)) # Check flags equations.check_flags({DIFFERENTIAL_EQUATION: ('active'), PARAMETER: ('constant')}) # Set dtypes and units self.prepare_dtypes(dtype=dtype) self.units = dict((var, equations.units[var]) for var in equations.equations.keys()) # Allocate memory (TODO: this should be refactored somewhere at some point) self.allocate_memory() #: The array of spikes from the most recent threshold operation self.spikes = array([], dtype=int) # Set these for documentation purposes #: Performs numerical integration step self.state_updater = None #: Performs thresholding step, sets the value of `spikes` self.thresholder = None #: Resets neurons which have spiked (`spikes`) self.resetter = None # Code generation (TODO: this should be refactored and modularised) # Temporary, set default language to Python if language is None: language = PythonLanguage() self.language = language self.create_state_updater() self.create_thresholder(threshold, level=level+1) self.create_resetter(reset, level=level+1) # Creation of contained_objects that do the work self.contained_objects.append(self.state_updater) if self.thresholder is not None: self.contained_objects.append(self.thresholder) if self.resetter is not None: self.contained_objects.append(self.resetter) # Activate name attribute access Group.__init__(self)
def __init__(self, variables, namespace=None): self.variables = variables self.namespace = namespace # We use a unique name to get repeated warnings Group.__init__(self, name='simplegroup_' + str(uuid.uuid4()).replace('-','_'))
def __init__(self, N, indices, times, dt=None, clock=None, period=1e100 * second, when='thresholds', order=0, sorted=False, name='spikegeneratorgroup*', codeobj_class=None): Group.__init__(self, dt=dt, clock=clock, when=when, order=order, name=name) self.codeobj_class = codeobj_class if N < 1 or int(N) != N: raise ValueError('N has to be an integer >=1.') if len(indices) != len(times): raise ValueError( ('Length of the indices and times array must ' 'match, but %d != %d') % (len(indices), len(times))) if period < 0 * second: raise ValueError('The period cannot be negative.') elif len(times) and period <= np.max(times): raise ValueError( 'The period has to be greater than the maximum of ' 'the spike times') self.start = 0 self.stop = N if not sorted: # sort times and indices first by time, then by indices rec = np.rec.fromarrays([times, indices], names=['t', 'i']) rec.sort() times = np.ascontiguousarray(rec.t) indices = np.ascontiguousarray(rec.i) self.variables = Variables(self) # We store the indices and times also directly in the Python object, # this way we can use them for checks in `before_run` even in standalone # TODO: Remove this when the checks in `before_run` have been moved to the template self._spike_time = times self._neuron_index = indices # standard variables self.variables.add_constant('N', unit=Unit(1), value=N) self.variables.add_constant('period', unit=second, value=period) self.variables.add_arange('i', N) self.variables.add_arange('spike_number', len(indices)) self.variables.add_array('neuron_index', values=indices, size=len(indices), unit=Unit(1), dtype=np.int32, index='spike_number', read_only=True) self.variables.add_array('spike_time', values=times, size=len(times), unit=second, index='spike_number', read_only=True) self.variables.add_array('_spikespace', size=N + 1, unit=Unit(1), dtype=np.int32) self.variables.add_array('_lastindex', size=1, values=np.zeros(1), unit=Unit(1), dtype=np.int32, read_only=True) self.variables.create_clock_variables(self._clock) #: Remember the dt we used the last time when we checked the spike bins #: to not repeat the work for multiple runs with the same dt self._previous_dt = None CodeRunner.__init__(self, self, code='', template='spikegenerator', clock=self._clock, when=when, order=order, name=None) # Activate name attribute access self._enable_group_attributes()
def __init__(self, source, target=None, model=None, pre=None, post=None, connect=False, delay=None, namespace=None, dtype=None, codeobj_class=None, clock=None, method=None, name='synapses*'): BrianObject.__init__(self, when=clock, name=name) self.codeobj_class = codeobj_class self.source = weakref.proxy(source) if target is None: self.target = self.source else: self.target = weakref.proxy(target) ##### Prepare and validate equations if model is None: model = '' if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Check flags model.check_flags({DIFFERENTIAL_EQUATION: ['event-driven', 'lumped'], STATIC_EQUATION: ['lumped'], PARAMETER: ['constant', 'lumped']}) # Separate the equations into event-driven and continuously updated # equations event_driven = [] continuous = [] for single_equation in model.itervalues(): if 'event-driven' in single_equation.flags: if 'lumped' in single_equation.flags: raise ValueError(('Event-driven variable %s cannot be ' 'a lumped variable.') % single_equation.varname) event_driven.append(single_equation) else: continuous.append(single_equation) # Add the lastupdate variable, used by event-driven equations continuous.append(SingleEquation(PARAMETER, 'lastupdate', second)) if len(event_driven): self.event_driven = Equations(event_driven) else: self.event_driven = None self.equations = Equations(continuous) ##### Setup the memory self.arrays = self._allocate_memory(dtype=dtype) # Setup the namespace self._given_namespace = namespace self.namespace = create_namespace(namespace) self._queues = {} self._delays = {} self.item_mapping = SynapticItemMapping(self) self.indices = {'_idx': self.item_mapping, '_presynaptic_idx': self.item_mapping.synaptic_pre, '_postsynaptic_idx': self.item_mapping.synaptic_post} # Allow S.i instead of S.indices.i, etc. self.i = self.item_mapping.i self.j = self.item_mapping.j self.k = self.item_mapping.k # Setup variables self.variables = self._create_variables() #: List of names of all updaters, e.g. ['pre', 'post'] self._updaters = [] for prepost, argument in zip(('pre', 'post'), (pre, post)): if not argument: continue if isinstance(argument, basestring): self._add_updater(argument, prepost) elif isinstance(argument, collections.Mapping): for key, value in argument.iteritems(): if not isinstance(key, basestring): err_msg = ('Keys for the "{}" argument' 'have to be strings, got ' '{} instead.').format(prepost, type(key)) raise TypeError(err_msg) self._add_updater(value, prepost, objname=key) # If we have a pathway called "pre" (the most common use case), provide # direct access to its delay via a delay attribute (instead of having # to use pre.delay) if 'pre' in self._updaters: self.variables['delay'] = self.pre.variables['delay'] if delay is not None: if isinstance(delay, Quantity): if not 'pre' in self._updaters: raise ValueError(('Cannot set delay, no "pre" pathway exists.' 'Use a dictionary if you want to set the ' 'delay for a pathway with a different name.')) delay = {'pre': delay} if not isinstance(delay, collections.Mapping): raise TypeError('Delay argument has to be a quantity or a ' 'dictionary, is type %s instead.' % type(delay)) for pathway, pathway_delay in delay.iteritems(): if not pathway in self._updaters: raise ValueError(('Cannot set the delay for pathway ' '"%s": unknown pathway.') % pathway) if not isinstance(pathway_delay, Quantity): raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a quantity, got %s instead.') % (pathway, type(pathway_delay))) if pathway_delay.size != 1: raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a scalar quantity, got a ' 'quantity with shape %s instead.') % str(pathway_delay.shape)) fail_for_dimension_mismatch(pathway_delay, second, ('Delay has to be ' 'specified in units ' 'of seconds')) updater = getattr(self, pathway) self.item_mapping.unregister_variable(updater._delays) del updater._delays # For simplicity, store the delay as a one-element array # so that for example updater._delays[:] works. updater._delays = np.array([float(pathway_delay)]) variable = ArrayVariable('delay', second, updater._delays, group_name=self.name, scalar=True) updater.variables['delay'] = variable if pathway == 'pre': self.variables['delay'] = variable #: Performs numerical integration step self.state_updater = StateUpdater(self, method) self.contained_objects.append(self.state_updater) #: "Lumped variable" mechanism -- sum over all synapses of a #: postsynaptic target self.lumped_updaters = {} for single_equation in self.equations.itervalues(): if 'lumped' in single_equation.flags: varname = single_equation.varname # For a lumped variable, we need an equivalent parameter in the # target group if not varname in self.target.variables: raise ValueError(('The lumped variable %s needs a variable ' 'of the same name in the target ' 'group ') % single_equation.varname) fail_for_dimension_mismatch(self.variables[varname].unit, self.target.variables[varname], ('Lumped variables need to have ' 'the same units in Synapses ' 'and the target group')) # TODO: Add some more stringent check about the type of # variable in the target group updater = LumpedUpdater(varname, self, self.target) self.lumped_updaters[varname] = updater self.contained_objects.append(updater) # Do an initial connect, if requested if not isinstance(connect, (bool, basestring)): raise TypeError(('"connect" keyword has to be a boolean value or a ' 'string, is type %s instead.' % type(connect))) self._initial_connect = connect if not connect is False: self.connect(connect, level=1) # Activate name attribute access Group.__init__(self)