def __init__(self, source, name='ratemonitor*', codeobj_class=None): #: The group we are recording from self.source = source scheduler = Scheduler(clock=source.clock, when='end') self.codeobj_class = codeobj_class CodeRunner.__init__(self, group=self, template='ratemonitor', when=scheduler, name=name) self.variables = Variables(self) self.variables.add_reference('_spikespace', source.variables['_spikespace']) self.variables.add_reference('_clock_t', source.variables['t']) self.variables.add_reference('_clock_dt', source.variables['dt']) self.variables.add_dynamic_array('rate', size=0, unit=hertz, constant_size=False) self.variables.add_dynamic_array('t', size=0, unit=second, constant_size=False) self.variables.add_reference('_num_source_neurons', source.variables['N']) self.variables.add_attribute_variable('N', unit=Unit(1), obj=self, attribute='_N', dtype=np.int32) self._N = 0 self._enable_group_attributes()
def __init__(self, source, name='ratemonitor*', codeobj_class=None): #: The group we are recording from self.source = source self.codeobj_class = codeobj_class CodeRunner.__init__(self, group=self, code='', template='ratemonitor', clock=source.clock, when='end', order=0, name=name) self.add_dependency(source) self.variables = Variables(self) # Handle subgroups correctly start = getattr(source, 'start', 0) stop = getattr(source, 'stop', len(source)) self.variables.add_constant('_source_start', Unit(1), start) self.variables.add_constant('_source_stop', Unit(1), stop) self.variables.add_reference('_spikespace', source) self.variables.add_dynamic_array('rate', size=0, unit=hertz, constant_size=False) self.variables.add_dynamic_array('t', size=0, unit=second, constant_size=False) self.variables.add_reference('_num_source_neurons', source, 'N') self.variables.add_array('N', unit=Unit(1), dtype=np.int32, size=1, scalar=True, read_only=True) self.variables.create_clock_variables(self._clock, prefix='_clock_') self._enable_group_attributes()
def __init__(self, source, record=True, when='end', order=0, name='spikemonitor*', codeobj_class=None): self.record = bool(record) #: The source we are recording from self.source =source self.codeobj_class = codeobj_class CodeRunner.__init__(self, group=self, code='', template='spikemonitor', name=name, clock=source.clock, when=when, order=order) self.add_dependency(source) # Handle subgroups correctly start = getattr(source, 'start', 0) stop = getattr(source, 'stop', len(source)) self.variables = Variables(self) self.variables.add_reference('_spikespace', source) self.variables.add_dynamic_array('i', size=0, unit=Unit(1), dtype=np.int32, constant_size=False) self.variables.add_dynamic_array('t', size=0, unit=second, constant_size=False) self.variables.add_arange('_source_i', size=len(source)) self.variables.add_array('_count', size=len(source), unit=Unit(1), dtype=np.int32, read_only=True, index='_source_i') self.variables.add_constant('_source_start', Unit(1), start) self.variables.add_constant('_source_stop', Unit(1), stop) self.variables.add_attribute_variable('N', unit=Unit(1), obj=self, attribute='_N', dtype=np.int32) self.variables.create_clock_variables(self._clock, prefix='_clock_') self._enable_group_attributes()
def __init__(self, group, method, clock, order=0): # group is the neuron (a group of compartments) self.method_choice = method self.group = weakref.proxy(group) CodeRunner.__init__(self, group, 'spatialstateupdate', code='''_gtot = gtot__private _I0 = I0__private''', clock=clock, when='groups', order=order, name=group.name + '_spatialstateupdater*', check_units=False) n = len(group) # total number of compartments segments = self.number_branches(group.morphology) self.variables = Variables(self, default_index='_segment_idx') self.variables.add_reference('N', group) self.variables.add_arange('_compartment_idx', size=n) self.variables.add_arange('_segment_idx', size=segments) self.variables.add_arange('_segment_root_idx', size=segments+1) self.variables.add_arange('_P_idx', size=(segments+1)**2) self.variables.add_array('_invr', unit=siemens, size=n, constant=True, index='_compartment_idx') self.variables.add_array('_P', unit=Unit(1), size=(segments+1)**2, constant=True, index='_P_idx') self.variables.add_array('_B', unit=Unit(1), size=segments+1, constant=True, index='_segment_root_idx') self.variables.add_array('_morph_i', unit=Unit(1), size=segments, dtype=np.int32, constant=True) self.variables.add_array('_morph_parent_i', unit=Unit(1), size=segments, dtype=np.int32, constant=True) self.variables.add_array('_starts', unit=Unit(1), size=segments, dtype=np.int32, constant=True) self.variables.add_array('_ends', unit=Unit(1), size=segments, dtype=np.int32, constant=True) self.variables.add_array('_invr0', unit=siemens, size=segments, constant=True) self.variables.add_array('_invrn', unit=siemens, size=segments, constant=True) self._enable_group_attributes() # The morphology is considered fixed (length etc. can still be changed, # though) # Traverse it once to get a flattened representation self._temp_morph_i = np.zeros(segments, dtype=np.int32) self._temp_morph_parent_i = np.zeros(segments, dtype=np.int32) self._temp_starts = np.zeros(segments, dtype=np.int32) self._temp_ends = np.zeros(segments, dtype=np.int32) self._pre_calc_iteration(self.group.morphology) self._morph_i = self._temp_morph_i self._morph_parent_i = self._temp_morph_parent_i self._starts = self._temp_starts self._ends = self._temp_ends self._prepare_codeobj = None
def __init__(self, group, method): self.method_choice = method CodeRunner.__init__(self, group, 'stateupdate', when=(group.clock, 'groups'), name=group.name + '_stateupdater', check_units=False) self.method = StateUpdateMethod.determine_stateupdater(self.group.equations, self.group.variables, method)
def __init__(self, target, target_var, N, rate, weight, when='synapses', order=0): if target_var not in target.variables: raise KeyError('%s is not a variable of %s' % (target_var, target.name)) if isinstance(weight, basestring): weight = '(%s)' % weight else: weight_unit = get_unit(weight) weight = repr(weight) target_unit = target.variables[target_var].unit # This will be checked automatically in the abstract code as well # but doing an explicit check here allows for a clearer error # message if not have_same_dimensions(weight_unit, target_unit): raise DimensionMismatchError(('The provided weight does not ' 'have the same unit as the ' 'target variable "%s"') % target_var, weight_unit.dim, target_unit.dim) binomial_sampling = BinomialFunction(N, rate*target.clock.dt, name='poissoninput_binomial*') code = '{targetvar} += {binomial}()*{weight}'.format(targetvar=target_var, binomial=binomial_sampling.name, weight=weight) self._stored_dt = target.dt_[:] # make a copy # FIXME: we need an explicit reference here for on-the-fly subgroups # For example: PoissonInput(group[:N], ...) self._group = target CodeRunner.__init__(self, group=target, template='stateupdate', code=code, user_code='', when=when, order=order, name='poissoninput*', clock=target.clock ) self.variables = Variables(self) self.variables._add_variable(binomial_sampling.name, binomial_sampling)
def __init__(self, group, method): # group is the neuron (a group of compartments) self.method_choice = method self._isprepared = False CodeRunner.__init__(self, group, 'spatialstateupdate', when=(group.clock, 'groups', 1), name=group.name + '_spatialstateupdater*', check_units=False) self.abstract_code = ''' _gtot = gtot__private _I0 = I0__private ''' N = len(self.group) self.ab_star = zeros((3, N)) self.ab_plus = zeros((3, N)) self.ab_minus = zeros((3, N)) self.b_plus = zeros(N) self.b_minus = zeros(N) self.v_star = zeros(N) self.u_plus = zeros(N) self.u_minus = zeros(N) self.variables = Variables(self) # These 5 variables are constant after prepare() self.variables.add_array('ab_star', Unit(1), 3 * N, values=self.ab_star.flatten(), dtype=self.ab_star.dtype) self.variables.add_array('ab_plus', Unit(1), 3 * N, values=self.ab_plus.flatten(), dtype=self.ab_plus.dtype) self.variables.add_array('ab_minus', Unit(1), 3 * N, values=self.ab_minus.flatten(), dtype=self.ab_minus.dtype) self.variables.add_array('b_plus', Unit(1), N, values=self.b_plus, dtype=self.b_plus.dtype) self.variables.add_array('b_minus', Unit(1), N, values=self.b_minus, dtype=self.b_minus.dtype) # These 3 variables change every time step self.variables.add_array('v_star', Unit(1), N, values=self.v_star, dtype=self.v_star.dtype) self.variables.add_array('u_plus', Unit(1), N, values=self.u_plus, dtype=self.u_plus.dtype) self.variables.add_array('u_minus', Unit(1), N, values=self.u_minus, dtype=self.u_minus.dtype)
def __init__(self, expression, target_varname, synapses, target): # Handling sumped variables using the standard mechanisms is not # possible, we therefore also directly give the names of the arrays # to the template. code = ''' _synaptic_var = {expression} '''.format(expression=expression, target_varname=target_varname) template_kwds = {'_target_var': synapses.variables[target_varname]} CodeRunner.__init__(self, group=synapses, template='summed_variable', code=code, needed_variables=[target_varname], # We want to update the sumned variable before # the target group gets updated when=(target.clock, 'groups', -1), name=synapses.name + '_summed_variable_' + target_varname, template_kwds=template_kwds)
def __init__(self, source, record=True, when=None, name='spikemonitor*', codeobj_class=None): self.record = bool(record) #: The source we are recording from self.source =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' self.codeobj_class = codeobj_class CodeRunner.__init__(self, group=self, template='spikemonitor', name=name, when=scheduler) self.add_dependency(source) # Handle subgroups correctly start = getattr(source, 'start', 0) stop = getattr(source, 'stop', len(source)) self.variables = Variables(self) self.variables.add_clock_variables(scheduler.clock, prefix='_clock_') self.variables.add_reference('_spikespace', source) self.variables.add_dynamic_array('i', size=0, unit=Unit(1), dtype=np.int32, constant_size=False) self.variables.add_dynamic_array('t', size=0, unit=second, constant_size=False) self.variables.add_array('_count', size=len(source), unit=Unit(1), dtype=np.int32) self.variables.add_constant('_source_start', Unit(1), start) self.variables.add_constant('_source_stop', Unit(1), stop) self.variables.add_attribute_variable('N', unit=Unit(1), obj=self, attribute='_N', dtype=np.int32) 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 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, source, variables, record=None, dt=None, clock=None, when='start', order=0, name='statemonitor*', codeobj_class=None): self.source = source # Make the monitor use the explicitly defined namespace of its source # group (if it exists) self.namespace = getattr(source, 'namespace', None) self.codeobj_class = codeobj_class # run by default on source clock at the end if dt is None and clock is None: clock = source.clock # variables should always be a list of strings if variables is True: variables = source.equations.names elif isinstance(variables, str): variables = [variables] #: The variables to record self.record_variables = variables # record should always be an array of ints self.record_all = False if hasattr(record, '_indices'): # The ._indices method always returns absolute indices # If the source is already a subgroup of another group, we therefore # have to shift the indices to become relative to the subgroup record = record._indices() - getattr(source, '_offset', 0) if record is True: self.record_all = True try: record = np.arange(len(source), dtype=np.int32) except NotImplementedError: # In standalone mode, this is not possible for synaptic # variables because the number of synapses is not defined yet raise NotImplementedError(('Cannot determine the actual ' 'indices to record for record=True. ' 'This can occur for example in ' 'standalone mode when trying to ' 'record a synaptic variable. ' 'Consider providing an explicit ' 'array of indices for the record ' 'argument.')) elif record is None or record is False: logger.warn(('The StateMonitor set up to record the variable(s) ' '{vars} of "{source}" is not recording any value. ' 'Did you forget to provide the record ' 'argument?').format(vars=', '.join('"%s"' % var for var in variables), source=self.source.name), once=True) record = np.array([], dtype=np.int32) elif isinstance(record, numbers.Number): record = np.array([record], dtype=np.int32) else: record = np.asarray(record, dtype=np.int32) #: The array of recorded indices self.record = record self.n_indices = len(record) # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = _source_%s' % (v, v) for v in variables] code = '\n'.join(code) CodeRunner.__init__(self, group=self, template='statemonitor', code=code, name=name, clock=clock, dt=dt, when=when, order=order, check_units=False) self.add_dependency(source) # Setup variables self.variables = Variables(self) self.variables.add_dynamic_array('t', size=0, unit=second, constant=False) self.variables.add_array('N', unit=Unit(1), dtype=np.int32, size=1, scalar=True, read_only=True) self.variables.add_array('_indices', size=len(self.record), unit=Unit(1), dtype=self.record.dtype, constant=True, read_only=True, values=self.record) self.variables.create_clock_variables(self._clock, prefix='_clock_') for varname in variables: var = source.variables[varname] if var.scalar and len(self.record) > 1: logger.warn(('Variable %s is a shared variable but it will be ' 'recorded once for every target.' % varname), once=True) index = source.variables.indices[varname] self.variables.add_reference('_source_%s' % varname, source, varname, index=index) if not index in ('_idx', '0') and index not in variables: self.variables.add_reference(index, source) self.variables.add_dynamic_array(varname, size=(0, len(self.record)), resize_along_first=True, unit=var.unit, dtype=var.dtype, constant=False) for varname in variables: var = self.source.variables[varname] self.variables.add_auxiliary_variable('_to_record_' + varname, unit=var.unit, dtype=var.dtype, scalar=var.scalar) self.recorded_variables = dict([(varname, self.variables[varname]) for varname in variables]) recorded_names = [varname for varname in variables] self.needed_variables = recorded_names self.template_kwds = {'_recorded_variables': self.recorded_variables} 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 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 self._period = period # 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=np.zeros(1), 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, group, method, clock, order=0): # group is the neuron (a group of compartments) self.method_choice = method self.group = weakref.proxy(group) compartments = group.flat_morphology.n sections = group.flat_morphology.sections CodeRunner.__init__(self, group, 'spatialstateupdate', code='''_gtot = gtot__private _I0 = I0__private''', clock=clock, when='groups', order=order, name=group.name + '_spatialstateupdater*', check_units=False, template_kwds={'number_sections': sections}) self.variables = Variables(self, default_index='_section_idx') self.variables.add_reference('N', group) # One value per compartment self.variables.add_arange('_compartment_idx', size=compartments) self.variables.add_array('_invr', unit=siemens, size=compartments, constant=True, index='_compartment_idx') # one value per section self.variables.add_arange('_section_idx', size=sections) self.variables.add_array('_P_parent', unit=Unit(1), size=sections, constant=True) # elements below diagonal self.variables.add_arrays(['_morph_idxchild', '_morph_parent_i', '_starts', '_ends'], unit=Unit(1), size=sections, dtype=np.int32, constant=True) self.variables.add_arrays(['_invr0', '_invrn'], unit=siemens, size=sections, constant=True) # one value per section + 1 value for the root self.variables.add_arange('_section_root_idx', size=sections+1) self.variables.add_array('_P_diag', unit=Unit(1), size=sections+1, constant=True, index='_section_root_idx') self.variables.add_array('_B', unit=Unit(1), size=sections+1, constant=True, index='_section_root_idx') self.variables.add_array('_morph_children_num', unit=Unit(1), size=sections+1, dtype=np.int32, constant=True, index='_section_root_idx') # 2D matrices of size (sections + 1) x max children per section self.variables.add_arange('_morph_children_idx', size=len(group.flat_morphology.morph_children)) self.variables.add_array('_P_children', unit=Unit(1), size=len(group.flat_morphology.morph_children), index='_morph_children_idx', constant=True) # elements above diagonal self.variables.add_array('_morph_children', unit=Unit(1), size=len(group.flat_morphology.morph_children), dtype=np.int32, constant=True, index='_morph_children_idx') self._enable_group_attributes() self._morph_parent_i = group.flat_morphology.morph_parent_i self._morph_children_num = group.flat_morphology.morph_children_num self._morph_children = group.flat_morphology.morph_children self._morph_idxchild = group.flat_morphology.morph_idxchild self._starts = group.flat_morphology.starts self._ends = group.flat_morphology.ends self._prepare_codeobj = None
def __init__(self, source, event, variables=None, record=True, when=None, order=None, name='eventmonitor*', codeobj_class=None): if not isinstance(source, SpikeSource): raise TypeError(('%s can only monitor groups producing spikes ' '(such as NeuronGroup), but the given argument ' 'is of type %s.') % (self.__class__.__name__, type(source))) #: The source we are recording from self.source = source #: Whether to record times and indices of events self.record = record if when is None: if order is not None: raise ValueError('Cannot specify order if when is not specified.') if hasattr(source, 'thresholder'): parent_obj = source.thresholder[event] else: parent_obj = source when = parent_obj.when order = parent_obj.order + 1 elif order is None: order = 0 #: The event that we are listening to self.event = event if variables is None: variables = {} elif isinstance(variables, basestring): variables = {variables} #: The additional variables that will be recorded self.record_variables = set(variables) for variable in variables: if variable not in source.variables: raise ValueError(("'%s' is not a variable of the recorded " "group" % variable)) if self.record: self.record_variables |= {'i', 't'} # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = _source_%s' % (v, v) for v in self.record_variables] code = '\n'.join(code) self.codeobj_class = codeobj_class # Since this now works for general events not only spikes, we have to # pass the information about which variable to use to the template, # it can not longer simply refer to "_spikespace" eventspace_name = '_{}space'.format(event) # Handle subgroups correctly start = getattr(source, 'start', 0) stop = getattr(source, 'stop', len(source)) Nameable.__init__(self, name=name) self.variables = Variables(self) self.variables.add_reference(eventspace_name, source) for variable in self.record_variables: source_var = source.variables[variable] self.variables.add_reference('_source_%s' % variable, source, variable) self.variables.add_auxiliary_variable('_to_record_%s' % variable, unit=source_var.unit, dtype=source_var.dtype) self.variables.add_dynamic_array(variable, size=0, unit=source_var.unit, dtype=source_var.dtype, read_only=True) self.variables.add_arange('_source_idx', size=len(source)) self.variables.add_array('count', size=len(source), unit=Unit(1), dtype=np.int32, read_only=True, index='_source_idx') self.variables.add_constant('_source_start', Unit(1), start) self.variables.add_constant('_source_stop', Unit(1), stop) self.variables.add_array('N', unit=Unit(1), size=1, dtype=np.int32, read_only=True, scalar=True) record_variables = {varname: self.variables[varname] for varname in self.record_variables} template_kwds = {'eventspace_variable': source.variables[eventspace_name], 'record_variables': record_variables, 'record': self.record} needed_variables = {eventspace_name} | self.record_variables CodeRunner.__init__(self, group=self, code=code, template='spikemonitor', name=None, # The name has already been initialized clock=source.clock, when=when, order=order, needed_variables=needed_variables, template_kwds=template_kwds) self.variables.create_clock_variables(self._clock, prefix='_clock_') self.add_dependency(source) self._enable_group_attributes()
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, event, variables=None, record=True, when=None, order=None, name='eventmonitor*', codeobj_class=None): if not isinstance(source, SpikeSource): raise TypeError(('%s can only monitor groups producing spikes ' '(such as NeuronGroup), but the given argument ' 'is of type %s.') % (self.__class__.__name__, type(source))) #: The source we are recording from self.source = source #: Whether to record times and indices of events self.record = record #: The array of event counts (length = size of target group) self.count = None del self.count # this is handled by the Variable mechanism if when is None: if order is not None: raise ValueError('Cannot specify order if when is not specified.') if hasattr(source, 'thresholder'): parent_obj = source.thresholder[event] else: parent_obj = source when = parent_obj.when order = parent_obj.order + 1 elif order is None: order = 0 #: The event that we are listening to self.event = event if variables is None: variables = {} elif isinstance(variables, basestring): variables = {variables} #: The additional variables that will be recorded self.record_variables = set(variables) for variable in variables: if variable not in source.variables: raise ValueError(("'%s' is not a variable of the recorded " "group" % variable)) if self.record: self.record_variables |= {'i', 't'} # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = _source_%s' % (v, v) for v in self.record_variables] code = '\n'.join(code) self.codeobj_class = codeobj_class # Since this now works for general events not only spikes, we have to # pass the information about which variable to use to the template, # it can not longer simply refer to "_spikespace" eventspace_name = '_{}space'.format(event) # Handle subgroups correctly start = getattr(source, 'start', 0) stop = getattr(source, 'stop', len(source)) source_N = getattr(source, '_source_N', len(source)) Nameable.__init__(self, name=name) self.variables = Variables(self) self.variables.add_reference(eventspace_name, source) for variable in self.record_variables: source_var = source.variables[variable] self.variables.add_reference('_source_%s' % variable, source, variable) self.variables.add_auxiliary_variable('_to_record_%s' % variable, dimensions=source_var.dim, dtype=source_var.dtype) self.variables.add_dynamic_array(variable, size=0, dimensions=source_var.dim, dtype=source_var.dtype, read_only=True) self.variables.add_arange('_source_idx', size=len(source)) self.variables.add_array('count', size=len(source), dtype=np.int32, read_only=True, index='_source_idx') self.variables.add_constant('_source_start', start) self.variables.add_constant('_source_stop', stop) self.variables.add_constant('_source_N', source_N) self.variables.add_array('N', size=1, dtype=np.int32, read_only=True, scalar=True) record_variables = {varname: self.variables[varname] for varname in self.record_variables} template_kwds = {'eventspace_variable': source.variables[eventspace_name], 'record_variables': record_variables, 'record': self.record} needed_variables = {eventspace_name} | self.record_variables CodeRunner.__init__(self, group=self, code=code, template='spikemonitor', name=None, # The name has already been initialized clock=source.clock, when=when, order=order, needed_variables=needed_variables, template_kwds=template_kwds) self.variables.create_clock_variables(self._clock, prefix='_clock_') self.add_dependency(source) self.written_readonly_vars = {self.variables[varname] for varname in self.record_variables} self._enable_group_attributes()
def __init__( self, source, event, variables=None, record=True, when=None, order=None, name="eventmonitor*", codeobj_class=None, ): #: The source we are recording from self.source = source #: Whether to record times and indices of events self.record = record if when is None: if order is not None: raise ValueError("Cannot specify order if when is not specified.") if hasattr(source, "thresholder"): parent_obj = source.thresholder[event] else: parent_obj = source when = parent_obj.when order = parent_obj.order + 1 elif order is None: order = 0 #: The event that we are listening to self.event = event if variables is None: variables = {} elif isinstance(variables, basestring): variables = {variables} #: The additional variables that will be recorded self.record_variables = set(variables) for variable in variables: if variable not in source.variables: raise ValueError(("'%s' is not a variable of the recorded " "group" % variable)) if self.record: self.record_variables |= {"i", "t"} # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ["_to_record_%s = _source_%s" % (v, v) for v in self.record_variables] code = "\n".join(code) self.codeobj_class = codeobj_class # Since this now works for general events not only spikes, we have to # pass the information about which variable to use to the template, # it can not longer simply refer to "_spikespace" eventspace_name = "_{}space".format(event) # Handle subgroups correctly start = getattr(source, "start", 0) stop = getattr(source, "stop", len(source)) Nameable.__init__(self, name=name) self.variables = Variables(self) self.variables.add_reference(eventspace_name, source) for variable in self.record_variables: source_var = source.variables[variable] self.variables.add_reference("_source_%s" % variable, source, variable) self.variables.add_auxiliary_variable( "_to_record_%s" % variable, unit=source_var.unit, dtype=source_var.dtype ) self.variables.add_dynamic_array( variable, size=0, unit=source_var.unit, dtype=source_var.dtype, constant_size=False ) self.variables.add_arange("_source_idx", size=len(source)) self.variables.add_array( "count", size=len(source), unit=Unit(1), dtype=np.int32, read_only=True, index="_source_idx" ) self.variables.add_constant("_source_start", Unit(1), start) self.variables.add_constant("_source_stop", Unit(1), stop) self.variables.add_array("N", unit=Unit(1), size=1, dtype=np.int32, read_only=True, scalar=True) record_variables = {varname: self.variables[varname] for varname in self.record_variables} template_kwds = { "eventspace_variable": source.variables[eventspace_name], "record_variables": record_variables, "record": self.record, } needed_variables = {eventspace_name} | self.record_variables CodeRunner.__init__( self, group=self, code=code, template="spikemonitor", name=None, # The name has already been initialized clock=source.clock, when=when, order=order, needed_variables=needed_variables, template_kwds=template_kwds, ) self.variables.create_clock_variables(self._clock, prefix="_clock_") self.add_dependency(source) self._enable_group_attributes()
def __init__(self, source, variables, record=None, when=None, name='statemonitor*', codeobj_class=None): self.source = weakref.proxy(source) self.codeobj_class = codeobj_class # 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' # variables should always be a list of strings if variables is True: variables = source.equations.names elif isinstance(variables, str): variables = [variables] #: The variables to record self.record_variables = variables # record should always be an array of ints self.record_all = False if record is True: self.record_all = True record = np.arange(len(source), dtype=np.int32) elif record is None or record is False: record = np.array([], dtype=np.int32) elif isinstance(record, int): record = np.array([record], dtype=np.int32) else: record = np.asarray(record, dtype=np.int32) #: The array of recorded indices self.indices = record self.n_indices = len(record) # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = %s' % (v, v) for v in self.record_variables] code = '\n'.join(code) CodeRunner.__init__(self, group=self, template='statemonitor', code=code, name=name, when=scheduler, check_units=False) # Setup variables self.variables = Variables(self) self.variables.add_dynamic_array('t', size=0, unit=second, constant=False, constant_size=False) if scheduler.clock is source.clock: self.variables.add_reference('_clock_t', source.variables['t']) else: self.variables.add_attribute_variable('_clock_t', unit=second, obj=scheduler.clock, attribute='t_') self.variables.add_attribute_variable('N', unit=Unit(1), dtype=np.int32, obj=self, attribute='_N') self.variables.add_array('_indices', size=len(self.indices), unit=Unit(1), dtype=self.indices.dtype, constant=True, read_only=True) self.variables['_indices'].set_value(self.indices) for varname in variables: var = source.variables[varname] if var.scalar and len(self.indices) > 1: logger.warn(('Variable %s is a scalar variable but it will be ' 'recorded once for every target.' % varname), once=True) index = source.variables.indices[varname] self.variables.add_reference(varname, var, index=index) if not index in ('_idx', '0') and index not in variables: self.variables.add_reference(index, source.variables[index]) # For subexpressions, we also need all referred variables (if they # are not already present, e.g. the t as _clock_t if isinstance(var, Subexpression): for subexpr_varname in var.identifiers: if subexpr_varname in source.variables: source_var = source.variables[subexpr_varname] index = source.variables.indices[subexpr_varname] if index != '_idx' and index not in variables: self.variables.add_reference(index, source.variables[index]) if not source_var in self.variables.values(): source_index = source.variables.indices[subexpr_varname] # `translate_subexpression` will later replace # the name used in the original subexpression with # _source_varname self.variables.add_reference('_source_'+subexpr_varname, source_var, index=source_index) self.variables.add_dynamic_array('_recorded_' + varname, size=(0, len(self.indices)), unit=var.unit, dtype=var.dtype, constant=False, constant_size=False) for varname in self.record_variables: var = self.source.variables[varname] self.variables.add_auxiliary_variable('_to_record_' + varname, unit=var.unit, dtype=var.dtype, scalar=var.scalar) self.recorded_variables = dict([(varname, self.variables['_recorded_'+varname]) for varname in self.record_variables]) recorded_names = ['_recorded_'+varname for varname in self.record_variables] self.needed_variables = recorded_names self.template_kwds = template_kwds={'_recorded_variables': self.recorded_variables} self._N = 0 self._enable_group_attributes()
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) 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, group, method, clock, order=0): # group is the neuron (a group of compartments) self.method_choice = method self.group = weakref.proxy(group) compartments = group.flat_morphology.n sections = group.flat_morphology.sections CodeRunner.__init__(self, group, 'spatialstateupdate', code='''_gtot = gtot__private _I0 = I0__private''', clock=clock, when='groups', order=order, name=group.name + '_spatialstateupdater*', check_units=False, template_kwds={'number_sections': sections}) self.variables = Variables(self, default_index='_section_idx') self.variables.add_reference('N', group) # One value per compartment self.variables.add_arange('_compartment_idx', size=compartments) self.variables.add_array('_invr', dimensions=siemens.dim, size=compartments, constant=True, index='_compartment_idx') # one value per section self.variables.add_arange('_section_idx', size=sections) self.variables.add_array('_P_parent', size=sections, constant=True) # elements below diagonal self.variables.add_arrays( ['_morph_idxchild', '_morph_parent_i', '_starts', '_ends'], size=sections, dtype=np.int32, constant=True) self.variables.add_arrays(['_invr0', '_invrn'], dimensions=siemens.dim, size=sections, constant=True) # one value per section + 1 value for the root self.variables.add_arange('_section_root_idx', size=sections + 1) self.variables.add_array('_P_diag', size=sections + 1, constant=True, index='_section_root_idx') self.variables.add_array('_B', size=sections + 1, constant=True, index='_section_root_idx') self.variables.add_array('_morph_children_num', size=sections + 1, dtype=np.int32, constant=True, index='_section_root_idx') # 2D matrices of size (sections + 1) x max children per section self.variables.add_arange('_morph_children_idx', size=len( group.flat_morphology.morph_children)) self.variables.add_array('_P_children', size=len( group.flat_morphology.morph_children), index='_morph_children_idx', constant=True) # elements above diagonal self.variables.add_array('_morph_children', size=len( group.flat_morphology.morph_children), dtype=np.int32, constant=True, index='_morph_children_idx') self._enable_group_attributes() self._morph_parent_i = group.flat_morphology.morph_parent_i self._morph_children_num = group.flat_morphology.morph_children_num self._morph_children = group.flat_morphology.morph_children self._morph_idxchild = group.flat_morphology.morph_idxchild self._starts = group.flat_morphology.starts self._ends = group.flat_morphology.ends self._prepare_codeobj = None
def __init__(self, synapses, code, prepost, objname=None, delay=None): self.code = code self.prepost = prepost if prepost == 'pre': self.source = synapses.source self.target = synapses.target self.synapse_sources = synapses.variables['_synaptic_pre'] elif prepost == 'post': self.source = synapses.target self.target = synapses.source self.synapse_sources = synapses.variables['_synaptic_post'] else: raise ValueError('prepost argument has to be either "pre" or ' '"post"') self.synapses = synapses if objname is None: objname = prepost + '*' CodeRunner.__init__(self, synapses, 'synapses', code=code, when=(synapses.clock, 'synapses'), name=synapses.name + '_' + objname, template_kwds={'pathway': self}) self._pushspikes_codeobj = None self.spikes_start = self.source.start self.spikes_stop = self.source.stop self.spiking_synapses = [] self.variables = Variables(self) self.variables.add_attribute_variable('_spiking_synapses', unit=Unit(1), obj=self, attribute='spiking_synapses', constant=False, scalar=False) self.variables.add_reference('_spikespace', self.source.variables['_spikespace']) self.variables.add_reference('N', synapses.variables['N']) if delay is None: # variable delays self.variables.add_dynamic_array('delay', unit=second, size=synapses._N, constant=True, constant_size=True) # Register the object with the `SynapticIndex` object so it gets # automatically resized synapses.register_variable(self.variables['delay']) else: if not isinstance(delay, Quantity): raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a quantity, got %s instead.') % (objname, type(delay))) if delay.size != 1: raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a scalar quantity, got a ' 'quantity with shape %s instead.') % str(delay.shape)) fail_for_dimension_mismatch(delay, second, ('Delay has to be ' 'specified in units ' 'of seconds')) self.variables.add_array('delay', unit=second, size=1, constant=True, scalar=True) self.variables['delay'].set_value(delay) self._delays = self.variables['delay'] # 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_ #: The `SpikeQueue` self.queue = None #: The `CodeObject` initalising the `SpikeQueue` at the begin of a run self._initialise_queue_codeobj = None self.namespace = synapses.namespace # Enable access to the delay attribute via the specifier self._enable_group_attributes()
def __init__(self, group, method, clock, order=0): # group is the neuron (a group of compartments) self.method_choice = method self.group = weakref.proxy(group) compartments = len(group) # total number of compartments branches = self.number_branches(group.morphology) CodeRunner.__init__(self, group, 'spatialstateupdate', code='''_gtot = gtot__private _I0 = I0__private''', clock=clock, when='groups', order=order, name=group.name + '_spatialstateupdater*', check_units=False, template_kwds={'number_branches': branches}) # The morphology is considered fixed (length etc. can still be changed, # though) # Traverse it once to get a flattened representation self._temp_morph_i = np.zeros(branches, dtype=np.int32) self._temp_morph_parent_i = np.zeros(branches, dtype=np.int32) # for the following: a smaller array of size no_segments x max_no_children would suffice... self._temp_morph_children = np.zeros((branches+1, branches), dtype=np.int32) # children count per branch: determines the no of actually used elements of the array above self._temp_morph_children_num = np.zeros(branches+1, dtype=np.int32) # each branch is child of exactly one parent (and we say the first branch i=1 is child of branch i=0) # here we store the indices j-1->k of morph_children_i[i,k] = j self._temp_morph_idxchild = np.zeros(branches, dtype=np.int32) self._temp_starts = np.zeros(branches, dtype=np.int32) self._temp_ends = np.zeros(branches, dtype=np.int32) self._pre_calc_iteration(self.group.morphology) # flattened and reduce children indices max_children = max(self._temp_morph_children_num) self._temp_morph_children = self._temp_morph_children[:,:max_children].reshape(-1) self.variables = Variables(self, default_index='_branch_idx') self.variables.add_reference('N', group) # One value per compartment self.variables.add_arange('_compartment_idx', size=compartments) self.variables.add_array('_invr', unit=siemens, size=compartments, constant=True, index='_compartment_idx') # one value per branch self.variables.add_arange('_branch_idx', size=branches) self.variables.add_array('_P_parent', unit=Unit(1), size=branches, constant=True) # elements below diagonal self.variables.add_array('_morph_idxchild', unit=Unit(1), size=branches, dtype=np.int32, constant=True) self.variables.add_arrays(['_morph_i', '_morph_parent_i', '_starts', '_ends'], unit=Unit(1), size=branches, dtype=np.int32, constant=True) self.variables.add_arrays(['_invr0', '_invrn'], unit=siemens, size=branches, constant=True) # one value per branch + 1 value for the root self.variables.add_arange('_branch_root_idx', size=branches+1) self.variables.add_array('_P_diag', unit=Unit(1), size=branches+1, constant=True, index='_branch_root_idx') self.variables.add_array('_B', unit=Unit(1), size=branches+1, constant=True, index='_branch_root_idx') self.variables.add_arange('_morph_children_num_idx', size=branches+1) self.variables.add_array('_morph_children_num', unit=Unit(1), size=branches+1, dtype=np.int32, constant=True, index='_morph_children_num_idx') # 2D matrices of size (branches + 1) x max children per branch # Note that this data structure wastes space if the number of children # per branch is very different. In practice, however, this should not # matter much, since branches will normally have 0, 1, or 2 children # (e.g. SWC files on neuromporh are strictly binary trees) self.variables.add_array('_P_children', unit=Unit(1), size=(branches+1)*max_children, constant=True) # elements above diagonal self.variables.add_arange('_morph_children_idx', size=(branches+1)*max_children) self.variables.add_array('_morph_children', unit=Unit(1), size=(branches+1)*max_children, dtype=np.int32, constant=True, index='_morph_children_idx') self._enable_group_attributes() self._morph_i = self._temp_morph_i self._morph_parent_i = self._temp_morph_parent_i self._morph_children_num = self._temp_morph_children_num self._morph_children = self._temp_morph_children self._morph_idxchild = self._temp_morph_idxchild self._starts = self._temp_starts self._ends = self._temp_ends self._prepare_codeobj = None
def __init__(self, source, variables, record, dt=None, clock=None, when='start', order=0, name='statemonitor*', codeobj_class=None): self.source = source # Make the monitor use the explicitly defined namespace of its source # group (if it exists) self.namespace = getattr(source, 'namespace', None) self.codeobj_class = codeobj_class # run by default on source clock at the end if dt is None and clock is None: clock = source.clock # variables should always be a list of strings if variables is True: variables = source.equations.names elif isinstance(variables, str): variables = [variables] #: The variables to record self.record_variables = variables # record should always be an array of ints self.record_all = False if hasattr(record, '_indices'): # The ._indices method always returns absolute indices # If the source is already a subgroup of another group, we therefore # have to shift the indices to become relative to the subgroup record = record._indices() - getattr(source, '_offset', 0) if record is True: self.record_all = True try: record = np.arange(len(source), dtype=np.int32) except NotImplementedError: # In standalone mode, this is not possible for synaptic # variables because the number of synapses is not defined yet raise NotImplementedError( ('Cannot determine the actual ' 'indices to record for record=True. ' 'This can occur for example in ' 'standalone mode when trying to ' 'record a synaptic variable. ' 'Consider providing an explicit ' 'array of indices for the record ' 'argument.')) elif record is False: record = np.array([], dtype=np.int32) elif isinstance(record, numbers.Number): record = np.array([record], dtype=np.int32) else: record = np.asarray(record, dtype=np.int32) #: The array of recorded indices self.record = record self.n_indices = len(record) # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = _source_%s' % (v, v) for v in variables] code = '\n'.join(code) CodeRunner.__init__(self, group=self, template='statemonitor', code=code, name=name, clock=clock, dt=dt, when=when, order=order, check_units=False) self.add_dependency(source) # Setup variables self.variables = Variables(self) self.variables.add_dynamic_array( 't', size=0, dimensions=second.dim, constant=False, dtype=self._clock.variables['t'].dtype) self.variables.add_array('N', dtype=np.int32, size=1, scalar=True, read_only=True) self.variables.add_array('_indices', size=len(self.record), dtype=self.record.dtype, constant=True, read_only=True, values=self.record) self.variables.create_clock_variables(self._clock, prefix='_clock_') for varname in variables: var = source.variables[varname] if var.scalar and len(self.record) > 1: logger.warn(('Variable %s is a shared variable but it will be ' 'recorded once for every target.' % varname), once=True) index = source.variables.indices[varname] self.variables.add_reference('_source_%s' % varname, source, varname, index=index) if not index in ('_idx', '0') and index not in variables: self.variables.add_reference(index, source) self.variables.add_dynamic_array(varname, size=(0, len(self.record)), resize_along_first=True, dimensions=var.dim, dtype=var.dtype, constant=False, read_only=True) for varname in variables: var = self.source.variables[varname] self.variables.add_auxiliary_variable('_to_record_' + varname, dimensions=var.dim, dtype=var.dtype, scalar=var.scalar) self.recorded_variables = dict([(varname, self.variables[varname]) for varname in variables]) recorded_names = [varname for varname in variables] self.needed_variables = recorded_names self.template_kwds = {'_recorded_variables': self.recorded_variables} self.written_readonly_vars = { self.variables[varname] for varname in self.record_variables } self._enable_group_attributes()
def __init__(self, source, variables, record=None, dt=None, clock=None, when='end', order=0, name='statemonitor*', codeobj_class=None): self.source = source # Make the monitor use the explicitly defined namespace of its source # group (if it exists) self.namespace = getattr(source, 'namespace', None) self.codeobj_class = codeobj_class # run by default on source clock at the end if dt is None and clock is None: clock = source.clock # variables should always be a list of strings if variables is True: variables = source.equations.names elif isinstance(variables, str): variables = [variables] #: The variables to record self.record_variables = variables # record should always be an array of ints self.record_all = False if hasattr(record, '_indices'): # The ._indices method always returns absolute indices # If the source is already a subgroup of another group, we therefore # have to shift the indices to become relative to the subgroup record = record._indices() - getattr(source, '_offset', 0) if record is True: self.record_all = True record = np.arange(len(source), dtype=np.int32) elif record is None or record is False: record = np.array([], dtype=np.int32) elif isinstance(record, numbers.Number): record = np.array([record], dtype=np.int32) else: record = np.asarray(record, dtype=np.int32) #: The array of recorded indices self.record = record self.n_indices = len(record) # Some dummy code so that code generation takes care of the indexing # and subexpressions code = ['_to_record_%s = %s' % (v, v) for v in self.record_variables] code = '\n'.join(code) CodeRunner.__init__(self, group=self, template='statemonitor', code=code, name=name, clock=clock, dt=dt, when=when, order=order, check_units=False) self.add_dependency(source) # Setup variables self.variables = Variables(self) self.variables.add_dynamic_array('t', size=0, unit=second, constant=False, constant_size=False) self.variables.add_attribute_variable('N', unit=Unit(1), dtype=np.int32, obj=self, attribute='_N') self.variables.add_array('_indices', size=len(self.record), unit=Unit(1), dtype=self.record.dtype, constant=True, read_only=True, values=self.record) self.variables.create_clock_variables(self._clock, prefix='_clock_') for varname in variables: var = source.variables[varname] if var.scalar and len(self.record) > 1: logger.warn(('Variable %s is a shared variable but it will be ' 'recorded once for every target.' % varname), once=True) index = source.variables.indices[varname] self.variables.add_reference(varname, source, varname, index=index) if not index in ('_idx', '0') and index not in variables: self.variables.add_reference(index, source) self.variables.add_dynamic_array('_recorded_' + varname, size=(0, len(self.record)), unit=var.unit, dtype=var.dtype, constant=False, constant_size=False) for varname in self.record_variables: var = self.source.variables[varname] self.variables.add_auxiliary_variable('_to_record_' + varname, unit=var.unit, dtype=var.dtype, scalar=var.scalar) self.recorded_variables = dict([(varname, self.variables['_recorded_'+varname]) for varname in self.record_variables]) recorded_names = ['_recorded_'+varname for varname in self.record_variables] self.needed_variables = recorded_names self.template_kwds = template_kwds={'_recorded_variables': self.recorded_variables} self._enable_group_attributes()