def before_run(self, run_namespace): # Do some checks on the period vs. dt dt = self.dt_[:] # make a copy period = self.period_ if period < np.inf: if period < dt: raise ValueError('The period of %s is %s, which is smaller ' 'than its dt of %s.' % (self.name, self.period, dt)) if (abs(int(period / dt) * dt - period) > period * np.finfo(dt.dtype).eps): raise NotImplementedError('The period of %s is %s, which is ' 'not an integer multiple of its dt ' 'of %s.' % (self.name, self.period, dt)) if self._spikes_changed: current_t = self.variables['t'].get_value().item() timesteps = timestep(self._spike_time, dt) current_step = timestep(current_t, dt) in_the_past = np.nonzero(timesteps < current_step)[0] if len(in_the_past): logger.warn('The SpikeGeneratorGroup contains spike times ' 'earlier than the start time of the current run ' '(t = {}), these spikes will be ' 'ignored.'.format(str(current_t * second)), name_suffix='ignored_spikes') self.variables['_lastindex'].set_value(in_the_past[-1] + 1) else: self.variables['_lastindex'].set_value(0) # Check that we don't have more than one spike per neuron in a time bin if self._previous_dt is None or dt != self._previous_dt or self._spikes_changed: # We shift all the spikes by a tiny amount to make sure that spikes # at exact multiples of dt do not end up in the previous time bin # This shift has to be quite significant relative to machine # epsilon, we use 1e-3 of the dt here shift = 1e-3 * dt timebins = np.asarray(np.asarray(self._spike_time + shift) / dt, dtype=np.int32) # time is already in sorted order, so it's enough to check if the condition # that timebins[i]==timebins[i+1] and self._neuron_index[i]==self._neuron_index[i+1] # is ever both true if (np.logical_and( np.diff(timebins) == 0, np.diff(self._neuron_index) == 0)).any(): raise ValueError('Using a dt of %s, some neurons of ' 'SpikeGeneratorGroup "%s" spike more than ' 'once during a time step.' % (str(self.dt), self.name)) self._previous_dt = dt self._spikes_changed = False super(SpikeGeneratorGroup, self).before_run(run_namespace=run_namespace)
def test_timestep_function(): dt = defaultclock.dt_ # Check that multiples of dt end up in the correct time step t = np.arange(100000) * dt assert_equal(timestep(t, dt), np.arange(100000)) # Scalar values should stay scalar ts = timestep(0.0005, 0.0001) assert np.isscalar(ts) and ts == 5 # Length-1 arrays should stay arrays ts = timestep(np.array([0.0005]), 0.0001) assert ts.shape == (1, ) and ts == 5
def test_timestep_function(): dt = defaultclock.dt_ # Check that multiples of dt end up in the correct time step t = np.arange(100000)*dt assert_equal(timestep(t, dt), np.arange(100000)) # Scalar values should stay scalar ts = timestep(0.0005, 0.0001) assert np.isscalar(ts) and ts == 5 # Length-1 arrays should stay arrays ts = timestep(np.array([0.0005]), 0.0001) assert ts.shape == (1,) and ts == 5
def test_timestep_function(): dt = defaultclock.dt_ # Check that multiples of dt end up in the correct time step t = np.arange(100000) * dt assert_equal(timestep(t, dt), np.arange(100000)) # Check that inf is handled correctly t = np.array([-np.inf, np.inf]) ts = timestep(t, dt) assert ts[0] < -1e9 assert ts[1] > 1e9 # Scalar values should stay scalar ts = timestep(0.0005, 0.0001) assert np.isscalar(ts) and ts == 5
def __call__(self, t): if not hasattr(self, 'target_variable'): self.target_variable = weakref.ref(self.group().variables[self.targetvar]) i = timestep(t, self.dt) if not (self.buffer_start<=i<self.buffer_end): if i==0: self.filterbank.buffer_init() self.buffer_start = i self.buffer_end = self.buffer_start+self.buffersize self.buffer = self.filterbank.buffer_fetch(self.buffer_start, self.buffer_end) self.target_variable().set_value(self.buffer[i-self.buffer_start, :])
def _check_args(self, indices, times, period, N, sorted, dt): times = Quantity(times) 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 != 0*second: period_bins = np.round(period / dt) # Note: we have to use the timestep function here, to use the same # binning as in the actual simulation max_bin = timestep(np.max(times), dt) if max_bin >= period_bins: 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) times = np.asarray(times) indices = np.asarray(indices) if not sorted: # sort times and indices first by time, then by indices I = np.lexsort((indices, times)) indices = indices[I] times = times[I] # 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._neuron_index = indices self._spike_time = times self._spikes_changed = True return indices, times
def _check_args(self, indices, times, period, N, sorted, dt): times = Quantity(times) 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 != 0 * second: period_bins = np.round(period / dt) # Note: we have to use the timestep function here, to use the same # binning as in the actual simulation max_bin = timestep(np.max(times), dt) if max_bin >= period_bins: 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) times = np.asarray(times) indices = np.asarray(indices) if not sorted: # sort times and indices first by time, then by indices I = np.lexsort((indices, times)) indices = indices[I] times = times[I] # 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._neuron_index = indices self._spike_time = times self._spikes_changed = True return indices, times
def before_run(self, run_namespace): # Do some checks on the period vs. dt dt = self.dt_[:] # make a copy period = self.period_ if period < np.inf and period != 0: if period < dt: raise ValueError('The period of %s is %s, which is smaller ' 'than its dt of %s.' % (self.name, self.period[:], dt*second)) if self._spikes_changed: current_t = self.variables['t'].get_value().item() timesteps = timestep(self._spike_time, dt) current_step = timestep(current_t, dt) in_the_past = np.nonzero(timesteps < current_step)[0] if len(in_the_past): logger.warn('The SpikeGeneratorGroup contains spike times ' 'earlier than the start time of the current run ' '(t = {}), these spikes will be ' 'ignored.'.format(str(current_t*second)), name_suffix='ignored_spikes') self.variables['_lastindex'].set_value(in_the_past[-1] + 1) else: self.variables['_lastindex'].set_value(0) # Check that we don't have more than one spike per neuron in a time bin if self._previous_dt is None or dt != self._previous_dt or self._spikes_changed: # We shift all the spikes by a tiny amount to make sure that spikes # at exact multiples of dt do not end up in the previous time bin # This shift has to be quite significant relative to machine # epsilon, we use 1e-3 of the dt here shift = 1e-3*dt timebins = np.asarray(np.asarray(self._spike_time + shift)/dt, dtype=np.int32) # time is already in sorted order, so it's enough to check if the condition # that timebins[i]==timebins[i+1] and self._neuron_index[i]==self._neuron_index[i+1] # is ever both true if (np.logical_and(np.diff(timebins)==0, np.diff(self._neuron_index)==0)).any(): raise ValueError('Using a dt of %s, some neurons of ' 'SpikeGeneratorGroup "%s" spike more than ' 'once during a time step.' % (str(self.dt), self.name)) self.variables['_timebins'].set_value(timebins) period_bins = np.round(period / dt) max_int = np.iinfo(np.int32).max if period_bins > max_int: logger.warn('Periods longer than {} timesteps (={}) are not ' 'supported, the period will therefore be ' 'considered infinite. Set the period to 0*second ' 'to avoid this ' 'warning.'.format(max_int, str(max_int*dt*second)), 'spikegenerator_long_period') period = period_bins = 0 if np.abs(period_bins * dt - period) > period * np.finfo(dt.dtype).eps: raise NotImplementedError('The period of %s is %s, which is ' 'not an integer multiple of its dt ' 'of %s.' % (self.name, self.period[:], dt * second)) self.variables['_period_bins'].set_value(period_bins) self._previous_dt = dt self._spikes_changed = False super(SpikeGeneratorGroup, self).before_run(run_namespace=run_namespace)
def test_refractoriness_variables(ref_time): # Try a string evaluating to a quantity, and an explicit boolean # condition -- all should do the same thing G = NeuronGroup(1, """ dv/dt = 99.999*Hz : 1 (unless refractory) dw/dt = 99.999*Hz : 1 ref : second ref_no_unit : 1 time_since_spike = (t - lastspike) +1e-3*dt : second ref_subexpression = (t - lastspike + 1e-3*dt) < ref : boolean """, threshold='v>1', reset='v=0;w=0', refractory=ref_time, dtype={ 'ref': defaultclock.variables['t'].dtype, 'ref_no_unit': defaultclock.variables['t'].dtype, 'lastspike': defaultclock.variables['t'].dtype, 'time_since_spike': defaultclock.variables['t'].dtype }) G.ref = 5 * ms G.ref_no_unit = 5 # It should take 10ms to reach the threshold, then v should stay at 0 # for 5ms, while w continues to increase mon = StateMonitor(G, ['v', 'w'], record=True, when='end') run(20 * ms) try: # No difference before the spike assert_allclose(mon[0].v[:timestep(10 * ms, defaultclock.dt)], mon[0].w[:timestep(10 * ms, defaultclock.dt)]) # v is not updated during refractoriness in_refractoriness = mon[0].v[ timestep(10 * ms, defaultclock.dt):timestep(15 * ms, defaultclock.dt)] assert_allclose(in_refractoriness, np.zeros_like(in_refractoriness)) # w should evolve as before assert_allclose( mon[0].w[:timestep(5 * ms, defaultclock.dt)], mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1]) assert np.all(mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1] > 0) # After refractoriness, v should increase again assert np.all( mon[0].v[timestep(15 * ms, defaultclock.dt ):timestep(20 * ms, defaultclock.dt)] > 0) except AssertionError as ex: raise raise AssertionError( f'Assertion failed when using {ref_time!r} as refractory argument:\n{ex}' )
def test_refractoriness_basic(): G = NeuronGroup(1, """ dv/dt = 99.999*Hz : 1 (unless refractory) dw/dt = 99.999*Hz : 1 """, threshold='v>1', reset='v=0;w=0', refractory=5 * ms) # It should take 10ms to reach the threshold, then v should stay at 0 # for 5ms, while w continues to increase mon = StateMonitor(G, ['v', 'w'], record=True, when='end') run(20 * ms) # No difference before the spike assert_allclose(mon[0].v[:timestep(10 * ms, defaultclock.dt)], mon[0].w[:timestep(10 * ms, defaultclock.dt)]) # v is not updated during refractoriness in_refractoriness = mon[0].v[timestep(10 * ms, defaultclock.dt ):timestep(15 * ms, defaultclock.dt)] assert_equal(in_refractoriness, np.zeros_like(in_refractoriness)) # w should evolve as before assert_allclose( mon[0].w[:timestep(5 * ms, defaultclock.dt)], mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1]) assert np.all(mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1] > 0) # After refractoriness, v should increase again assert np.all(mon[0].v[timestep(15 * ms, defaultclock.dt ):timestep(20 * ms, defaultclock.dt)] > 0)
def before_run(self, run_namespace): # Do some checks on the period vs. dt dt = self.dt_[:] # make a copy period = self.period_ if period < np.inf and period != 0: if period < dt: raise ValueError( f"The period of '{self.name}' is {self.period[:]!s}, " f"which is smaller than its dt of {dt*second!s}.") if self._spikes_changed: current_t = self.variables['t'].get_value().item() timesteps = timestep(self._spike_time, dt) current_step = timestep(current_t, dt) in_the_past = np.nonzero(timesteps < current_step)[0] if len(in_the_past): logger.warn( f"The SpikeGeneratorGroup contains spike times " f"earlier than the start time of the current run " f"(t = {current_t*second!s}), these spikes will be " f"ignored.", name_suffix='ignored_spikes') self.variables['_lastindex'].set_value(in_the_past[-1] + 1) else: self.variables['_lastindex'].set_value(0) # Check that we don't have more than one spike per neuron in a time bin if self._previous_dt is None or dt != self._previous_dt or self._spikes_changed: # We shift all the spikes by a tiny amount to make sure that spikes # at exact multiples of dt do not end up in the previous time bin # This shift has to be quite significant relative to machine # epsilon, we use 1e-3 of the dt here shift = 1e-3 * dt timebins = np.asarray(np.asarray(self._spike_time + shift) / dt, dtype=np.int32) # time is already in sorted order, so it's enough to check if the condition # that timebins[i]==timebins[i+1] and self._neuron_index[i]==self._neuron_index[i+1] # is ever both true if (np.logical_and( np.diff(timebins) == 0, np.diff(self._neuron_index) == 0)).any(): raise ValueError( f"Using a dt of {self.dt!s}, some neurons of " f"SpikeGeneratorGroup '{self.name}' spike more than " f"once during a time step.") self.variables['_timebins'].set_value(timebins) period_bins = np.round(period / dt) max_int = np.iinfo(np.int32).max if period_bins > max_int: logger.warn( f"Periods longer than {max_int} timesteps " f"(={max_int*dt*second!s}) are not " "supported, the period will therefore be " "considered infinite. Set the period to 0*second " "to avoid this " "warning.", 'spikegenerator_long_period') period = period_bins = 0 if np.abs(period_bins * dt - period) > period * np.finfo(dt.dtype).eps: raise NotImplementedError(f"The period of '{self.name}' is " f"{self.period[:]!s}, which is " f"not an integer multiple of its dt " f"of {dt*second!s}.") self.variables['_period_bins'].set_value(period_bins) self._previous_dt = dt self._spikes_changed = False super(SpikeGeneratorGroup, self).before_run(run_namespace=run_namespace)
def test_refractoriness_variables(): # Try a string evaluating to a quantity an an explicit boolean # condition -- all should do the same thing for ref_time in [ '5*ms', '(t-lastspike) < 5*ms', 'time_since_spike < 5*ms', 'ref_subexpression', '(t-lastspike) <= ref', 'ref', 'ref_no_unit*ms' ]: reinit_devices() G = NeuronGroup(1, ''' dv/dt = 99.999*Hz : 1 (unless refractory) dw/dt = 99.999*Hz : 1 ref : second ref_no_unit : 1 time_since_spike = t - lastspike : second ref_subexpression = (t - lastspike) < ref : boolean ''', threshold='v>1', reset='v=0;w=0', refractory=ref_time) G.ref = 5 * ms G.ref_no_unit = 5 # It should take 10ms to reach the threshold, then v should stay at 0 # for 5ms, while w continues to increase mon = StateMonitor(G, ['v', 'w'], record=True, when='end') run(20 * ms) try: # No difference before the spike assert_equal(mon[0].v[:timestep(10 * ms, defaultclock.dt)], mon[0].w[:timestep(10 * ms, defaultclock.dt)]) # v is not updated during refractoriness in_refractoriness = mon[0].v[timestep(10 * ms, defaultclock.dt): timestep(15 * ms, defaultclock.dt)] assert_equal(in_refractoriness, np.zeros_like(in_refractoriness)) # w should evolve as before assert_equal( mon[0].w[:timestep(5 * ms, defaultclock.dt)], mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1]) assert np.all( mon[0].w[timestep(10 * ms, defaultclock.dt) + 1:timestep(15 * ms, defaultclock.dt) + 1] > 0) # After refractoriness, v should increase again assert np.all(mon[0].v[timestep(15 * ms, defaultclock.dt ):timestep(20 * ms, defaultclock.dt)]) except AssertionError as ex: raise