def __init__(self, threshold=1 * mvolt, refractory=1 * msecond, state=0, clock=None): self.threshold = threshold # Threshold value self.state = state clock = guess_clock(clock) self.refractory = int(refractory / clock.dt) # this assumes that if the state stays over the threshold, and say # refractory=5ms the user wants spiking at 0ms 5ms 10ms 15ms etc. if is_approx_equal(self.refractory * clock.dt, refractory) and self.refractory > 0: self.refractory -= 1
def test_variable_threshold(G): M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() G.state(2)[:] = array([1., 2., 3.]) # the thresholds G.state(1)[:] = array([4., 1., 2.]) # the values net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0,)) assert (is_approx_equal(t[0], 0 * second))
def test_variable_threshold(G): M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() G.state(2)[:] = array([1., 2., 3.]) # the thresholds G.state(1)[:] = array([4., 1., 2.]) # the values net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0, )) assert (is_approx_equal(t[0], 0 * second))
def test_specified_state(G): M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(0)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(1)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond))
def test(): """ :class:`Reset` ~~~~~~~~~~~~~~ Initialised as:: R = Reset(resetvalue=0*mvolt, state=0) After a neuron from a group with this reset fires, it will set the specified state variable to the given value. State variable 0 is customarily the membrane voltage, but this isn't required. :class:`FunReset` ~~~~~~~~~~~~~~~~~ Initialised as:: R = FunReset(resetfun) Where resetfun is a function taking two arguments, the group it is acting on, and the indices of the spikes to be reset. The following is an example reset function:: def f(P,spikeindices): P._S[0,spikeindices]=array([i/10. for i in range(len(spikeindices))]) :class:`Refractoriness` ~~~~~~~~~~~~~~~~~~~~~~~ Initialised as:: R = Refractoriness(resetvalue=0*mvolt,period=5*msecond,state=0) After a neuron from a group with this reset fires, the specified state variable of the neuron will be set to the specified resetvalue for the specified period. :class:`NoReset` ~~~~~~~~~~~~~~~~ Initialised as:: R = NoReset() Does nothing. """ reinit_default_clock() # test that reset works as expected # the setup below is that group G starts with state values (1,1,1,1,1,0,0,0,0,0) threshold # value 0.5 (which should be initiated for the first 5 neurons) and reset 0.2 so that the # final state should be (0.2,0.2,0.2,0.2,0.2,0,0,0,0,0) G = NeuronGroup(10, model=LazyStateUpdater(), reset=Reset(0.2), threshold=Threshold(0.5), init=(0.,)) G1 = G.subgroup(5) G2 = G.subgroup(5) G1.state(0)[:] = array([1.] * 5) G2.state(0)[:] = array([0.] * 5) net = Network(G) net.run(1 * msecond) assert (all(G1.state(0) < 0.21) and all(0.19 < G1.state(0)) and all(G2.state(0) < 0.01)) # recreate the same behaviour with a VariableReset eqs = ''' v : 1 r : 1 ''' G = NeuronGroup(10, model=eqs, reset=VariableReset(resetvaluestate='r', state='v'), threshold=Threshold(0.5)) G.r = 0.2 G1 = G.subgroup(5) G2 = G.subgroup(5) G1.v[:] = array([1.] * 5) G2.v[:] = array([0.] * 5) net = Network(G) net.run(1 * msecond) assert (all(G1.state(0) < 0.21) and all(0.19 < G1.state(0)) and all(G2.state(0) < 0.01)) # test string reset that resets two variables eqs = '''v : 1 w : 1 ''' G = NeuronGroup(2, model=eqs, reset='v = 0; w = -1', threshold=Threshold(0.5)) G.v =[0.25, 1] G.w =[0, 0] # only second group crosses threshold net = Network(G) net.run(defaultclock.dt) # Check that the states of the first group are unchanged assert(G.v[0] == 0.25 and G.w[0] == 0) # Check that the states of the second group are reset correctly assert(G.v[1] == 0 and G.w[1] == -1) # check that function reset works as expected def f(P, spikeindices): P._S[0, spikeindices] = array([i / 10. for i in range(len(spikeindices))]) P.called_f = True G = NeuronGroup(10, model=LazyStateUpdater(), reset=FunReset(f), threshold=Threshold(2.), init=(3.,)) G.called_f = False net = Network(G) net.run(1 * msecond) assert (G.called_f) for i, v in enumerate(G.state(0)): assert (is_approx_equal(i / 10., v)) # check that refractoriness works as expected # the network below should start at V=15, immediately spike as it is above threshold=1, # then should be clamped at V=-.5 until t=1ms at which point it should quickly evolve # via the DE to a value near 0 (and certainly between -.5 and 0). We test that the # value at t=0.5 is exactly -.5 and the value at t=1.5 is between -0.4 and 0.1 (to # avoid floating point problems) dV = 'dV/dt=-V/(.1*msecond):1.' G = NeuronGroup(1, model=dV, threshold=1., reset=Refractoriness(-.5, 1 * msecond)) G.V = 15. net = Network(G) net.run(0.5 * msecond) for v in G.state(0): assert (is_approx_equal(v, -.5)) net.run(1 * msecond) for v in G.state(0): assert (-0.4 < v < 0.1) get_default_clock().reinit()
def test_spikemonitor(): ''' :class:`SpikeMonitor` ~~~~~~~~~~~~~~~~~~~~~ Records spikes from a :class:`NeuronGroup`. Initialised as one of:: SpikeMonitor(source(,record=True)) SpikeMonitor(source,function=function) Where: source A :class:`NeuronGroup` to record from record True or False to record all the spikes or just summary statistics. function A function f(spikes) which is passed the array of spikes numbers that have fired called each step, to define custom spike monitoring. Has two attributes: nspikes The number of recorded spikes spikes A time ordered list of pairs (i,t) where neuron i fired at time t. :class:`StateMonitor` ~~~~~~~~~~~~~~~~~~~~~ Records the values of a state variable from a :class:`NeuronGroup`. Initialise as:: StateMonitor(P,varname(,record=False) (,when='end)(,timestep=1)(,clock=clock)) Where: P The group to be recorded from varname The state variable name or number to be recorded record What to record. The default value is False and the monitor will only record summary statistics for the variable. You can choose record=integer to record every value of the neuron with that number, record=list of integers to record every value of each of those neurons, or record=True to record every value of every neuron (although beware that this may use a lot of memory). when When the recording should be made in the :class:`Network` update, possible values are any of the strings: 'start', 'before_groups', 'after_groups', 'before_connections', 'after_connections', 'before_resets', 'after_resets', 'end' (in order of when they are run). timestep A recording will be made each timestep clock updates (so timestep should be an integer). clock A clock for the update schedule, use this if you have specified a clock other than the default one in your network, or to update at a lower frequency than the update cycle. Note though that if the clock here is different from the main clock, the when parameter will not be taken into account, as network updates are done clock by clock. Use the timestep parameter if you need recordings to be made at a precise point in the network update step. The :class:`StateMonitor` object has the following properties: times The times at which recordings were made mean The mean value of the state variable for every neuron in the group (not just the ones specified in the record keyword) var The unbiased estimate of the variances, as in mean std The square root of var, as in mean In addition, if M is a :class:`StateMonitor` object, you write:: M[i] for the recorded values of neuron i (if it was specified with the record keyword). It returns an array object. Others ~~~~~~ The following monitors also exist, but are not part of the assured interface because their syntax is subject to change. See the documentation for each class for more details. * :class:`Monitor` (base class) * :class:`ISIHistogramMonitor` * :class:`FileSpikeMonitor` * :class:`PopulationRateMonitor` ''' reinit_default_clock() # test that SpikeMonitor retrieves the spikes generator by SpikeGeneratorGroup spikes = [(0, 3 * ms), (1, 4 * ms), (0, 7 * ms)] G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) M = SpikeMonitor(G) net = Network(G, M) net.run(10 * ms) assert (M.nspikes == 3) for (mi, mt), (i, t) in zip(M.spikes, spikes): assert (mi == i) assert (is_approx_equal(mt, t)) # test that the spiketimes are saved and accessed correctly assert(len(M[0]) == len(M.spiketimes[0]) == 2 and len(M[1]) == len(M.spiketimes[1]) == 1) assert((M.spiketimes[0] == M[0]).all() and (M.spiketimes[1] == M[1]).all()) # test that spiketimes are cleared on reinit M.reinit() assert (M.nspikes == 0) assert (len(M.spikes) == 0) assert (len(M[0]) == 0 and len(M[1]) == 0) assert (len(M.spiketimes[0]) == 0 and len(M.spiketimes[1]) == 0) # test that SpikeMonitor function calling usage does what you'd expect f_spikes = [] def f(spikes): if len(spikes): f_spikes.extend(spikes) G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) M = SpikeMonitor(G, function=f) net = Network(G, M) reinit_default_clock() net.run(10 * ms) assert (f_spikes == [0, 1, 0]) # test that SpikeMonitors in MultiConnection objects do reinitialize # properly G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) G2 = NeuronGroup(1, model='dv/dt = -v / (5*ms) : 1') C = Connection(G, G2, 'v', weight=0) M = SpikeMonitor(G) # Note: Because M and C share the source (G), they are replaced by a # MultiConnection net = Network(G, G2, C, M) net.run(10 * ms) net.reinit() # make sure that the reinit propagates to the SpikeMonitor assert (M.nspikes == 0) assert (len(M.spikes) == 0) assert (len(M[0]) == 0 and len(M[1]) == 0) assert (len(M.spiketimes[0]) == 0 and len(M.spiketimes[1]) == 0) # test interface for StateMonitor object dV = 'dV/dt = 0*Hz : 1.' G = NeuronGroup(3, model=dV, reset=0., threshold=10.) @network_operation(when='start') def f(clock): if clock.t >= 1 * ms: G.V = [1., 2., 3.] M1 = StateMonitor(G, 'V') M2 = StateMonitor(G, 'V', record=0) M3 = StateMonitor(G, 'V', record=[0, 1]) M4 = StateMonitor(G, 'V', record=True) reinit_default_clock() net = Network(G, f, M1, M2, M3, M4) net.run(2 * ms) assert (is_within_absolute_tolerance(M2[0][0], 0.)) assert (is_within_absolute_tolerance(M2[0][-1], 1.)) assert (is_within_absolute_tolerance(M3[1][0], 0.)) assert (is_within_absolute_tolerance(M3[1][-1], 2.)) assert (is_within_absolute_tolerance(M4[2][0], 0.)) assert (is_within_absolute_tolerance(M4[2][-1], 3.)) assert_raises(IndexError, M1.__getitem__, 0) assert_raises(IndexError, M2.__getitem__, 1) assert_raises(IndexError, M3.__getitem__, 2) assert_raises(IndexError, M4.__getitem__, 3) for M in [M3, M4]: assert (is_within_absolute_tolerance(float(max(abs(M.times - M2.times))), float(0 * ms))) assert (is_within_absolute_tolerance(float(max(abs(M.times_ - M2.times_))), 0.)) assert (is_within_absolute_tolerance(float(M2.times[0]), float(0 * ms))) d = diff(M2.times) assert (is_within_absolute_tolerance(max(d), min(d))) assert (is_within_absolute_tolerance(float(max(d)), float(get_default_clock().dt))) # construct unbiased estimator from variances of recorded arrays v = array([ var(M4[0]), var(M4[1]), var(M4[2]) ]) * float(len(M4[0])) / float(len(M4[0]) - 1) m = array([0.5, 1.0, 1.5]) assert (is_within_absolute_tolerance(abs(max(M1.mean - m)), 0.)) assert (is_within_absolute_tolerance(abs(max(M1.var - v)), 0.)) assert (is_within_absolute_tolerance(abs(max(M1.std - v ** 0.5)), 0.)) # test when, timestep, clock for StateMonitor c = Clock(dt=0.1 * ms) cslow = Clock(dt=0.2 * ms) dV = 'dV/dt = 0*Hz : 1.' G = NeuronGroup(1, model=dV, reset=0., threshold=1., clock=c) @network_operation(when='start', clock=c) def f(): G.V = 2. M1 = StateMonitor(G, 'V', record=True, clock=cslow) M2 = StateMonitor(G, 'V', record=True, timestep=2, clock=c) M3 = StateMonitor(G, 'V', record=True, when='before_groups', clock=c) net = Network(G, f, M1, M2, M3, M4) net.run(2 * ms) print M1[0], M3[0] assert (2 * len(M1[0]) == len(M3[0])) assert (len(M1[0]) == len(M2[0])) for i in range(len(M1[0])): assert (is_within_absolute_tolerance(M1[0][i], M2[0][i])) assert (is_within_absolute_tolerance(M1[0][i], 0.)) for x in M3[0]: assert (is_within_absolute_tolerance(x, 2.)) reinit_default_clock() # for next test
def test(): """ :class:`Threshold` ~~~~~~~~~~~~~~~~~~ Initialised as ``Threshold(threshold[,state=0])`` Causes a spike whenever the given state variable is above the threshold value. :class:`NoThreshold` ~~~~~~~~~~~~~~~~~~~~ Does nothing, initialised as ``NoThreshold()`` Functional thresholds ~~~~~~~~~~~~~~~~~~~~~ Initialised as:: FunThreshold(thresholdfun) SimpleFunThreshold(thresholdfun[,state=0]) Threshold functions return a boolean array the same size as the number of neurons in the group, where if the returned array is True at index i then neuron i fires. The arguments passed to the :class:`FunThreshold` function are the full array of state variables for the group in order. The argument passed to the :class:`SimpleFunThreshold` function is the array of length N corresponding to the given state variable. :class:`VariableThreshold` ~~~~~~~~~~~~~~~~~~~~~~~~~~ Initialised as ``VariableThreshold(threshold_state[,state=0])`` Causes a spike whenever the state variable defined by state is above the state variable defined by threshold_state. :class:`EmpiricalThreshold` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Initialised as:: EmpiricalThreshold(threshold[,refractory=1*msecond[,state=0[,clock]]]) Causes a spike when the given state variable exceeds the threshold value, but only if there has been no spike within the refractory period. Will use the given clock if specified, otherwise the standard guessing procedure is used. Poisson thresholds ~~~~~~~~~~~~~~~~~~ Initialised as:: PoissonThreshold([state=0]) HomogeneousPoissonThreshold([state=0]) The Poisson process gets the rates from the specified state variable, the homogeneous version uses the rates from the specified variable of the first neuron in the group. """ reinit_default_clock() # test that Threshold works as expected with default state G = NeuronGroup(3, model=LazyStateUpdater(), reset=Reset(0.), threshold=Threshold(1.), init=(0.,)) M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes) == 0) G.state(0)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 1 * msecond)) # test that Threshold works as expected with specified state def test_specified_state(G): M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(0)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(1)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=2), reset=Reset(0., state=1), threshold=Threshold(1., state=1), init=(0., 0.)) test_specified_state(G) # use string threshold eqs = '''v : 1 w : 1 ''' G = NeuronGroup(3, model=eqs, reset=Reset(0., state=1), threshold='w > 1') test_specified_state(G) # test that VariableThreshold works as expected def test_variable_threshold(G): M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() G.state(2)[:] = array([1., 2., 3.]) # the thresholds G.state(1)[:] = array([4., 1., 2.]) # the values net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0,)) assert (is_approx_equal(t[0], 0 * second)) G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=3), reset=Reset(0., state=1), threshold=VariableThreshold(2, state=1), init=(0., 0., 0.)) test_variable_threshold(G) # use string threshold eqs = '''v : 1 w : 1 x : 1 ''' G = NeuronGroup(3, model=eqs, reset=Reset(0., state=1), threshold='w > x') test_variable_threshold(G) # test that FunThreshold works as expected def f(S0, S1): return S0 > S1 * S1 G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=2), reset=Reset(0.), threshold=FunThreshold(f), init=(0., 0.)) G.state(0)[:] = array([2., 3., 10.]) G.state(1)[:] = array([1., 2., 3.]) # the square root of the threshold values M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) # test that SimpleFunThreshold works as expected def f(S): return S > 1. G = NeuronGroup(3, model=LazyStateUpdater(), reset=Reset(0.), threshold=SimpleFunThreshold(f), init=(0.,)) G.state(0)[:] = array([0.5, 1.5, 2.5]) M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) # test that EmpiricalThreshold works as expected G = NeuronGroup(1, model=LazyStateUpdater(numstatevariables=2), reset=NoReset(), threshold=EmpiricalThreshold(1., refractory=0.5 * msecond, state=1), init=(0., 2.)) M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1.6 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(1))) assert (i == (0, 0, 0, 0)) for i, s in enumerate(t): assert (is_approx_equal(s, i * 0.5 * msecond)) # test that PoissonThreshold works def test_poisson_threshold(G): init = float(1. / get_default_clock().dt) # should cause spiking at every time interval G.state(0)[:] = array([0., init, 0.]) M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes)) i, t = zip(*sorted(M.spikes, key=itemgetter(1))) assert (all(j == 1 for j in i)) G = NeuronGroup(3, model=LazyStateUpdater(), reset=NoReset(), threshold=PoissonThreshold()) test_poisson_threshold(G) # Poisson threshold via a string threshold using the rand() function eqs = '''v : 1 w : 1 x : 1 ''' # A threshold with rand in it is not supported by CThreshold if not (get_global_preference('usecodegen') and get_global_preference('usecodegenthreshold') and get_global_preference('useweave') and get_global_preference('usecodegenweave')): G = NeuronGroup(3, model=eqs, reset=NoReset(), threshold='rand() < v') test_poisson_threshold(G) G = NeuronGroup(3, model=eqs, reset=NoReset(), threshold=StringThreshold('rand() < v')) test_poisson_threshold(G) # test that HomogeneousPoissonThreshold works init = float(1. / get_default_clock().dt) # should cause spiking at every time interval G = NeuronGroup(3, model=LazyStateUpdater(), reset=NoReset(), threshold=HomogeneousPoissonThreshold()) M = SpikeMonitor(G, True) net = Network(G, M) G.state(0)[:] = array([0., init, 0.]) # should do nothing, because only first neuron is looked at net.run(1 * msecond) assert (len(M.spikes) == 0) G.state(0)[:] = array([init, 0., 0.]) # should do nothing, because only first neuron is looked at net.run(1 * msecond)
def test(): """ :class:`Threshold` ~~~~~~~~~~~~~~~~~~ Initialised as ``Threshold(threshold[,state=0])`` Causes a spike whenever the given state variable is above the threshold value. :class:`NoThreshold` ~~~~~~~~~~~~~~~~~~~~ Does nothing, initialised as ``NoThreshold()`` Functional thresholds ~~~~~~~~~~~~~~~~~~~~~ Initialised as:: FunThreshold(thresholdfun) SimpleFunThreshold(thresholdfun[,state=0]) Threshold functions return a boolean array the same size as the number of neurons in the group, where if the returned array is True at index i then neuron i fires. The arguments passed to the :class:`FunThreshold` function are the full array of state variables for the group in order. The argument passed to the :class:`SimpleFunThreshold` function is the array of length N corresponding to the given state variable. :class:`VariableThreshold` ~~~~~~~~~~~~~~~~~~~~~~~~~~ Initialised as ``VariableThreshold(threshold_state[,state=0])`` Causes a spike whenever the state variable defined by state is above the state variable defined by threshold_state. :class:`EmpiricalThreshold` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Initialised as:: EmpiricalThreshold(threshold[,refractory=1*msecond[,state=0[,clock]]]) Causes a spike when the given state variable exceeds the threshold value, but only if there has been no spike within the refractory period. Will use the given clock if specified, otherwise the standard guessing procedure is used. Poisson thresholds ~~~~~~~~~~~~~~~~~~ Initialised as:: PoissonThreshold([state=0]) HomogeneousPoissonThreshold([state=0]) The Poisson process gets the rates from the specified state variable, the homogeneous version uses the rates from the specified variable of the first neuron in the group. """ reinit_default_clock() # test that Threshold works as expected with default state G = NeuronGroup(3, model=LazyStateUpdater(), reset=Reset(0.), threshold=Threshold(1.), init=(0., )) M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes) == 0) G.state(0)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 1 * msecond)) # test that Threshold works as expected with specified state def test_specified_state(G): M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(0)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) assert (len(M.spikes) == 0) net.reinit() G.state(1)[:] = array([0.5, 1.5, 2.5]) net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=2), reset=Reset(0., state=1), threshold=Threshold(1., state=1), init=(0., 0.)) test_specified_state(G) # use string threshold eqs = '''v : 1 w : 1 ''' G = NeuronGroup(3, model=eqs, reset=Reset(0., state=1), threshold='w > 1') test_specified_state(G) # test that VariableThreshold works as expected def test_variable_threshold(G): M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() G.state(2)[:] = array([1., 2., 3.]) # the thresholds G.state(1)[:] = array([4., 1., 2.]) # the values net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0, )) assert (is_approx_equal(t[0], 0 * second)) G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=3), reset=Reset(0., state=1), threshold=VariableThreshold(2, state=1), init=(0., 0., 0.)) test_variable_threshold(G) # use string threshold eqs = '''v : 1 w : 1 x : 1 ''' G = NeuronGroup(3, model=eqs, reset=Reset(0., state=1), threshold='w > x') test_variable_threshold(G) # test that FunThreshold works as expected def f(S0, S1): return S0 > S1 * S1 G = NeuronGroup(3, model=LazyStateUpdater(numstatevariables=2), reset=Reset(0.), threshold=FunThreshold(f), init=(0., 0.)) G.state(0)[:] = array([2., 3., 10.]) G.state(1)[:] = array([1., 2., 3.]) # the square root of the threshold values M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (0, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) # test that SimpleFunThreshold works as expected def f(S): return S > 1. G = NeuronGroup(3, model=LazyStateUpdater(), reset=Reset(0.), threshold=SimpleFunThreshold(f), init=(0., )) G.state(0)[:] = array([0.5, 1.5, 2.5]) M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(0))) assert (i == (1, 2)) for s in t: assert (is_approx_equal(s, 0 * msecond)) # test that EmpiricalThreshold works as expected G = NeuronGroup(1, model=LazyStateUpdater(numstatevariables=2), reset=NoReset(), threshold=EmpiricalThreshold(1., refractory=0.5 * msecond, state=1), init=(0., 2.)) M = SpikeMonitor(G, True) net = Network(G, M) get_default_clock().reinit() net.run(1.6 * msecond) i, t = zip(*sorted(M.spikes, key=itemgetter(1))) assert (i == (0, 0, 0, 0)) for i, s in enumerate(t): assert (is_approx_equal(s, i * 0.5 * msecond)) # test that PoissonThreshold works def test_poisson_threshold(G): init = float(1. / get_default_clock().dt ) # should cause spiking at every time interval G.state(0)[:] = array([0., init, 0.]) M = SpikeMonitor(G, True) net = Network(G, M) net.run(1 * msecond) assert (len(M.spikes)) i, t = zip(*sorted(M.spikes, key=itemgetter(1))) assert (all(j == 1 for j in i)) G = NeuronGroup(3, model=LazyStateUpdater(), reset=NoReset(), threshold=PoissonThreshold()) test_poisson_threshold(G) # Poisson threshold via a string threshold using the rand() function eqs = '''v : 1 w : 1 x : 1 ''' # A threshold with rand in it is not supported by CThreshold if not (get_global_preference('usecodegen') and get_global_preference('usecodegenthreshold') and get_global_preference('useweave') and get_global_preference('usecodegenweave')): G = NeuronGroup(3, model=eqs, reset=NoReset(), threshold='rand() < v') test_poisson_threshold(G) G = NeuronGroup(3, model=eqs, reset=NoReset(), threshold=StringThreshold('rand() < v')) test_poisson_threshold(G) # test that HomogeneousPoissonThreshold works init = float( 1. / get_default_clock().dt) # should cause spiking at every time interval G = NeuronGroup(3, model=LazyStateUpdater(), reset=NoReset(), threshold=HomogeneousPoissonThreshold()) M = SpikeMonitor(G, True) net = Network(G, M) G.state(0)[:] = array( [0., init, 0.]) # should do nothing, because only first neuron is looked at net.run(1 * msecond) assert (len(M.spikes) == 0) G.state(0)[:] = array( [init, 0., 0.]) # should do nothing, because only first neuron is looked at net.run(1 * msecond)
def test_construction(): ''' :class:`Connection` ~~~~~~~~~~~~~~~~~~~ **Initialised as:** :: Connection(source, target[, state=0[, delay=0*ms]]) With arguments: ``source`` The group from which spikes will be propagated. ``target`` The group to which spikes will be propagated. ``state`` The state variable name or number that spikes will be propagated to in the target group. ``delay`` The delay between a spike being generated at the source and received at the target. At the moment, the mechanism for delays only works for relatively short delays (an error will be generated for delays that are too long), but this is subject to change. The exact behaviour then is not part of the assured interface, although it is very likely that the syntax will not change (or will at least be backwards compatible). **Methods** ``connect_random(P,Q,p[,weight=1])`` Connects each neuron in ``P`` to each neuron in ``Q``. ``connect_full(P,Q[,weight=1])`` Connect every neuron in ``P`` to every neuron in ``Q``. ``connect_one_to_one(P,Q)`` If ``P`` and ``Q`` have the same number of neurons then neuron ``i`` in ``P`` will be connected to neuron ``i`` in ``Q`` with weight 1. Additionally, you can directly access the matrix of weights by writing:: C = Connection(P,Q) print C[i,j] C[i,j] = ... Where here ``i`` is the source neuron and ``j`` is the target neuron. Note: No unit checking is currently done if you use this method, but this is subject to change for future releases. The behaviour when a list of neuron ``spikes`` is received is to add ``W[i,:]`` to the target state variable for each ``i`` in ``spikes``. ''' reinit_default_clock() # test Connection object eqs = ''' da/dt = 0.*hertz : 1. db/dt = 0.*hertz : 1. ''' spikes = [(0, 1 * msecond), (1, 3 * msecond)] G1 = SpikeGeneratorGroup(2, spikes) G2 = NeuronGroup(2, model=eqs, threshold=10., reset=0.) # first test the methods # connect_full C = Connection(G1, G2) C.connect_full(G1, G2, weight=2.) for i in range(2): for j in range(2): assert (is_approx_equal(C[i, j], 2.)) # connect_random C = Connection(G1, G2) C.connect_random(G1, G2, 0.5, weight=2.) # can't assert anything about that # connect_one_to_one C = Connection(G1, G2) C.connect_one_to_one(G1, G2) for i in range(2): for j in range(2): if i == j: assert (is_approx_equal(C[i, j], 1.)) else: assert (is_approx_equal(C[i, j], 0.)) del C # and we will use a specific set of connections in the next part Ca = Connection(G1, G2, 'a') Cb = Connection(G1, G2, 'b') Ca[0, 0] = 1. Ca[0, 1] = 1. Ca[1, 0] = 1. #Ca[1,1]=0 by default #Cb[0,0]=0 by default Cb[0, 1] = 1. Cb[1, 0] = 1. Cb[1, 1] = 1. net = Network(G1, G2, Ca, Cb) net.run(2 * msecond) # after 2 ms, neuron 0 will have fired, so a 0 and 1 should # have increased by 1 to [1,1], and b 1 should have increased # by 1 to 1 assert (is_approx_equal(G2.a[0], 1.)) assert (is_approx_equal(G2.a[1], 1.)) assert (is_approx_equal(G2.b[0], 0.)) assert (is_approx_equal(G2.b[1], 1.)) net.run(2 * msecond) # after 4 ms, neuron 1 will have fired, so a 0 should have # increased by 1 to 2, and b 0 and 1 should have increased # by 1 to [1, 2] assert (is_approx_equal(G2.a[0], 2.)) assert (is_approx_equal(G2.a[1], 1.)) assert (is_approx_equal(G2.b[0], 1.)) assert (is_approx_equal(G2.b[1], 2.)) reinit_default_clock()
def test(): """ :class:`Reset` ~~~~~~~~~~~~~~ Initialised as:: R = Reset(resetvalue=0*mvolt, state=0) After a neuron from a group with this reset fires, it will set the specified state variable to the given value. State variable 0 is customarily the membrane voltage, but this isn't required. :class:`FunReset` ~~~~~~~~~~~~~~~~~ Initialised as:: R = FunReset(resetfun) Where resetfun is a function taking two arguments, the group it is acting on, and the indices of the spikes to be reset. The following is an example reset function:: def f(P,spikeindices): P._S[0,spikeindices]=array([i/10. for i in range(len(spikeindices))]) :class:`Refractoriness` ~~~~~~~~~~~~~~~~~~~~~~~ Initialised as:: R = Refractoriness(resetvalue=0*mvolt,period=5*msecond,state=0) After a neuron from a group with this reset fires, the specified state variable of the neuron will be set to the specified resetvalue for the specified period. :class:`NoReset` ~~~~~~~~~~~~~~~~ Initialised as:: R = NoReset() Does nothing. """ reinit_default_clock() # test that reset works as expected # the setup below is that group G starts with state values (1,1,1,1,1,0,0,0,0,0) threshold # value 0.5 (which should be initiated for the first 5 neurons) and reset 0.2 so that the # final state should be (0.2,0.2,0.2,0.2,0.2,0,0,0,0,0) G = NeuronGroup(10, model=LazyStateUpdater(), reset=Reset(0.2), threshold=Threshold(0.5), init=(0., )) G1 = G.subgroup(5) G2 = G.subgroup(5) G1.state(0)[:] = array([1.] * 5) G2.state(0)[:] = array([0.] * 5) net = Network(G) net.run(1 * msecond) assert (all(G1.state(0) < 0.21) and all(0.19 < G1.state(0)) and all(G2.state(0) < 0.01)) # recreate the same behaviour with a VariableReset eqs = ''' v : 1 r : 1 ''' G = NeuronGroup(10, model=eqs, reset=VariableReset(resetvaluestate='r', state='v'), threshold=Threshold(0.5)) G.r = 0.2 G1 = G.subgroup(5) G2 = G.subgroup(5) G1.v[:] = array([1.] * 5) G2.v[:] = array([0.] * 5) net = Network(G) net.run(1 * msecond) assert (all(G1.state(0) < 0.21) and all(0.19 < G1.state(0)) and all(G2.state(0) < 0.01)) # test string reset that resets two variables eqs = '''v : 1 w : 1 ''' G = NeuronGroup(2, model=eqs, reset='v = 0; w = -1', threshold=Threshold(0.5)) G.v = [0.25, 1] G.w = [0, 0] # only second group crosses threshold net = Network(G) net.run(defaultclock.dt) # Check that the states of the first group are unchanged assert (G.v[0] == 0.25 and G.w[0] == 0) # Check that the states of the second group are reset correctly assert (G.v[1] == 0 and G.w[1] == -1) # check that function reset works as expected def f(P, spikeindices): P._S[0, spikeindices] = array([i / 10. for i in range(len(spikeindices))]) P.called_f = True G = NeuronGroup(10, model=LazyStateUpdater(), reset=FunReset(f), threshold=Threshold(2.), init=(3., )) G.called_f = False net = Network(G) net.run(1 * msecond) assert (G.called_f) for i, v in enumerate(G.state(0)): assert (is_approx_equal(i / 10., v)) # check that refractoriness works as expected # the network below should start at V=15, immediately spike as it is above threshold=1, # then should be clamped at V=-.5 until t=1ms at which point it should quickly evolve # via the DE to a value near 0 (and certainly between -.5 and 0). We test that the # value at t=0.5 is exactly -.5 and the value at t=1.5 is between -0.4 and 0.1 (to # avoid floating point problems) dV = 'dV/dt=-V/(.1*msecond):1.' G = NeuronGroup(1, model=dV, threshold=1., reset=Refractoriness(-.5, 1 * msecond)) G.V = 15. net = Network(G) net.run(0.5 * msecond) for v in G.state(0): assert (is_approx_equal(v, -.5)) net.run(1 * msecond) for v in G.state(0): assert (-0.4 < v < 0.1) get_default_clock().reinit()
def test_spikemonitor(): ''' :class:`SpikeMonitor` ~~~~~~~~~~~~~~~~~~~~~ Records spikes from a :class:`NeuronGroup`. Initialised as one of:: SpikeMonitor(source(,record=True)) SpikeMonitor(source,function=function) Where: source A :class:`NeuronGroup` to record from record True or False to record all the spikes or just summary statistics. function A function f(spikes) which is passed the array of spikes numbers that have fired called each step, to define custom spike monitoring. Has two attributes: nspikes The number of recorded spikes spikes A time ordered list of pairs (i,t) where neuron i fired at time t. :class:`StateMonitor` ~~~~~~~~~~~~~~~~~~~~~ Records the values of a state variable from a :class:`NeuronGroup`. Initialise as:: StateMonitor(P,varname(,record=False) (,when='end)(,timestep=1)(,clock=clock)) Where: P The group to be recorded from varname The state variable name or number to be recorded record What to record. The default value is False and the monitor will only record summary statistics for the variable. You can choose record=integer to record every value of the neuron with that number, record=list of integers to record every value of each of those neurons, or record=True to record every value of every neuron (although beware that this may use a lot of memory). when When the recording should be made in the :class:`Network` update, possible values are any of the strings: 'start', 'before_groups', 'after_groups', 'before_connections', 'after_connections', 'before_resets', 'after_resets', 'end' (in order of when they are run). timestep A recording will be made each timestep clock updates (so timestep should be an integer). clock A clock for the update schedule, use this if you have specified a clock other than the default one in your network, or to update at a lower frequency than the update cycle. Note though that if the clock here is different from the main clock, the when parameter will not be taken into account, as network updates are done clock by clock. Use the timestep parameter if you need recordings to be made at a precise point in the network update step. The :class:`StateMonitor` object has the following properties: times The times at which recordings were made mean The mean value of the state variable for every neuron in the group (not just the ones specified in the record keyword) var The unbiased estimate of the variances, as in mean std The square root of var, as in mean In addition, if M is a :class:`StateMonitor` object, you write:: M[i] for the recorded values of neuron i (if it was specified with the record keyword). It returns an array object. Others ~~~~~~ The following monitors also exist, but are not part of the assured interface because their syntax is subject to change. See the documentation for each class for more details. * :class:`Monitor` (base class) * :class:`ISIHistogramMonitor` * :class:`FileSpikeMonitor` * :class:`PopulationRateMonitor` ''' reinit_default_clock() # test that SpikeMonitor retrieves the spikes generator by SpikeGeneratorGroup spikes = [(0, 3 * ms), (1, 4 * ms), (0, 7 * ms)] G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) M = SpikeMonitor(G) net = Network(G, M) net.run(10 * ms) assert (M.nspikes == 3) for (mi, mt), (i, t) in zip(M.spikes, spikes): assert (mi == i) assert (is_approx_equal(mt, t)) # test that the spiketimes are saved and accessed correctly assert (len(M[0]) == len(M.spiketimes[0]) == 2 and len(M[1]) == len(M.spiketimes[1]) == 1) assert ((M.spiketimes[0] == M[0]).all() and (M.spiketimes[1] == M[1]).all()) # test that spiketimes are cleared on reinit M.reinit() assert (M.nspikes == 0) assert (len(M.spikes) == 0) assert (len(M[0]) == 0 and len(M[1]) == 0) assert (len(M.spiketimes[0]) == 0 and len(M.spiketimes[1]) == 0) # test that SpikeMonitor function calling usage does what you'd expect f_spikes = [] def f(spikes): if len(spikes): f_spikes.extend(spikes) G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) M = SpikeMonitor(G, function=f) net = Network(G, M) reinit_default_clock() net.run(10 * ms) assert (f_spikes == [0, 1, 0]) # test that SpikeMonitors in MultiConnection objects do reinitialize # properly G = SpikeGeneratorGroup(2, spikes, clock=defaultclock) G2 = NeuronGroup(1, model='dv/dt = -v / (5*ms) : 1') C = Connection(G, G2, 'v', weight=0) M = SpikeMonitor(G) # Note: Because M and C share the source (G), they are replaced by a # MultiConnection net = Network(G, G2, C, M) net.run(10 * ms) net.reinit() # make sure that the reinit propagates to the SpikeMonitor assert (M.nspikes == 0) assert (len(M.spikes) == 0) assert (len(M[0]) == 0 and len(M[1]) == 0) assert (len(M.spiketimes[0]) == 0 and len(M.spiketimes[1]) == 0) # test interface for StateMonitor object dV = 'dV/dt = 0*Hz : 1.' G = NeuronGroup(3, model=dV, reset=0., threshold=10.) @network_operation(when='start') def f(clock): if clock.t >= 1 * ms: G.V = [1., 2., 3.] M1 = StateMonitor(G, 'V') M2 = StateMonitor(G, 'V', record=0) M3 = StateMonitor(G, 'V', record=[0, 1]) M4 = StateMonitor(G, 'V', record=True) reinit_default_clock() net = Network(G, f, M1, M2, M3, M4) net.run(2 * ms) assert (is_within_absolute_tolerance(M2[0][0], 0.)) assert (is_within_absolute_tolerance(M2[0][-1], 1.)) assert (is_within_absolute_tolerance(M3[1][0], 0.)) assert (is_within_absolute_tolerance(M3[1][-1], 2.)) assert (is_within_absolute_tolerance(M4[2][0], 0.)) assert (is_within_absolute_tolerance(M4[2][-1], 3.)) assert_raises(IndexError, M1.__getitem__, 0) assert_raises(IndexError, M2.__getitem__, 1) assert_raises(IndexError, M3.__getitem__, 2) assert_raises(IndexError, M4.__getitem__, 3) for M in [M3, M4]: assert (is_within_absolute_tolerance( float(max(abs(M.times - M2.times))), float(0 * ms))) assert (is_within_absolute_tolerance( float(max(abs(M.times_ - M2.times_))), 0.)) assert (is_within_absolute_tolerance(float(M2.times[0]), float(0 * ms))) d = diff(M2.times) assert (is_within_absolute_tolerance(max(d), min(d))) assert (is_within_absolute_tolerance(float(max(d)), float(get_default_clock().dt))) # construct unbiased estimator from variances of recorded arrays v = array([var(M4[0]), var(M4[1]), var(M4[2])]) * float(len( M4[0])) / float(len(M4[0]) - 1) m = array([0.5, 1.0, 1.5]) assert (is_within_absolute_tolerance(abs(max(M1.mean - m)), 0.)) assert (is_within_absolute_tolerance(abs(max(M1.var - v)), 0.)) assert (is_within_absolute_tolerance(abs(max(M1.std - v**0.5)), 0.)) # test when, timestep, clock for StateMonitor c = Clock(dt=0.1 * ms) cslow = Clock(dt=0.2 * ms) dV = 'dV/dt = 0*Hz : 1.' G = NeuronGroup(1, model=dV, reset=0., threshold=1., clock=c) @network_operation(when='start', clock=c) def f(): G.V = 2. M1 = StateMonitor(G, 'V', record=True, clock=cslow) M2 = StateMonitor(G, 'V', record=True, timestep=2, clock=c) M3 = StateMonitor(G, 'V', record=True, when='before_groups', clock=c) net = Network(G, f, M1, M2, M3, M4) net.run(2 * ms) print M1[0], M3[0] assert (2 * len(M1[0]) == len(M3[0])) assert (len(M1[0]) == len(M2[0])) for i in range(len(M1[0])): assert (is_within_absolute_tolerance(M1[0][i], M2[0][i])) assert (is_within_absolute_tolerance(M1[0][i], 0.)) for x in M3[0]: assert (is_within_absolute_tolerance(x, 2.)) reinit_default_clock() # for next test