def parse_expressions(renderer, evaluator, numvalues=10): exprs = [([m for m in get_identifiers(l) if len(m)==1], [], l.strip()) for l in TEST_EXPRESSIONS.split('\n') if l.strip()] i, imod = 1, 33 for varids, funcids, expr in exprs: pexpr = renderer.render_expr(expr) n = 0 for _ in xrange(numvalues): # assign some random values ns = {} for v in varids: if v in ['n', 'm']: # integer values ns[v] = i else: ns[v] = float(i)/imod i = i%imod+1 r1 = eval(expr.replace('&', ' and ').replace('|', ' or '), ns) n += 1 r2 = evaluator(pexpr, ns) try: # Use all close because we can introduce small numerical # difference through sympy's rearrangements assert_allclose(r1, r2, atol=10) except AssertionError as e: raise AssertionError("In expression " + str(expr) + " translated to " + str(pexpr) + " " + str(e))
def test_function_dependencies_numpy(): if prefs.codegen.target != 'numpy': raise SkipTest('numpy-only test') @implementation('cpp', ''' float foo(float x) { return 42*0.001; }''') @check_units(x=volt, result=volt) def foo(x): return 42*mV # Second function with an independent implementation for C++ and an # implementation for numpy that makes use of the previous function. # Note that we don't need to use the explicit dependencies mechanism for # numpy, since the Python function stores a reference to the referenced # function directly @implementation('cpp', ''' float bar(float x) { return 84*0.001; }''') @check_units(x=volt, result=volt) def bar(x): return 2*foo(x) G = NeuronGroup(5, 'v : volt') G.run_regularly('v = bar(v)') net = Network(G) net.run(defaultclock.dt) assert_allclose(G.v_[:], 84*0.001)
def test_state_monitor_indexing(): # Check indexing semantics G = NeuronGroup(10, 'v:volt') G.v = np.arange(10) * volt mon = StateMonitor(G, 'v', record=[5, 6, 7]) run(2 * defaultclock.dt) assert_array_equal(mon.v, np.array([[5, 5], [6, 6], [7, 7]]) * volt) assert_array_equal(mon.v_, np.array([[5, 5], [6, 6], [7, 7]])) assert_array_equal(mon[5].v, mon.v[0]) assert_array_equal(mon[7].v, mon.v[2]) assert_array_equal(mon[[5, 7]].v, mon.v[[0, 2]]) assert_array_equal(mon[np.array([5, 7])].v, mon.v[[0, 2]]) assert_allclose(mon.t[1:], Quantity([defaultclock.dt])) assert_raises(IndexError, lambda: mon[8]) assert_raises(TypeError, lambda: mon['string']) assert_raises(TypeError, lambda: mon[5.0]) assert_raises(TypeError, lambda: mon[[5.0, 6.0]])
def test_function_dependencies_cython(): if prefs.codegen.target != 'cython': raise SkipTest('cython-only test') @implementation('cython', ''' cdef float foo(float x): return 42*0.001 ''') @check_units(x=volt, result=volt) def foo(x): return 42*mV # Second function with an independent implementation for numpy and an # implementation for C++ that makes use of the previous function. @implementation('cython', ''' cdef float bar(float x): return 2*foo(x) ''', dependencies={'foo': foo}) @check_units(x=volt, result=volt) def bar(x): return 84*mV G = NeuronGroup(5, 'v : volt') G.run_regularly('v = bar(v)') net = Network(G) net.run(defaultclock.dt) assert_allclose(G.v_[:], 84*0.001)
def test_inplace_on_scalars(): # We want "copy semantics" for in-place operations on scalar quantities # in the same way as for Python scalars for scalar in [3*mV, 3*mV/mV]: scalar_reference = scalar scalar_copy = Quantity(scalar, copy=True) scalar += scalar_copy assert_equal(scalar_copy, scalar_reference) scalar *= 1.5 assert_equal(scalar_copy, scalar_reference) scalar /= 2 assert_equal(scalar_copy, scalar_reference) # also check that it worked correctly for the scalar itself assert_allclose(scalar, (scalar_copy + scalar_copy)*1.5/2) # For arrays, it should use reference semantics for vector in [[3]*mV, [3]*mV/mV]: vector_reference = vector vector_copy = Quantity(vector, copy=True) vector += vector_copy assert_equal(vector, vector_reference) vector *= 1.5 assert_equal(vector, vector_reference) vector /= 2 assert_equal(vector, vector_reference) # also check that it worked correctly for the vector itself assert_allclose(vector, (vector_copy + vector_copy)*1.5/2)
def test_math_functions(): ''' Test that math functions give the same result, regardless of whether used directly or in generated Python or C++ code. ''' default_dt = defaultclock.dt test_array = np.array([-1, -0.5, 0, 0.5, 1]) def int_(x): return array(x, dtype=int) int_.__name__ = 'int' with catch_logs() as _: # Let's suppress warnings about illegal values # Functions with a single argument for func in [cos, tan, sinh, cosh, tanh, arcsin, arccos, arctan, log, log10, exp, np.sqrt, np.ceil, np.floor, np.sign, int_]: # Calculate the result directly numpy_result = func(test_array) # Calculate the result in a somewhat complicated way by using a # subexpression in a NeuronGroup if func.__name__ == 'absolute': # we want to use the name abs instead of absolute func_name = 'abs' else: func_name = func.__name__ G = NeuronGroup(len(test_array), '''func = {func}(variable) : 1 variable : 1'''.format(func=func_name)) G.variable = test_array mon = StateMonitor(G, 'func', record=True) net = Network(G, mon) net.run(default_dt) assert_allclose(numpy_result, mon.func_.flatten(), err_msg='Function %s did not return the correct values' % func.__name__) # Functions/operators scalar = 3 for func, operator in [(np.power, '**'), (np.mod, '%')]: # Calculate the result directly numpy_result = func(test_array, scalar) # Calculate the result in a somewhat complicated way by using a # subexpression in a NeuronGroup G = NeuronGroup(len(test_array), '''func = variable {op} scalar : 1 variable : 1'''.format(op=operator)) G.variable = test_array mon = StateMonitor(G, 'func', record=True) net = Network(G, mon) net.run(default_dt) assert_allclose(numpy_result, mon.func_.flatten(), err_msg='Function %s did not return the correct values' % func.__name__)
def test_rate_monitor_2(): G = NeuronGroup(10, 'v : 1', threshold='v>1') # no reset G.v['i<5'] = 1.1 # Half of the neurons fire every time step rate_mon = PopulationRateMonitor(G) net = Network(G, rate_mon) net.run(10*defaultclock.dt) assert_allclose(rate_mon.rate, 0.5 * np.ones(10) / defaultclock.dt) assert_allclose(rate_mon.rate_, 0.5 *np.asarray(np.ones(10) / defaultclock.dt_))
def test_timedarray_no_upsampling(): # Test a TimedArray where no upsampling is necessary because the monitor's # dt is bigger than the TimedArray's ta = TimedArray(np.arange(10), dt=0.01*ms) G = NeuronGroup(1, 'value = ta(t): 1', dt=0.1*ms) mon = StateMonitor(G, 'value', record=True, dt=1*ms) run(2.1*ms) assert_allclose(mon[0].value, [0, 9, 9])
def assert_quantity(q, values, unit): assert isinstance(q, Quantity) or (have_same_dimensions(unit, 1) and (values.shape == () or isinstance(q, np.ndarray))), q assert_allclose(np.asarray(q), values) assert have_same_dimensions(q, unit), ('Dimension mismatch: (%s) (%s)' % (get_dimensions(q), get_dimensions(unit)))
def test_timedarray_semantics(): # Make sure that timed arrays are interpreted as specifying the values # between t and t+dt (not between t-dt/2 and t+dt/2 as in Brian1) ta = TimedArray(array([0, 1]), dt=0.4*ms) G = NeuronGroup(1, 'value = ta(t) : 1', dt=0.1*ms) mon = StateMonitor(G, 'value', record=0) run(0.8*ms) assert_allclose(mon[0].value, [0, 0, 0, 0, 1, 1, 1, 1]) assert_allclose(mon[0].value, ta(mon.t))
def test_rallpack1(): ''' Rallpack 1 ''' if prefs.core.default_float_dtype is np.float32: raise SkipTest('Need double precision for this test') defaultclock.dt = 0.05*ms # Morphology diameter = 1*um length = 1*mm Cm = 1 * uF / cm ** 2 Ri = 100 * ohm * cm N = 1000 morpho = Cylinder(diameter=diameter, length=length, n=N) # Passive channels gL = 1./(40000*ohm*cm**2) EL = -65*mV eqs = ''' Im = gL*(EL - v) : amp/meter**2 I : amp (point current, constant) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri) neuron.v = EL neuron.I[0] = 0.1*nA # injecting at the left end #Record at the two ends mon = StateMonitor(neuron, 'v', record=[0, 999], when='start', dt=0.05*ms) run(250*ms + defaultclock.dt) # Load the theoretical results basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rallpack_data') data_0 = np.loadtxt(os.path.join(basedir, 'ref_cable.0')) data_x = np.loadtxt(os.path.join(basedir, 'ref_cable.x')) scale_0 = max(data_0[:, 1]*volt) - min(data_0[:, 1]*volt) scale_x = max(data_x[:, 1]*volt) - min(data_x[:, 1]*volt) squared_diff_0 = (data_0[:, 1] * volt - mon[0].v)**2 squared_diff_x = (data_x[:, 1] * volt - mon[999].v)**2 rel_RMS_0 = sqrt(mean(squared_diff_0))/scale_0 rel_RMS_x = sqrt(mean(squared_diff_x))/scale_x max_rel_0 = sqrt(max(squared_diff_0))/scale_0 max_rel_x = sqrt(max(squared_diff_x))/scale_x # sanity check: times are the same assert_allclose(mon.t/second, data_0[:, 0]) assert_allclose(mon.t/second, data_x[:, 0]) # RMS error should be < 0.1%, maximum error along the curve should be < 0.5% assert 100*rel_RMS_0 < 0.1 assert 100*rel_RMS_x < 0.1 assert 100*max_rel_0 < 0.5 assert 100*max_rel_x < 0.5
def test_constants_values(): ''' Make sure that symbolic constants use the correct values in code ''' G = NeuronGroup(3, 'v : 1') G.v[0] = 'pi' G.v[1] = 'e' G.v[2] = 'inf' run(0*ms) assert_allclose(G.v[:], [np.pi, np.e, np.inf])
def test_poissoninput_refractory(): eqs = ''' dv/dt = 0/second : 1 (unless refractory) ''' G = NeuronGroup(10, eqs, reset='v=0', threshold='v>4.5', refractory=5*defaultclock.dt) # Will increase the value by 1.0 at each time step P = PoissonInput(G, 'v', 1, 1/defaultclock.dt, weight=1.0) mon = StateMonitor(G, 'v', record=5) run(10*defaultclock.dt) expected = np.arange(10, dtype=np.float) expected[6-int(schedule_propagation_offset()/defaultclock.dt):] = 0 assert_allclose(mon[5].v[:], expected)
def test_active_flag(): G = NeuronGroup(1, 'dv/dt = 1/ms : 1') mon = StateMonitor(G, 'v', record=0) mon.active = False run(1*ms) mon.active = True G.active = False run(1*ms) device.build(direct_call=False, **device.build_options) # Monitor should start recording at 1ms # Neurongroup should not integrate after 1ms (but should have integrated before) assert_allclose(mon[0].t[0], 1*ms) assert_allclose(mon[0].v, 1.0)
def test_str_repr(): ''' Test that str representations do not raise any errors and that repr fullfills eval(repr(x)) == x. ''' from numpy import array # necessary for evaluating repr units_which_should_exist = [metre, meter, kilogram, kilogramme, second, amp, kelvin, mole, candle, radian, steradian, hertz, newton, pascal, joule, watt, coulomb, volt, farad, ohm, siemens, weber, tesla, henry, lumen, lux, becquerel, gray, sievert, katal, gram, gramme, molar, liter, litre] # scaled versions of all these units should exist (we just check farad as an example) some_scaled_units = [Yfarad, Zfarad, Efarad, Pfarad, Tfarad, Gfarad, Mfarad, kfarad, hfarad, dafarad, dfarad, cfarad, mfarad, ufarad, nfarad, pfarad, ffarad, afarad, zfarad, yfarad] # some powered units powered_units = [cmetre2, Yfarad3] # Combined units complex_units = [(kgram * metre2)/(amp * second3), 5 * (kgram * metre2)/(amp * second3), metre * second**-1, 10 * metre * second**-1, array([1, 2, 3]) * kmetre / second, np.ones(3) * nS / cm**2, Unit(1, dim=get_or_create_dimension(length=5, time=2)), 8000*umetre**3, [0.0001, 10000] * umetre**3, 1/metre, 1/(coulomb*metre**2), Unit(1)/second, 3.*mM, 5*mole/liter, 7*liter/meter3] unitless = [second/second, 5 * second/second, Unit(1)] for u in itertools.chain(units_which_should_exist, some_scaled_units, powered_units, complex_units, unitless): assert(len(str(u)) > 0) assert_allclose(eval(repr(u)), u) # test the `DIMENSIONLESS` object assert str(DIMENSIONLESS) == '1' assert repr(DIMENSIONLESS) == 'Dimension()' # test DimensionMismatchError (only that it works without raising an error for error in [DimensionMismatchError('A description'), DimensionMismatchError('A description', DIMENSIONLESS), DimensionMismatchError('A description', DIMENSIONLESS, second.dim)]: assert len(str(error)) assert len(repr(error))
def test_state_monitor_record_single_timestep(): G = NeuronGroup(1, 'dv/dt = -v/(5*ms) : 1') G.v = 1 mon = StateMonitor(G, 'v', record=True) # Recording before a run should not work assert_raises(TypeError, lambda: mon.record_single_timestep()) run(0.5*ms) mon.record_single_timestep() device.build(direct_call=False, **device.build_options) assert mon.t[0] == 0*ms assert mon[0].v[0] == 1 assert_allclose(mon.t[-1], 0.5*ms) assert len(mon.t) == 6 assert mon[0].v[-1] == G.v
def test_spikegenerator_period(): ''' Basic test for `SpikeGeneratorGroup`. ''' indices = np.array([3, 2, 1, 1, 2, 3, 3, 2, 1]) times = np.array([1, 4, 4, 3, 2, 4, 2, 3, 2]) * ms SG = SpikeGeneratorGroup(5, indices, times, period=5*ms) s_mon = SpikeMonitor(SG) run(10*ms) for idx in xrange(5): generator_spikes = sorted([(idx, time) for time in times[indices==idx]] + [(idx, time+5*ms) for time in times[indices==idx]]) recorded_spikes = sorted([(idx, time) for time in s_mon.t[s_mon.i==idx]]) assert_allclose(generator_spikes, recorded_spikes)
def test_spikegenerator_extreme_period(): ''' Basic test for `SpikeGeneratorGroup`. ''' indices = np.array([0, 1, 2]) times = np.array([0, 1, 2]) * ms SG = SpikeGeneratorGroup(5, indices, times, period=1e6*second) s_mon = SpikeMonitor(SG) with catch_logs() as l: run(10*ms) assert_equal(s_mon.i, np.array([0, 1, 2])) assert_allclose(s_mon.t, [0, 1, 2]*ms) assert len(l) == 1 and l[0][1].endswith('spikegenerator_long_period')
def test_long_timedarray(): ''' Use a very long timedarray (with a big dt), where the upsampling can lead to integer overflow. ''' ta = TimedArray(np.arange(16385), dt=1*second) G = NeuronGroup(1, 'value = ta(t) : 1') mon = StateMonitor(G, 'value', record=True) net = Network(G, mon) # We'll start the simulation close to the critical boundary # FIXME: setting the time like this does not work for standalone net.t_ = float(16384*second - 5*ms) net.run(10*ms) assert_allclose(mon[0].value[mon.t < 16384*second], 16383) assert_allclose(mon[0].value[mon.t >= 16384*second], 16384)
def test_state_variables_string_indices(): ''' Test accessing subgroups with string indices. ''' G = NeuronGroup(10, 'v : volt') SG = G[4:9] assert len(SG.v['i>3']) == 1 G.v = np.arange(10) * mV assert len(SG.v['v>7.5*mV']) == 1 # Combined string indexing and assignment SG.v['i > 3'] = 'i*10*mV' assert_allclose(G.v[:], [0, 1, 2, 3, 4, 5, 6, 7, 40, 9] * mV)
def test_rate_monitor_1(): G = NeuronGroup(5, 'v : 1', threshold='v>1') # no reset G.v = 1.1 # All neurons spike every time step rate_mon = PopulationRateMonitor(G) run(10*defaultclock.dt) assert_allclose(rate_mon.t, np.arange(10) * defaultclock.dt) assert_allclose(rate_mon.t_, np.arange(10) * defaultclock.dt_) assert_allclose(rate_mon.rate, np.ones(10) / defaultclock.dt) assert_allclose(rate_mon.rate_, np.asarray(np.ones(10) / defaultclock.dt_))
def test_timedarray_direct_use(): ta = TimedArray(np.linspace(0, 10, 11), 1*ms) assert ta(-1*ms) == 0 assert ta(5*ms) == 5 assert ta(10*ms) == 10 assert ta(15*ms) == 10 ta = TimedArray(np.linspace(0, 10, 11)*amp, 1*ms) assert ta(-1*ms) == 0*amp assert ta(5*ms) == 5*amp assert ta(10*ms) == 10*amp assert ta(15*ms) == 10*amp ta2d = TimedArray((np.linspace(0, 11, 12)*amp).reshape(4, 3), 1*ms) assert ta2d(-1*ms, 0) == 0*amp assert ta2d(0*ms, 0) == 0*amp assert ta2d(0*ms, 1) == 1*amp assert ta2d(1*ms, 1) == 4*amp assert_allclose(ta2d(1*ms, [0, 1, 2]), [3, 4, 5]*amp) assert_allclose(ta2d(15*ms, [0, 1, 2]), [9, 10, 11]*amp)
def test_synapses_state_monitor(): G = NeuronGroup(2, '') S = Synapses(G, G, 'w: siemens') S.connect(True) S.w = 'j*nS' # record from a Synapses object (all synapses connecting to neuron 1) synapse_mon = StateMonitor(S, 'w', record=S[:, 1]) synapse_mon2 = StateMonitor(S, 'w', record=S['j==1']) net = Network(G, S, synapse_mon, synapse_mon2) net.run(10*ms) # Synaptic variables assert_allclose(synapse_mon[S[0, 1]].w, 1*nS) assert_allclose(synapse_mon.w[1], 1*nS) assert_allclose(synapse_mon2[S[0, 1]].w, 1*nS) assert_allclose(synapse_mon2[S['i==0 and j==1']].w, 1*nS) assert_allclose(synapse_mon2.w[1], 1*nS)
def test_custom_events(): # Set (could be moved in a setup) EL = -65*mV gL = 0.0003*siemens/cm**2 ev = ''' Im = gL * (EL - v) : amp/meter**2 event_time1 : second ''' # Create a three compartments morphology morpho = Soma(diameter=10*um) morpho.dend1 = Cylinder(n=1, diameter=1*um, length=10*um ) morpho.dend2 = Cylinder(n=1, diameter=1*um, length=10*um ) G = SpatialNeuron(morphology=morpho, model=ev, events={'event1': 't>=i*ms and t<i*ms+dt'}) G.run_on_event('event1', 'event_time1 = 0.1*ms') run(0.2*ms) # Event has size three now because there are three compartments assert_allclose(G.event_time1[:], [0.1, 0, 0]*ms)
def test_constants(): import brian2.units.constants as constants # Check that the expected names exist and have the correct dimensions assert constants.avogadro_constant.dim == (1/mole).dim assert constants.boltzmann_constant.dim == (joule/kelvin).dim assert constants.electric_constant.dim == (farad/meter).dim assert constants.electron_mass.dim == kilogram.dim assert constants.elementary_charge.dim == coulomb.dim assert constants.faraday_constant.dim == (coulomb/mole).dim assert constants.gas_constant.dim == (joule/mole/kelvin).dim assert constants.magnetic_constant.dim == (newton/amp2).dim assert constants.molar_mass_constant.dim == (kilogram/mole).dim assert constants.zero_celsius.dim == kelvin.dim # Check the consistency between a few constants assert_allclose(constants.gas_constant, constants.avogadro_constant*constants.boltzmann_constant) assert_allclose(constants.faraday_constant, constants.avogadro_constant*constants.elementary_charge)
def test_rate_monitor_subgroups_2(): G = NeuronGroup(6, '''do_spike : boolean''', threshold='do_spike') G.do_spike = [True, False, False, False, True, True] rate_all = PopulationRateMonitor(G) rate_1 = PopulationRateMonitor(G[:2]) rate_2 = PopulationRateMonitor(G[2:4]) rate_3 = PopulationRateMonitor(G[4:]) run(2*defaultclock.dt) assert_allclose(rate_all.rate, 0.5 / defaultclock.dt) assert_allclose(rate_1.rate, 0.5 / defaultclock.dt) assert_allclose(rate_2.rate, 0*Hz) assert_allclose(rate_3.rate, 1 / defaultclock.dt)
def test_spatialneuron_capacitive_currents(): if prefs.core.default_float_dtype is np.float32: raise SkipTest('Need double precision for this test') defaultclock.dt = 0.1*ms morpho = Cylinder(x=[0, 10]*cm, diameter=2*238*um, n=200, type='axon') El = 10.613* mV ENa = 115*mV EK = -12*mV gl = 0.3*msiemens/cm**2 gNa0 = 120*msiemens/cm**2 gK = 36*msiemens/cm**2 # Typical equations eqs = ''' # The same equations for the whole neuron, but possibly different parameter values # distributed transmembrane current Im = gl * (El-v) + gNa * m**3 * h * (ENa-v) + gK * n**4 * (EK-v) : amp/meter**2 I : amp (point current) # applied current dm/dt = alpham * (1-m) - betam * m : 1 dn/dt = alphan * (1-n) - betan * n : 1 dh/dt = alphah * (1-h) - betah * h : 1 alpham = (0.1/mV) * (-v+25*mV) / (exp((-v+25*mV) / (10*mV)) - 1)/ms : Hz betam = 4 * exp(-v/(18*mV))/ms : Hz alphah = 0.07 * exp(-v/(20*mV))/ms : Hz betah = 1/(exp((-v+30*mV) / (10*mV)) + 1)/ms : Hz alphan = (0.01/mV) * (-v+10*mV) / (exp((-v+10*mV) / (10*mV)) - 1)/ms : Hz betan = 0.125*exp(-v/(80*mV))/ms : Hz gNa : siemens/meter**2 ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=1*uF/cm**2, Ri=35.4*ohm*cm, method="exponential_euler") mon = StateMonitor(neuron, ['Im', 'Ic'], record=True, when='end') run(10*ms) neuron.I[0] = 1*uA # current injection at one end run(3*ms) neuron.I = 0*amp run(10*ms) device.build(direct_call=False, **device.build_options) assert_allclose((mon.Im-mon.Ic).sum(axis=0)/(mA/cm**2), np.zeros(230), atol=1e6)
def test_infinitecable(): ''' Test simulation of an infinite cable vs. theory for current pulse (Green function) ''' BrianLogger.suppress_name('resolution_conflict') defaultclock.dt = 0.001*ms # Morphology diameter = 1*um Cm = 1 * uF / cm ** 2 Ri = 100 * ohm * cm N = 500 morpho=Cylinder(diameter=diameter,length=3*mm,n=N) # Passive channels gL=1e-4*siemens/cm**2 eqs=''' Im=-gL*v : amp/meter**2 I : amp (point current) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri) # Monitors mon=StateMonitor(neuron,'v',record=N/2-20) neuron.I[len(neuron)//2]=1*nA # injecting in the middle run(0.02*ms) neuron.I=0*amp run(3*ms) t = mon.t v = mon[N//2-20].v # Theory (incorrect near cable ends) x = 20*morpho.length[0] la = neuron.space_constant[0] taum = Cm/gL # membrane time constant theory = 1./(la*Cm*pi*diameter)*sqrt(taum/(4*pi*(t+defaultclock.dt)))*\ exp(-(t+defaultclock.dt)/taum-taum/(4*(t+defaultclock.dt))*(x/la)**2) theory = theory*1*nA*0.02*ms assert_allclose(v[t>0.5*ms],theory[t>0.5*ms], rtol=1e14, atol=1e10) # high error tolerance (not exact because not infinite cable)
def test_finitecable(): ''' Test simulation of short cylinder vs. theory for constant current. ''' if prefs.core.default_float_dtype is np.float32: raise SkipTest('Need double precision for this test') BrianLogger.suppress_name('resolution_conflict') defaultclock.dt = 0.01*ms # Morphology diameter = 1*um length = 300*um Cm = 1 * uF / cm ** 2 Ri = 150 * ohm * cm N = 200 morpho=Cylinder(diameter=diameter,length=length,n=N) # Passive channels gL=1e-4*siemens/cm**2 EL=-70*mV eqs=''' Im=gL*(EL-v) : amp/meter**2 I : amp (point current) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri) neuron.v = EL neuron.I[0]=0.02*nA # injecting at the left end run(100*ms) # Theory x = neuron.distance v = neuron.v la = neuron.space_constant[0] ra = la*4*Ri/(pi*diameter**2) theory = EL+ra*neuron.I[0]*cosh((length-x)/la)/sinh(length/la) assert_allclose(v-EL, theory-EL, rtol=1e12, atol=1e8)
def test_clip(): G = NeuronGroup(4, ''' clipexpr1 = clip(integer_var1, 0, 1) : integer clipexpr2 = clip(integer_var2, -0.5, 1.5) : integer clipexpr3 = clip(float_var1, 0, 1) : 1 clipexpr4 = clip(float_var2, -0.5, 1.5) : 1 integer_var1 : integer integer_var2 : integer float_var1 : 1 float_var2 : 1 ''') G.integer_var1 = [0, 1, -1, 2] G.integer_var2 = [0, 1, -1, 2] G.float_var1 = [0., 1., -1., 2.] G.float_var2 = [0., 1., -1., 2.] s_mon = StateMonitor(G, ['clipexpr1', 'clipexpr2', 'clipexpr3', 'clipexpr4'], record=True) run(defaultclock.dt) assert_equal(s_mon.clipexpr1.flatten(), [0, 1, 0, 1]) assert_equal(s_mon.clipexpr2.flatten(), [0, 1, 0, 1]) assert_allclose(s_mon.clipexpr3.flatten(), [0, 1, 0, 1]) assert_allclose(s_mon.clipexpr4.flatten(), [0, 1, -0.5, 1.5])
def test_conditional_write_automatic_and_manual(): source = NeuronGroup(1, '', threshold='True') # spiking all the time target = NeuronGroup( 2, '''dv/dt = 0/ms : 1 (unless refractory) dw/dt = 0/ms : 1''', threshold='t == 0*ms', refractory='False') # only refractory for the first time step # Cell is spiking/refractory only in the first time step syn = Synapses(source, target, on_pre='''v += 1 w += 1 * int(not_refractory_post)''' ) syn.connect() mon = StateMonitor(target, ['v', 'w'], record=True, when='end') run(2 * defaultclock.dt) # Synapse should not have been effective in the first time step assert_allclose(mon.v[:, 0], 0) assert_allclose(mon.v[:, 1], 1) assert_allclose(mon.w[:, 0], 0) assert_allclose(mon.w[:, 1], 1)
def test_rate_monitor_subgroups(): old_dt = defaultclock.dt defaultclock.dt = 0.01 * ms G = NeuronGroup(4, '''dv/dt = rate : 1 rate : Hz''', threshold='v>0.999', reset='v=0') G.rate = [100, 200, 400, 800] * Hz rate_all = PopulationRateMonitor(G) rate_1 = PopulationRateMonitor(G[:2]) rate_2 = PopulationRateMonitor(G[2:]) run(1 * second) assert_allclose(mean(G.rate_[:]), mean(rate_all.rate_[:])) assert_allclose(mean(G.rate_[:2]), mean(rate_1.rate_[:])) assert_allclose(mean(G.rate_[2:]), mean(rate_2.rate_[:])) defaultclock.dt = old_dt
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 + 1e-3*dt) < 5*ms', 'time_since_spike + 1e-3*dt < 5*ms', 'ref_subexpression', '(t-lastspike + 1e-3*dt) <= ref', 'ref', 'ref_no_unit*ms']: reinit_and_delete() 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('Assertion failed when using %r as refractory argument:\n%s' % (ref_time, ex))
def test_timedarray_no_units(): ta = TimedArray(np.arange(10), dt=0.1 * ms) G = NeuronGroup(1, 'value = ta(t) + 1: 1', dt=0.1 * ms) mon = StateMonitor(G, 'value', record=True, dt=0.1 * ms) run(1.1 * ms) assert_allclose(mon[0].value_, np.clip(np.arange(len(mon[0].t)), 0, 9) + 1)
def test_CudaSpikeQueue_push_outer_loop2(): # This test was testing a special case scenario in an old version of # CudaSpikeQueue::push(). It is now left here as a sanity check for spike # propagation. # This test creates a situation where one pre neuron is connected to 2048 # post neurons, where all synapses have different delays except of the the # two synapses with ids 1023 and 1024 (last thread of first loop cycle and # first thread of last loop cycle in CudaSpikeQueue::push()), which have # the same delay. Additionaly all delay queues have different size when the # pre neuron spikes one time. The different delay queue sizes is just a # leftover of another test and does not have any effect on this test, but # I'll leave it as an example. num_blocks = 1 prefs['devices.cuda_standalone.parallel_blocks'] = num_blocks # make sure we don't use less then 1024 threads due to register usage prefs['devices.cuda_standalone.cuda_backend.extra_compile_args_nvcc'] += [ '-maxrregcount=64' ] threads_per_block = 1024 neurons_per_block = 2 * threads_per_block N = neurons_per_block * num_blocks # each block gets 2048 synapses to push default_dt = defaultclock.dt # for the first connection each thread has a different delay per block of # postsynatpic neurons delays_one_block = arange(neurons_per_block) * default_dt delays0 = tile(delays_one_block, num_blocks) # for the second connection we want to trigger situation (a), so per block the # last thread of first loop and first thread of last loop have the same delay delays_one_block[threads_per_block] = delays_one_block[threads_per_block - 1] delays = tile(delays_one_block, num_blocks) # inp neuron 0 fires the first (2*threads_per_block) time steps, s.t. all queues # have different size after that (first 2048, last 0) # inp neuron 1 fires once after that indices = zeros(neurons_per_block + 1) indices[-1] = 1 times = arange(neurons_per_block + 1) * default_dt inp = SpikeGeneratorGroup(2, indices, times) G = NeuronGroup(N, 'v:1', threshold='v>1', reset='v=0') S = Synapses(inp, G, 'w:1', on_pre='v+=w') S.connect() # inp neuron 0 has no effect on target neurons (weight 0) S.w['i == 0'] = 0 # inp neuron 1 makes G neurons spike S.w['i == 1'] = 1.1 # delays from inp neuron 0 are equal to post neuron number j S.delay[ 'i == 0'] = '(j % neurons_per_block) * default_dt' # arange(2048) per block # delays from inp neuron 0 are the same except of the delay for the synapse # of the thread of the second loop in the spikequeue.push(), which is the # same as the previous synapse S.delay[ 'i == 1'] = '(j % neurons_per_block) * default_dt' # arange(2048), but [..., 1023, 1023, 1025, ...] S.delay[ 'i == 1 and j % neurons_per_block == threads_per_block'] = '(threads_per_block - 1) * default_dt' mon = SpikeMonitor(G) # first neurons_per_block time steps we resize queues # at time step neurons_per_block + 1 we send a spike that triggeres # postsynaptic spikes with delays = arange(neurons_per_block), but [..., 1023, 1023, 1035, ...] # then it takes neurons_per_block - 1 time steps for all effects to be applied time_steps = 2 * neurons_per_block + 2 run(time_steps * default_dt) assert_allclose(S.delay[0, :], delays0) assert_allclose(S.delay[1, :], delays) assert_equal(len(mon), N) unique_mon_t = unique(mon.t) for n in range(time_steps): t = n * default_dt if n <= neurons_per_block or n == time_steps - 1: # no spikes assert t not in unique_mon_t else: try: mon_idx = sort(mon.i[mon.t == t]) # first neuron (idx 0) spikes at time step neurons_per_block + 1 spiking_neuron_idx = n - (neurons_per_block + 1) # neuron indices increment over blocks, get idx starting at 0 per block mon_idx_per_block = mod(mon_idx, neurons_per_block) assert_array_equal(sort(mon_idx), mon_idx) if spiking_neuron_idx == threads_per_block - 1: # 1023 # [1023, 1024, 1023, 1024, ...] expected_indices = tile( [spiking_neuron_idx, spiking_neuron_idx + 1], num_blocks) elif spiking_neuron_idx == threads_per_block: # 1024 # [] expected_indices = array([]) else: # [spiking_neuron_idx, spiking_neuron_idx, ...] expected_indices = tile([spiking_neuron_idx], num_blocks) assert_equal(mon_idx_per_block, expected_indices) except AssertionError: print('n =', n) print(mon_idx) raise
def test_spatialneuron_morphology_assignment(): sec = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1.sec11 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1.sec12 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec2 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec2.sec21 = Cylinder(length=50 * um, diameter=10 * um, n=2) neuron = SpatialNeuron(sec, 'Im = 0*amp/meter**2 : amp/meter**2') neuron.v[sec.sec1.sec11] = 1 * volt assert_allclose(neuron.sec1.sec11.v[:], np.ones(2) * volt) assert_allclose(neuron.sec1.sec12.v[:], np.zeros(2) * volt) assert_allclose(neuron.sec1.main.v[:], np.zeros(2) * volt) assert_allclose(neuron.main.v[:], np.zeros(2) * volt) assert_allclose(neuron.sec2.v[:], np.zeros(4) * volt) neuron.v[sec.sec2[25 * um:]] = 2 * volt neuron.v[sec.sec1[:25 * um]] = 3 * volt assert_allclose(neuron.main.v[:], np.zeros(2) * volt) assert_allclose(neuron.sec2.main.v[:], [0, 2] * volt) assert_allclose(neuron.sec2.sec21.v[:], np.zeros(2) * volt) assert_allclose(neuron.sec1.main.v[:], [3, 0] * volt) assert_allclose(neuron.sec1.sec11.v[:], np.ones(2) * volt) assert_allclose(neuron.sec1.sec12.v[:], np.zeros(2) * volt)
def test_construction_coordinates(): # Same as test_construction, but uses coordinates instead of lengths to # set up everything # Note that all coordinates here are relative to the origin of the # respective cylinder BrianLogger.suppress_name('resolution_conflict') morpho = Soma(diameter=30 * um) morpho.L = Cylinder(x=[0, 10] * um, diameter=1 * um, n=10) morpho.LL = Cylinder(y=[0, 5] * um, diameter=2 * um, n=5) morpho.LR = Cylinder(z=[0, 5] * um, diameter=2 * um, n=10) morpho.right = Cylinder(x=[0, sqrt(2) * 1.5] * um, y=[0, sqrt(2) * 1.5] * um, diameter=1 * um, n=7) morpho.right.nextone = Cylinder(y=[0, sqrt(2)] * um, z=[0, sqrt(2)] * um, diameter=1 * um, n=3) gL = 1e-4 * siemens / cm**2 EL = -70 * mV eqs = ''' Im=gL*(EL-v) : amp/meter**2 I : meter (point current) ''' # Check units of currents with pytest.raises(DimensionMismatchError): SpatialNeuron(morphology=morpho, model=eqs) eqs = ''' Im=gL*(EL-v) : amp/meter**2 ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=1 * uF / cm**2, Ri=100 * ohm * cm) # Test initialization of values neuron.LL.v = EL assert_allclose(neuron.L.main.v, 0 * mV) assert_allclose(neuron.LL.v, EL) neuron.LL[1 * um:3 * um].v = 0 * mV assert_allclose(neuron.LL.v, Quantity([EL, 0 * mV, 0 * mV, EL, EL])) assert_allclose(neuron.Cm, 1 * uF / cm**2) # Test morphological variables assert_allclose(neuron.L.main.x, morpho.L.x) assert_allclose(neuron.LL.main.x, morpho.LL.x) assert_allclose(neuron.right.main.x, morpho.right.x) assert_allclose(neuron.L.main.distance, morpho.L.distance) # assert_allclose(neuron.L.main.diameter, morpho.L.diameter) assert_allclose(neuron.L.main.area, morpho.L.area) assert_allclose(neuron.L.main.length, morpho.L.length) # Check basic consistency of the flattened representation assert all(neuron.diffusion_state_updater._ends[:].flat >= neuron.diffusion_state_updater._starts[:].flat) # Check that length and distances make sense assert_allclose(sum(morpho.L.length), 10 * um) assert_allclose(morpho.L.distance, (0.5 + np.arange(10)) * um) assert_allclose(sum(morpho.LL.length), 5 * um) assert_allclose(morpho.LL.distance, (10 + .5 + np.arange(5)) * um) assert_allclose(sum(morpho.LR.length), 5 * um) assert_allclose(morpho.LR.distance, (10 + 0.25 + np.arange(10) * 0.5) * um) assert_allclose(sum(morpho.right.length), 3 * um) assert_allclose(morpho.right.distance, (0.5 + np.arange(7)) * 3. / 7. * um) assert_allclose(sum(morpho.right.nextone.length), 2 * um) assert_allclose(morpho.right.nextone.distance, 3 * um + (0.5 + np.arange(3)) * 2. / 3. * um)
def test_rall(): ''' Test simulation of a cylinder plus two branches, with diameters according to Rall's formula ''' if prefs.core.default_float_dtype is np.float32: pytest.skip('Need double precision for this test') BrianLogger.suppress_name('resolution_conflict') defaultclock.dt = 0.01 * ms # Passive channels gL = 1e-4 * siemens / cm**2 EL = -70 * mV # Morphology diameter = 1 * um length = 300 * um Cm = 1 * uF / cm**2 Ri = 150 * ohm * cm N = 500 rm = 1 / (gL * pi * diameter) # membrane resistance per unit length ra = (4 * Ri) / (pi * diameter**2) # axial resistance per unit length la = sqrt(rm / ra) # space length morpho = Cylinder(diameter=diameter, length=length, n=N) d1 = 0.5 * um L1 = 200 * um rm = 1 / (gL * pi * d1) # membrane resistance per unit length ra = (4 * Ri) / (pi * d1**2) # axial resistance per unit length l1 = sqrt(rm / ra) # space length morpho.L = Cylinder(diameter=d1, length=L1, n=N) d2 = (diameter**1.5 - d1**1.5)**(1. / 1.5) rm = 1 / (gL * pi * d2) # membrane resistance per unit length ra = (4 * Ri) / (pi * d2**2) # axial resistance per unit length l2 = sqrt(rm / ra) # space length L2 = (L1 / l1) * l2 morpho.R = Cylinder(diameter=d2, length=L2, n=N) eqs = ''' Im=gL*(EL-v) : amp/meter**2 I : amp (point current) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri) neuron.v = EL neuron.I[0] = 0.02 * nA # injecting at the left end run(100 * ms) # Check space constant calculation assert_allclose(la, neuron.space_constant[0]) assert_allclose(l1, neuron.L.space_constant[0]) assert_allclose(l2, neuron.R.space_constant[0]) # Theory x = neuron.main.distance ra = la * 4 * Ri / (pi * diameter**2) l = length / la + L1 / l1 theory = EL + ra * neuron.I[0] * cosh(l - x / la) / sinh(l) v = neuron.main.v assert_allclose(v - EL, theory - EL, rtol=1e12, atol=1e8) x = neuron.L.distance theory = EL + ra * neuron.I[0] * cosh(l - neuron.main.distance[-1] / la - (x - neuron.main.distance[-1]) / l1) / sinh(l) v = neuron.L.v assert_allclose(v - EL, theory - EL, rtol=1e12, atol=1e8) x = neuron.R.distance theory = EL + ra * neuron.I[0] * cosh(l - neuron.main.distance[-1] / la - (x - neuron.main.distance[-1]) / l2) / sinh(l) v = neuron.R.v assert_allclose(v - EL, theory - EL, rtol=1e12, atol=1e8)
def test_spatialneuron_subtree_assignment(): sec = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1.sec11 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec1.sec12 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec2 = Cylinder(length=50 * um, diameter=10 * um, n=2) sec.sec2.sec21 = Cylinder(length=50 * um, diameter=10 * um, n=2) neuron = SpatialNeuron(sec, 'Im = 0*amp/meter**2 : amp/meter**2') neuron.v = 1 * volt assert_allclose(neuron.v[:], np.ones(12) * volt) neuron.sec1.v += 1 * volt assert_allclose(neuron.main.v[:], np.ones(2) * volt) assert_allclose(neuron.sec1.v[:], np.ones(6) * 2 * volt) assert_allclose(neuron.sec1.main.v[:], np.ones(2) * 2 * volt) assert_allclose(neuron.sec1.sec11.v[:], np.ones(2) * 2 * volt) assert_allclose(neuron.sec1.sec12.v[:], np.ones(2) * 2 * volt) assert_allclose(neuron.sec2.v[:], np.ones(4) * volt) neuron.sec2.v = 5 * volt assert_allclose(neuron.sec2.v[:], np.ones(4) * 5 * volt) assert_allclose(neuron.sec2.main.v[:], np.ones(2) * 5 * volt) assert_allclose(neuron.sec2.sec21.v[:], np.ones(2) * 5 * volt)
def test_rallpack2(): ''' Rallpack 2 ''' if prefs.core.default_float_dtype is np.float32: pytest.skip('Need double precision for this test') defaultclock.dt = 0.1 * ms # Morphology diameter = 32 * um length = 16 * um Cm = 1 * uF / cm**2 Ri = 100 * ohm * cm # Construct binary tree according to Rall's formula morpho = Cylinder(n=1, diameter=diameter, y=[0, float(length)] * meter) endpoints = {morpho} for depth in range(1, 10): diameter /= 2.**(1. / 3.) length /= 2.**(2. / 3.) new_endpoints = set() for endpoint in endpoints: new_L = Cylinder(n=1, diameter=diameter, length=length) new_R = Cylinder(n=1, diameter=diameter, length=length) new_endpoints.add(new_L) new_endpoints.add(new_R) endpoint.L = new_L endpoint.R = new_R endpoints = new_endpoints # Passive channels gL = 1. / (40000 * ohm * cm**2) EL = -65 * mV eqs = ''' Im = gL*(EL - v) : amp/meter**2 I : amp (point current, constant) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri, method='rk4') neuron.v = EL neuron.I[0] = 0.1 * nA # injecting at the origin endpoint_indices = [endpoint.indices[0] for endpoint in endpoints] mon = StateMonitor(neuron, 'v', record=[0] + endpoint_indices, when='start', dt=0.1 * ms) run(250 * ms + defaultclock.dt) # Load the theoretical results basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rallpack_data') # Only use very second time step, since we run with 0.1ms instead of 0.05ms data_0 = np.loadtxt(os.path.join(basedir, 'ref_branch.0'))[::2] data_x = np.loadtxt(os.path.join(basedir, 'ref_branch.x'))[::2] # sanity check: times are the same assert_allclose(mon.t / second, data_0[:, 0]) assert_allclose(mon.t / second, data_x[:, 0]) # Check that all endpoints are the same: for endpoint in endpoints: assert_allclose(mon[endpoint].v, mon[endpoint[0]].v) scale_0 = max(data_0[:, 1] * volt) - min(data_0[:, 1] * volt) scale_x = max(data_x[:, 1] * volt) - min(data_x[:, 1] * volt) squared_diff_0 = (data_0[:, 1] * volt - mon[0].v)**2 # One endpoint squared_diff_x = (data_x[:, 1] * volt - mon[endpoint_indices[0]].v)**2 rel_RMS_0 = sqrt(mean(squared_diff_0)) / scale_0 rel_RMS_x = sqrt(mean(squared_diff_x)) / scale_x max_rel_0 = sqrt(max(squared_diff_0)) / scale_0 max_rel_x = sqrt(max(squared_diff_x)) / scale_x # RMS error should be < 0.25%, maximum error along the curve should be < 0.5% assert 100 * rel_RMS_0 < 0.25 assert 100 * rel_RMS_x < 0.25 assert 100 * max_rel_0 < 0.5 assert 100 * max_rel_x < 0.5
def test_rallpack3(): ''' Rallpack 3 ''' if prefs.core.default_float_dtype is np.float32: pytest.skip('Need double precision for this test') defaultclock.dt = 1 * usecond # Morphology diameter = 1 * um length = 1 * mm N = 1000 morpho = Cylinder(diameter=diameter, length=length, n=N) # Passive properties gl = 1. / (40000 * ohm * cm**2) El = -65 * mV Cm = 1 * uF / cm**2 Ri = 100 * ohm * cm # Active properties ENa = 50 * mV EK = -77 * mV gNa = 120 * msiemens / cm**2 gK = 36 * msiemens / cm**2 eqs = ''' Im = gl * (El-v) + gNa * m**3 * h * (ENa-v) + gK * n**4 * (EK-v) : amp/meter**2 dm/dt = alpham * (1-m) - betam * m : 1 dn/dt = alphan * (1-n) - betan * n : 1 dh/dt = alphah * (1-h) - betah * h : 1 v_shifted = v - El : volt alpham = (0.1/mV) * (-v_shifted+25*mV) / (exp((-v_shifted+25*mV) / (10*mV)) - 1)/ms : Hz betam = 4 * exp(-v_shifted/(18*mV))/ms : Hz alphah = 0.07 * exp(-v_shifted/(20*mV))/ms : Hz betah = 1/(exp((-v_shifted+30*mV) / (10*mV)) + 1)/ms : Hz alphan = (0.01/mV) * (-v_shifted+10*mV) / (exp((-v_shifted+10*mV) / (10*mV)) - 1)/ms : Hz betan = 0.125*exp(-v_shifted/(80*mV))/ms : Hz I : amp (point current, constant) ''' axon = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri, method='exponential_euler') axon.v = El # Pre-calculated equilibrium values at v = El axon.m = 0.0529324852572 axon.n = 0.317676914061 axon.h = 0.596120753508 axon.I[0] = 0.1 * nA # injecting at the left end #Record at the two ends mon = StateMonitor(axon, 'v', record=[0, 999], when='start', dt=0.05 * ms) run(250 * ms + defaultclock.dt) # Load the theoretical results basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rallpack_data') data_0 = np.loadtxt(os.path.join(basedir, 'ref_axon.0.neuron')) data_x = np.loadtxt(os.path.join(basedir, 'ref_axon.x.neuron')) # sanity check: times are the same assert_allclose(mon.t / second, data_0[:, 0]) assert_allclose(mon.t / second, data_x[:, 0]) scale_0 = max(data_0[:, 1] * volt) - min(data_0[:, 1] * volt) scale_x = max(data_x[:, 1] * volt) - min(data_x[:, 1] * volt) squared_diff_0 = (data_0[:, 1] * volt - mon[0].v)**2 squared_diff_x = (data_x[:, 1] * volt - mon[999].v)**2 rel_RMS_0 = sqrt(mean(squared_diff_0)) / scale_0 rel_RMS_x = sqrt(mean(squared_diff_x)) / scale_x max_rel_0 = sqrt(max(squared_diff_0)) / scale_0 max_rel_x = sqrt(max(squared_diff_x)) / scale_x # RMS error should be < 0.1%, maximum error along the curve should be < 0.5% # Note that this is much stricter than the original Rallpack evaluation, but # with the 1us time step, the voltage traces are extremely similar assert 100 * rel_RMS_0 < 0.1 assert 100 * rel_RMS_x < 0.1 assert 100 * max_rel_0 < 0.5 assert 100 * max_rel_x < 0.5
def test_scale(): # Check that unit scaling is implemented correctly from brian2.core.namespace import DEFAULT_UNITS siprefixes = { "y": 1e-24, "z": 1e-21, "a": 1e-18, "f": 1e-15, "p": 1e-12, "n": 1e-9, "u": 1e-6, "m": 1e-3, "": 1.0, "k": 1e3, "M": 1e6, "G": 1e9, "T": 1e12, "P": 1e15, "E": 1e18, "Z": 1e21, "Y": 1e24 } for prefix in siprefixes: if prefix in ['c', 'd', 'da', 'h']: continue scaled_unit = DEFAULT_UNITS[prefix + 'meter'] assert_allclose(float(scaled_unit), siprefixes[prefix]) assert_allclose(5 * scaled_unit / meter, 5 * siprefixes[prefix]) scaled_unit = DEFAULT_UNITS[prefix + 'meter2'] assert_allclose(float(scaled_unit), siprefixes[prefix]**2) assert_allclose(5 * scaled_unit / meter2, 5 * siprefixes[prefix]**2) scaled_unit = DEFAULT_UNITS[prefix + 'meter3'] assert_allclose(float(scaled_unit), siprefixes[prefix]**3) assert_allclose(5 * scaled_unit / meter3, 5 * siprefixes[prefix]**3) # liter, gram, and molar are special, they are not base units with a # value of one, even though they do not have any prefix for unit, factor in [('liter', 1e-3), ('litre', 1e-3), ('gram', 1e-3), ('gramme', 1e-3), ('molar', 1e3)]: base_unit = DEFAULT_UNITS[unit] scaled_unit = DEFAULT_UNITS[prefix + unit] assert_allclose(float(scaled_unit), siprefixes[prefix] * factor) assert_allclose(5 * scaled_unit / base_unit, 5 * siprefixes[prefix])
def test_array_cache(): # Check that variables are only accessible from Python when they should be set_device('cpp_standalone', build_on_run=False) G = NeuronGroup(10, '''dv/dt = -v / (10*ms) : 1 w : 1 x : 1 y : 1 z : 1 (shared)''', threshold='v>1') S = Synapses(G, G, 'weight: 1', on_pre='w += weight') S.connect(p=0.2) S.weight = 7 # All neurongroup values should be known assert_allclose(G.v, 0) assert_allclose(G.w, 0) assert_allclose(G.x, 0) assert_allclose(G.y, 0) assert_allclose(G.z, 0) assert_allclose(G.i, np.arange(10)) # But the synaptic variable is not -- we don't know the number of synapses with pytest.raises(NotImplementedError): S.weight[:] # Setting variables with explicit values should not change anything G.v = np.arange(10) + 1 G.w = 2 G.y = 5 G.z = 7 assert_allclose(G.v, np.arange(10) + 1) assert_allclose(G.w, 2) assert_allclose(G.y, 5) assert_allclose(G.z, 7) # But setting with code should invalidate them G.x = 'i*2' with pytest.raises(NotImplementedError): G.x[:] # Make sure that the array cache does not allow to use incorrectly sized # values to pass with pytest.raises(ValueError): setattr(G, 'w', [0, 2]) with pytest.raises(ValueError): G.w.__setitem__(slice(0, 4), [0, 2]) run(10 * ms) # v is now no longer known without running the network with pytest.raises(NotImplementedError): G.v[:] # Neither is w, it is updated in the synapse with pytest.raises(NotImplementedError): G.w[:] # However, no code touches y or z assert_allclose(G.y, 5) assert_allclose(G.z, 7) # i is read-only anyway assert_allclose(G.i, np.arange(10)) # After actually running the network, everything should be accessible device.build(directory=None, with_output=False) assert all(G.v > 0) assert all(G.w > 0) assert_allclose(G.x, np.arange(10) * 2) assert_allclose(G.y, 5) assert_allclose(G.z, 7) assert_allclose(G.i, np.arange(10)) assert_allclose(S.weight, 7)
def test_math_functions(): ''' Test that math functions give the same result, regardless of whether used directly or in generated Python or C++ code. ''' default_dt = defaultclock.dt test_array = np.array([-1, -0.5, 0, 0.5, 1]) def int_(x): return array(x, dtype=int) int_.__name__ = 'int' with catch_logs() as _: # Let's suppress warnings about illegal values # Functions with a single argument for func in [ cos, tan, sinh, cosh, tanh, arcsin, arccos, arctan, log, log10, exp, np.sqrt, np.ceil, np.floor, np.sign, int_ ]: # Calculate the result directly numpy_result = func(test_array) # Calculate the result in a somewhat complicated way by using a # subexpression in a NeuronGroup if func.__name__ == 'absolute': # we want to use the name abs instead of absolute func_name = 'abs' else: func_name = func.__name__ G = NeuronGroup( len(test_array), '''func = {func}(variable) : 1 variable : 1'''.format(func=func_name)) G.variable = test_array mon = StateMonitor(G, 'func', record=True) net = Network(G, mon) net.run(default_dt) assert_allclose( numpy_result, mon.func_.flatten(), err_msg='Function %s did not return the correct values' % func.__name__) # Functions/operators scalar = 3 for func, operator in [(np.power, '**'), (np.mod, '%')]: # Calculate the result directly numpy_result = func(test_array, scalar) # Calculate the result in a somewhat complicated way by using a # subexpression in a NeuronGroup G = NeuronGroup( len(test_array), '''func = variable {op} scalar : 1 variable : 1'''.format(op=operator)) G.variable = test_array mon = StateMonitor(G, 'func', record=True) net = Network(G, mon) net.run(default_dt) assert_allclose( numpy_result, mon.func_.flatten(), err_msg='Function %s did not return the correct values' % func.__name__)
def test_rate_monitor_1(): G = NeuronGroup(5, 'v : 1', threshold='v>1') # no reset G.v = 1.1 # All neurons spike every time step rate_mon = PopulationRateMonitor(G) run(10 * defaultclock.dt) assert_allclose(rate_mon.t, np.arange(10) * defaultclock.dt) assert_allclose(rate_mon.t_, np.arange(10) * defaultclock.dt_) assert_allclose(rate_mon.t, np.arange(10) * defaultclock.dt) assert_allclose(rate_mon.rate, np.ones(10) / defaultclock.dt) assert_allclose(rate_mon.rate_, np.asarray(np.ones(10) / defaultclock.dt_)) # Check that indexing into the VariableView works (this fails if we do not # update the N variable correctly) assert_allclose(rate_mon.t[:5], np.arange(5) * defaultclock.dt)
def test_construction(): BrianLogger.suppress_name('resolution_conflict') morpho = Soma(diameter=30*um) morpho.L = Cylinder(length=10*um, diameter=1*um, n=10) morpho.LL = Cylinder(length=5*um, diameter=2*um, n=5) morpho.LR = Cylinder(length=5*um, diameter=2*um, n=10) morpho.right = Cylinder(length=3*um, diameter=1*um, n=7) morpho.right.nextone = Cylinder(length=2*um, diameter=1*um, n=3) gL=1e-4*siemens/cm**2 EL=-70*mV eqs=""" Im=gL*(EL-v) : amp/meter**2 I : meter (point current) """ # Check units of currents with pytest.raises(DimensionMismatchError): SpatialNeuron(morphology=morpho, model=eqs) eqs=""" Im=gL*(EL-v) : amp/meter**2 """ neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=1 * uF / cm ** 2, Ri=100 * ohm * cm) # Test initialization of values neuron.LL.v = EL assert_allclose(neuron.L.main.v, 0*mV) assert_allclose(neuron.LL.v, EL) neuron.LL[1*um:3*um].v = 0*mV assert_allclose(neuron.LL.v, Quantity([EL, 0*mV, 0*mV, EL, EL])) assert_allclose(neuron.Cm, 1 * uF / cm ** 2) # Test morphological variables assert_allclose(neuron.L.main.distance, morpho.L.distance) assert_allclose(neuron.L.main.area, morpho.L.area) assert_allclose(neuron.L.main.length, morpho.L.length) # Check basic consistency of the flattened representation assert all(neuron.diffusion_state_updater._ends[:].flat >= neuron.diffusion_state_updater._starts[:].flat) # Check that length and distances make sense assert_allclose(sum(morpho.L.length), 10*um) assert_allclose(morpho.L.distance, (0.5 + np.arange(10))*um) assert_allclose(sum(morpho.LL.length), 5*um) assert_allclose(morpho.LL.distance, (10 + .5 + np.arange(5))*um) assert_allclose(sum(morpho.LR.length), 5*um) assert_allclose(morpho.LR.distance, (10 + 0.25 + np.arange(10)*0.5)*um) assert_allclose(sum(morpho.right.length), 3*um) assert_allclose(morpho.right.distance, (0.5 + np.arange(7))*3./7.*um) assert_allclose(sum(morpho.right.nextone.length), 2*um) assert_allclose(morpho.right.nextone.distance, 3*um + (0.5 + np.arange(3))*2./3.*um)
def test_atomics_parallelisation(): # Adapted from brian2.test_synapses:test_ufunc_at_vectorisation() for n, code in enumerate(permutation_analysis_good_examples): should_be_able_to_use_ufunc_at = not 'NOT_UFUNC_AT_VECTORISABLE' in code if should_be_able_to_use_ufunc_at: use_ufunc_at_list = [False, True] else: use_ufunc_at_list = [True] code = deindent(code) vars = get_identifiers(code) vars_src = [] vars_tgt = [] vars_syn = [] vars_shared = [] vars_const = {} for var in vars: if var.endswith('_pre'): vars_src.append(var[:-4]) elif var.endswith('_post'): vars_tgt.append(var[:-5]) elif var.endswith('_syn'): vars_syn.append(var[:-4]) elif var.endswith('_shared'): vars_shared.append(var[:-7]) elif var.endswith('_const'): vars_const[var[:-6]] = 42 eqs_src = '\n'.join(var + ':1' for var in vars_src) eqs_tgt = '\n'.join(var + ':1' for var in vars_tgt) eqs_syn = '\n'.join(var + ':1' for var in vars_syn) eqs_syn += '\n' + '\n'.join(var + ':1 (shared)' for var in vars_shared) origvals = {} endvals = {} group_size = 1000 syn_size = group_size**2 try: BrianLogger._log_messages.clear() with catch_logs(log_level=logging.INFO) as caught_logs: for use_ufunc_at in use_ufunc_at_list: set_device('cuda_standalone', directory=None, compile=True, run=True, debug=False) CUDACodeGenerator._use_atomics = use_ufunc_at src = NeuronGroup(group_size, eqs_src, threshold='True', name='src') tgt = NeuronGroup(group_size, eqs_tgt, name='tgt') syn = Synapses(src, tgt, eqs_syn, on_pre=code.replace('_syn', '').replace( '_const', '').replace('_shared', ''), name='syn', namespace=vars_const) syn.connect() for G, vars in [(src, vars_src), (tgt, vars_tgt), (syn, vars_syn)]: for var in vars: fullvar = var + G.name if fullvar in origvals: G.state(var)[:] = origvals[fullvar] else: if isinstance(G, Synapses): val = rand(syn_size) else: val = rand(len(G)) G.state(var)[:] = val origvals[fullvar] = val.copy() Network(src, tgt, syn).run(5 * defaultclock.dt) for G, vars in [(src, vars_src), (tgt, vars_tgt), (syn, vars_syn)]: for var in vars: fullvar = var + G.name val = G.state(var)[:].copy() if fullvar in endvals: assert_allclose(val, endvals[fullvar], err_msg='%d: %s' % (n, code), rtol=1e-5) else: endvals[fullvar] = val device.reinit() device.activate() cuda_generator_messages = [ l for l in caught_logs if l[1] == 'brian2.codegen.generators.cuda_generator' ] if should_be_able_to_use_ufunc_at: assert len( cuda_generator_messages) == 0, cuda_generator_messages else: assert len( cuda_generator_messages) == 1, cuda_generator_messages log_lev, log_mod, log_msg = cuda_generator_messages[0] assert log_msg.startswith( 'Failed to parallelise code'), log_msg finally: CUDACodeGenerator._use_atomics = False #restore it device.reinit() device.activate()
def test_default_function_implementations(): ''' Test that all default functions work as expected ''' # NeuronGroup variables are set in device code # Synapses are generated in host code # int function arguments use the template code # double arguments use the overloaded function ### sin # Promotes int to float in template device code G = NeuronGroup(1, 'v: 1') G.v = 'sin(i)' # Uses int overloaded C++ function in template host code S = Synapses(G, G, 'w: 1') S.connect(condition='sin(i)==0') # Uses double overloaded func (device code) G1 = NeuronGroup(1, 'v: 1') G1.v = 'sin(i*1.0)' # Uses double overloaded func (host code) S1 = Synapses(G, G, 'w: 1') S1.connect(condition='sin(i*1.0)==0') ### cos G2 = NeuronGroup(1, 'v: 1') G2.v = 'cos(i)' S2 = Synapses(G, G, 'w: 1') S2.connect(condition='cos(i)==1') G3 = NeuronGroup(1, 'v: 1') G3.v = 'cos(i*1.0)' S3 = Synapses(G, G, 'w: 1') S3.connect(condition='cos(i*1.0)==1') ### tan G4 = NeuronGroup(1, 'v: 1') G4.v = 'tan(i)' S4 = Synapses(G, G, 'w: 1') S4.connect(condition='tan(i)==0') G5 = NeuronGroup(1, 'v: 1') G5.v = 'tan(i*1.0)' S5 = Synapses(G, G, 'w: 1') S5.connect(condition='tan(i*1.0)==0') ### sinh G6 = NeuronGroup(1, 'v: 1') G6.v = 'sinh(i)' S6 = Synapses(G, G, 'w: 1') S6.connect(condition='sinh(i)==0') G7 = NeuronGroup(1, 'v: 1') G7.v = 'sinh(i*1.0)' S7 = Synapses(G, G, 'w: 1') S7.connect(condition='sinh(i*1.0)==0') ### cosh G8 = NeuronGroup(1, 'v: 1') G8.v = 'cosh(i)' S8 = Synapses(G, G, 'w: 1') S8.connect(condition='cosh(i)==1') G9 = NeuronGroup(1, 'v: 1') G9.v = 'cosh(i*1.0)' S9 = Synapses(G, G, 'w: 1') S9.connect(condition='cosh(i*1.0)==1') ### tanh G10 = NeuronGroup(1, 'v: 1') G10.v = 'tanh(i)' S10 = Synapses(G, G, 'w: 1') S10.connect(condition='tanh(i)==0') G11 = NeuronGroup(1, 'v: 1') G11.v = 'tanh(i*1.0)' S11 = Synapses(G, G, 'w: 1') S11.connect(condition='tanh(i*1.0)==0') ### exp G12 = NeuronGroup(1, 'v: 1') G12.v = 'exp(i)' S12 = Synapses(G, G, 'w: 1') S12.connect(condition='exp(i)==1') G13 = NeuronGroup(1, 'v: 1') G13.v = 'exp(i*1.0)' S13 = Synapses(G, G, 'w: 1') S13.connect(condition='exp(i*1.0)==1') ### log G14 = NeuronGroup(1, 'v: 1') G14.v = 'log(i+1)' S14 = Synapses(G, G, 'w: 1') S14.connect(condition='log(i+1)==0') G15 = NeuronGroup(1, 'v: 1') G15.v = 'log(i+1.0)' S15 = Synapses(G, G, 'w: 1') S15.connect(condition='log(i+1.0)==0') ### log10 G16 = NeuronGroup(1, 'v: 1') G16.v = 'log10(i+1)' S16 = Synapses(G, G, 'w: 1') S16.connect(condition='log10(i+1)==0') G17 = NeuronGroup(1, 'v: 1') G17.v = 'log10(i+1.0)' S17 = Synapses(G, G, 'w: 1') S17.connect(condition='log10(i+1.0)==0') ### sqrt G18 = NeuronGroup(1, 'v: 1') G18.v = 'sqrt(i)' S18 = Synapses(G, G, 'w: 1') S18.connect(condition='sqrt(i)==0') G19 = NeuronGroup(1, 'v: 1') G19.v = 'sqrt(i*1.0)' S19 = Synapses(G, G, 'w: 1') S19.connect(condition='sqrt(i*1.0)==0') ### ceil G20 = NeuronGroup(1, 'v: 1') G20.v = 'ceil(i)' S20 = Synapses(G, G, 'w: 1') S20.connect(condition='ceil(i)==0') G21 = NeuronGroup(1, 'v: 1') G21.v = 'ceil(i*1.0)' S21 = Synapses(G, G, 'w: 1') S21.connect(condition='ceil(i*1.0)==0') ### floor G22 = NeuronGroup(1, 'v: 1') G22.v = 'floor(i)' S22 = Synapses(G, G, 'w: 1') S22.connect(condition='floor(i)==0') G23 = NeuronGroup(1, 'v: 1') G23.v = 'floor(i*1.0)' S23 = Synapses(G, G, 'w: 1') S23.connect(condition='floor(i*1.0)==0') ### arcsin G24 = NeuronGroup(1, 'v: 1') G24.v = 'arcsin(i)' S24 = Synapses(G, G, 'w: 1') S24.connect(condition='arcsin(i)==0') G25 = NeuronGroup(1, 'v: 1') G25.v = 'arcsin(i*1.0)' S25 = Synapses(G, G, 'w: 1') S25.connect(condition='arcsin(i*1.0)==0') ### arccos G26 = NeuronGroup(1, 'v: 1') G26.v = 'arccos(i+1)' S26 = Synapses(G, G, 'w: 1') S26.connect(condition='arccos(i+1)==0') G27 = NeuronGroup(1, 'v: 1') G27.v = 'arccos(i+1.0)' S27 = Synapses(G, G, 'w: 1') S27.connect(condition='arccos(i+1.0)==0') ### arctan G28 = NeuronGroup(1, 'v: 1') G28.v = 'arctan(i)' S28 = Synapses(G, G, 'w: 1') S28.connect(condition='arctan(i)==0') G29 = NeuronGroup(1, 'v: 1') G29.v = 'arctan(i*1.0)' S29 = Synapses(G, G, 'w: 1') S29.connect(condition='arctan(i*1.0)==0') ### abs G30 = NeuronGroup(1, 'v: 1') G30.v = 'abs(i-1)' S30 = Synapses(G, G, 'w: 1') S30.connect(condition='abs(i-1)==1') G31 = NeuronGroup(1, 'v: 1') G31.v = 'abs(i-1.0)' S31 = Synapses(G, G, 'w: 1') S31.connect(condition='abs(i-1.0)==1') ### int G32 = NeuronGroup(1, 'v: 1') G32.v = 'int(i>0)' S32 = Synapses(G, G, 'w: 1') S32.connect(condition='int(i>0)==0') G33 = NeuronGroup(1, 'v: 1') G33.v = 'int(i+0.1)' S33 = Synapses(G, G, 'w: 1') S33.connect(condition='int(i+0.1)==0') ### clip G34 = NeuronGroup(1, 'v: 1') G34.v = 'clip(i+1,-1,0)' S34 = Synapses(G, G, 'w: 1') S34.connect(condition='clip(i+1,-1,0)==0') G35 = NeuronGroup(1, 'v: 1') G35.v = 'clip(i+1,-1.0,0.0)' S35 = Synapses(G, G, 'w: 1') S35.connect(condition='clip(i+1,-1.0,0.0)==0') ### sign G36 = NeuronGroup(1, 'v: 1') G36.v = 'sign(i+1)' S36 = Synapses(G, G, 'w: 1') S36.connect(condition='sign(i+1)==1') ### timestep G38 = NeuronGroup(1, 'v: 1') G38.v = 'timestep(0.1*ms, 0.001*ms)' S38 = Synapses(G, G, 'w: 1') S38.connect(condition='timestep(0.1*ms, 0.001*ms) == 100') run(0 * ms) assert_allclose([G38.v[0]], [100]) assert_allclose([S38.N[:]], [1]) assert_allclose([G36.v[0]], [1]) assert_allclose([S36.N[:]], [1]) assert_allclose([G34.v[0], G35.v[0]], [0, 0]) assert_allclose([S34.N[:], S35.N[:]], [1, 1]) assert_allclose([G32.v[0], G33.v[0]], [0, 0]) assert_allclose([S32.N[:], S33.N[:]], [1, 1]) assert_allclose([G30.v[0], G31.v[0]], [1, 1]) assert_allclose([S30.N[:], S31.N[:]], [1, 1]) assert_allclose([G28.v[0], G29.v[0]], [0, 0]) assert_allclose([S28.N[:], S29.N[:]], [1, 1]) assert_allclose([G26.v[0], G27.v[0]], [0, 0]) assert_allclose([S26.N[:], S27.N[:]], [1, 1]) assert_allclose([G24.v[0], G25.v[0]], [0, 0]) assert_allclose([S24.N[:], S25.N[:]], [1, 1]) assert_allclose([G22.v[0], G23.v[0]], [0, 0]) assert_allclose([S22.N[:], S23.N[:]], [1, 1]) assert_allclose([G20.v[0], G21.v[0]], [0, 0]) assert_allclose([S20.N[:], S21.N[:]], [1, 1]) assert_allclose([G18.v[0], G19.v[0]], [0, 0]) assert_allclose([S18.N[:], S19.N[:]], [1, 1]) assert_allclose([G16.v[0], G17.v[0]], [0, 0]) assert_allclose([S16.N[:], S17.N[:]], [1, 1]) assert_allclose([G14.v[0], G15.v[0]], [0, 0]) assert_allclose([S14.N[:], S15.N[:]], [1, 1]) assert_allclose([G12.v[0], G13.v[0]], [1, 1]) assert_allclose([S12.N[:], S13.N[:]], [1, 1]) assert_allclose([G10.v[0], G11.v[0]], [0, 0]) assert_allclose([S10.N[:], S11.N[:]], [1, 1]) assert_allclose([G8.v[0], G9.v[0]], [1, 1]) assert_allclose([S8.N[:], S9.N[:]], [1, 1]) assert_allclose([G6.v[0], G7.v[0]], [0, 0]) assert_allclose([S6.N[:], S7.N[:]], [1, 1]) assert_allclose([G4.v[0], G5.v[0]], [0, 0]) assert_allclose([S4.N[:], S5.N[:]], [1, 1]) assert_allclose([G2.v[0], G3.v[0]], [1, 1]) assert_allclose([S2.N[:], S3.N[:]], [1, 1]) assert_allclose([G.v[0], G1.v[0]], [0, 0]) assert_allclose([S.N[:], S1.N[:]], [1, 1])
def test_binomial_values(): if prefs.core.default_float_dtype is np.float32: # TODO: Make test single-precision compatible, see #262 pytest.skip('Need double precision for this test') # On Denis' local computer this test blows up all available RAM + SWAP when # compiling with all threads in parallel. Use half the threads instead. import socket if socket.gethostname() == 'selene': prefs.devices.cpp_standalone.extra_make_args_unix = ['-j4'] my_f_approximated = BinomialFunction(100, 0.1, approximate=True) my_f = BinomialFunction(100, 0.1, approximate=False) # Test neurongroup every tick objects G = NeuronGroup(10, '''dx/dt = my_f_approximated()/ms: 1 dy/dt = my_f()/ms: 1''', threshold='True') G.run_regularly('''x = my_f_approximated() y = my_f()''') # Test synapses every tick objects (where N is not known at compile time) syn = Synapses( G, G, model=''' dw/dt = my_f()/ms: 1 dv/dt = my_f_approximated()/ms: 1 ''', on_pre=''' x += w y += v ''' # TODO: fails when having binomial here, why? #on_pre=''' # x += w * my_f() # y += v * my_f_approximated() # ''' ) # Test synapses generation, which needs host side binomial syn.connect(condition='my_f_approximated() < 100') mon = StateMonitor(G, ['x', 'y'], record=True) w_mon = StateMonitor(syn, ['w', 'v'], record=np.arange(100)) def init_group_variables(my_f, my_f_approximated): # Test codeobjects run only once outside the network, # G.x uses group_variable_set_conditional, G.x[:5] uses group_variable_set # Synapse objects (N not known at compile time) syn.w = 'my_f()' syn.w['i < j'] = 'my_f_approximated()' syn.v = 'my_f_approximated()' syn.v['i < j'] = 'my_f()' # Neurongroup object G.x = 'my_f_approximated()' G.x[:5] = 'my_f()' G.y = 'my_f()' G.y[:5] = 'my_f_approximated()' # first run init_group_variables(my_f, my_f_approximated) run(2 * defaultclock.dt) # second run seed(11400) init_group_variables(my_f, my_f_approximated) run(2 * defaultclock.dt) # third run seed() init_group_variables(my_f, my_f_approximated) run(2 * defaultclock.dt) # forth run seed(11400) init_group_variables(my_f, my_f_approximated) run(2 * defaultclock.dt) device.build(direct_call=False, **device.build_options) run_values_1 = np.vstack([ mon.x[:, [0, 1]], mon.y[:, [0, 1]], w_mon.w[:, [0, 1]], w_mon.v[:, [0, 1]] ]) run_values_2 = np.vstack([ mon.x[:, [2, 3]], mon.y[:, [2, 3]], w_mon.w[:, [2, 3]], w_mon.v[:, [2, 3]] ]) run_values_3 = np.vstack([ mon.x[:, [4, 5]], mon.y[:, [4, 5]], w_mon.w[:, [4, 5]], w_mon.v[:, [4, 5]] ]) run_values_4 = np.vstack([ mon.x[:, [6, 7]], mon.y[:, [6, 7]], w_mon.w[:, [6, 7]], w_mon.v[:, [6, 7]] ]) # Two calls to binomial functions should return different numbers in all runs for n, values in enumerate( [run_values_1, run_values_2, run_values_3, run_values_4]): with pytest.raises(AssertionError): assert_allclose(values[:, 0], values[:, 1]) # 2. and 4. run set the same seed assert_allclose(run_values_2, run_values_4) # all other combinations should be different with pytest.raises(AssertionError): assert_allclose(run_values_1, run_values_2) with pytest.raises(AssertionError): assert_allclose(run_values_1, run_values_3) with pytest.raises(AssertionError): assert_allclose(run_values_2, run_values_3)
def test_rallpack1(): ''' Rallpack 1 ''' if prefs.core.default_float_dtype is np.float32: pytest.skip('Need double precision for this test') defaultclock.dt = 0.05 * ms # Morphology diameter = 1 * um length = 1 * mm Cm = 1 * uF / cm**2 Ri = 100 * ohm * cm N = 1000 morpho = Cylinder(diameter=diameter, length=length, n=N) # Passive channels gL = 1. / (40000 * ohm * cm**2) EL = -65 * mV eqs = ''' Im = gL*(EL - v) : amp/meter**2 I : amp (point current, constant) ''' neuron = SpatialNeuron(morphology=morpho, model=eqs, Cm=Cm, Ri=Ri) neuron.v = EL neuron.I[0] = 0.1 * nA # injecting at the left end #Record at the two ends mon = StateMonitor(neuron, 'v', record=[0, 999], when='start', dt=0.05 * ms) run(250 * ms + defaultclock.dt) # Load the theoretical results basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rallpack_data') data_0 = np.loadtxt(os.path.join(basedir, 'ref_cable.0')) data_x = np.loadtxt(os.path.join(basedir, 'ref_cable.x')) scale_0 = max(data_0[:, 1] * volt) - min(data_0[:, 1] * volt) scale_x = max(data_x[:, 1] * volt) - min(data_x[:, 1] * volt) squared_diff_0 = (data_0[:, 1] * volt - mon[0].v)**2 squared_diff_x = (data_x[:, 1] * volt - mon[999].v)**2 rel_RMS_0 = sqrt(mean(squared_diff_0)) / scale_0 rel_RMS_x = sqrt(mean(squared_diff_x)) / scale_x max_rel_0 = sqrt(max(squared_diff_0)) / scale_0 max_rel_x = sqrt(max(squared_diff_x)) / scale_x # sanity check: times are the same assert_allclose(mon.t / second, data_0[:, 0]) assert_allclose(mon.t / second, data_x[:, 0]) # RMS error should be < 0.1%, maximum error along the curve should be < 0.5% assert 100 * rel_RMS_0 < 0.1 assert 100 * rel_RMS_x < 0.1 assert 100 * max_rel_0 < 0.5 assert 100 * max_rel_x < 0.5
def test_spike_monitor(): G = NeuronGroup(3, '''dv/dt = rate : 1 rate: Hz''', threshold='v>1', reset='v=0') # We don't use 100 and 1000Hz, because then the membrane potential would # be exactly at 1 after 10 resp. 100 timesteps. Due to floating point # issues this will not be exact, G.rate = [101, 0, 1001] * Hz mon = SpikeMonitor(G) with pytest.raises(ValueError): SpikeMonitor(G, order=1) # need to specify 'when' as well # Creating a SpikeMonitor for a Synapses object should not work S = Synapses(G, G, on_pre='v += 0') S.connect() with pytest.raises(TypeError): SpikeMonitor(S) run(10 * ms) spike_trains = mon.spike_trains() assert_allclose(mon.t[mon.i == 0], [9.9] * ms) assert len(mon.t[mon.i == 1]) == 0 assert_allclose(mon.t[mon.i == 2], np.arange(10) * ms + 0.9 * ms) assert_allclose(mon.t_[mon.i == 0], np.array([9.9 * float(ms)])) assert len(mon.t_[mon.i == 1]) == 0 assert_allclose(mon.t_[mon.i == 2], (np.arange(10) + 0.9) * float(ms)) assert_allclose(spike_trains[0], [9.9] * ms) assert len(spike_trains[1]) == 0 assert_allclose(spike_trains[2], np.arange(10) * ms + 0.9 * ms) assert_array_equal(mon.count, np.array([1, 0, 10])) i, t = mon.it i_, t_ = mon.it_ assert_array_equal(i, mon.i) assert_array_equal(i, i_) assert_array_equal(t, mon.t) assert_array_equal(t_, mon.t_) with pytest.raises(KeyError): spike_trains[3] with pytest.raises(KeyError): spike_trains[-1] with pytest.raises(KeyError): spike_trains['string'] # Check that indexing into the VariableView works (this fails if we do not # update the N variable correctly) assert_allclose(mon.t[:5], [0.9, 1.9, 2.9, 3.9, 4.9] * ms)
def test_openmp_consistency(): previous_device = get_device() n_cells = 100 n_recorded = 10 numpy.random.seed(42) taum = 20 * ms taus = 5 * ms Vt = -50 * mV Vr = -60 * mV El = -49 * mV fac = (60 * 0.27 / 10) gmax = 20 * fac dApre = .01 taupre = 20 * ms taupost = taupre dApost = -dApre * taupre / taupost * 1.05 dApost *= 0.1 * gmax dApre *= 0.1 * gmax connectivity = numpy.random.randn(n_cells, n_cells) sources = numpy.random.randint(0, n_cells - 1, 10 * n_cells) # Only use one spike per time step (to rule out that a single source neuron # has more than one spike in a time step) times = numpy.random.choice( numpy.arange(10 * n_cells), 10 * n_cells, replace=False) * ms v_init = Vr + numpy.random.rand(n_cells) * (Vt - Vr) eqs = Equations(''' dv/dt = (g-(v-El))/taum : volt dg/dt = -g/taus : volt ''') results = {} for (n_threads, devicename) in [(0, 'runtime'), (0, 'cpp_standalone'), (1, 'cpp_standalone'), (2, 'cpp_standalone'), (3, 'cpp_standalone'), (4, 'cpp_standalone')]: set_device(devicename, build_on_run=False, with_output=False) Synapses.__instances__().clear() if devicename == 'cpp_standalone': reinit_and_delete() prefs.devices.cpp_standalone.openmp_threads = n_threads P = NeuronGroup(n_cells, model=eqs, threshold='v>Vt', reset='v=Vr', refractory=5 * ms) Q = SpikeGeneratorGroup(n_cells, sources, times) P.v = v_init P.g = 0 * mV S = Synapses(P, P, model='''dApre/dt=-Apre/taupre : 1 (event-driven) dApost/dt=-Apost/taupost : 1 (event-driven) w : 1''', pre='''g += w*mV Apre += dApre w = w + Apost''', post='''Apost += dApost w = w + Apre''') S.connect() S.w = fac * connectivity.flatten() T = Synapses(Q, P, model="w : 1", on_pre="g += w*mV") T.connect(j='i') T.w = 10 * fac spike_mon = SpikeMonitor(P) rate_mon = PopulationRateMonitor(P) state_mon = StateMonitor(S, 'w', record=np.arange(n_recorded), dt=0.1 * second) v_mon = StateMonitor(P, 'v', record=np.arange(n_recorded)) run(0.2 * second, report='text') if devicename == 'cpp_standalone': device.build(directory=None, with_output=False) results[n_threads, devicename] = {} results[n_threads, devicename]['w'] = state_mon.w results[n_threads, devicename]['v'] = v_mon.v results[n_threads, devicename]['s'] = spike_mon.num_spikes results[n_threads, devicename]['r'] = rate_mon.rate[:] for key1, key2 in [((0, 'runtime'), (0, 'cpp_standalone')), ((1, 'cpp_standalone'), (0, 'cpp_standalone')), ((2, 'cpp_standalone'), (0, 'cpp_standalone')), ((3, 'cpp_standalone'), (0, 'cpp_standalone')), ((4, 'cpp_standalone'), (0, 'cpp_standalone'))]: assert_allclose(results[key1]['w'], results[key2]['w']) assert_allclose(results[key1]['v'], results[key2]['v']) assert_allclose(results[key1]['r'], results[key2]['r']) assert_allclose(results[key1]['s'], results[key2]['s']) reset_device(previous_device)
def test_state_variables(): ''' Test the setting and accessing of state variables in subgroups. ''' G = NeuronGroup(10, 'v : volt') SG = G[4:9] with pytest.raises(DimensionMismatchError): SG.__setattr__('v', -70) SG.v_ = float(-80 * mV) assert_allclose(G.v, np.array([0, 0, 0, 0, -80, -80, -80, -80, -80, 0]) * mV) assert_allclose(SG.v, np.array([-80, -80, -80, -80, -80]) * mV) assert_allclose( G.v_, np.array([0, 0, 0, 0, -80, -80, -80, -80, -80, 0]) * float(mV)) assert_allclose(SG.v_, np.array([-80, -80, -80, -80, -80]) * float(mV)) # You should also be able to set variables with a string SG.v = 'v + i*mV' assert_allclose(SG.v[0], -80 * mV) assert_allclose(SG.v[4], -76 * mV) assert_allclose(G.v[4:9], -80 * mV + np.arange(5) * mV) # Calculating with state variables should work too assert all(G.v[4:9] - SG.v == 0) # And in-place modification should work as well SG.v += 10 * mV assert_allclose(G.v[4:9], -70 * mV + np.arange(5) * mV) SG.v *= 2 assert_allclose(G.v[4:9], 2 * (-70 * mV + np.arange(5) * mV)) # with unit checking with pytest.raises(DimensionMismatchError): SG.v.__iadd__(3 * second) with pytest.raises(DimensionMismatchError): SG.v.__iadd__(3) with pytest.raises(DimensionMismatchError): SG.v.__imul__(3 * second) # Indexing with subgroups assert_equal(G.v[SG], SG.v[:])
def test_spike_monitor_subgroups(): G = NeuronGroup(6, '''do_spike : boolean''', threshold='do_spike') G.do_spike = [True, False, False, False, True, True] spikes_all = SpikeMonitor(G) spikes_1 = SpikeMonitor(G[:2]) spikes_2 = SpikeMonitor(G[2:4]) spikes_3 = SpikeMonitor(G[4:]) run(defaultclock.dt) assert_allclose(spikes_all.i, [0, 4, 5]) assert_allclose(spikes_all.t, [0, 0, 0] * ms) assert_allclose(spikes_1.i, [0]) assert_allclose(spikes_1.t, [0] * ms) assert len(spikes_2.i) == 0 assert len(spikes_2.t) == 0 assert_allclose(spikes_3.i, [0, 1]) # recorded spike indices are relative assert_allclose(spikes_3.t, [0, 0] * ms)
def test_time_after_run(): set_device('cpp_standalone', build_on_run=False) # Check that the clock and network time after a run is correct, even if we # have not actually run the code yet (via build) G = NeuronGroup(10, 'dv/dt = -v/(10*ms) : 1') net = Network(G) assert_allclose(defaultclock.dt, 0.1 * ms) assert_allclose(defaultclock.t, 0. * ms) assert_allclose(G.t, 0. * ms) assert_allclose(net.t, 0. * ms) net.run(10 * ms) assert_allclose(defaultclock.t, 10. * ms) assert_allclose(G.t, 10. * ms) assert_allclose(net.t, 10. * ms) net.run(10 * ms) assert_allclose(defaultclock.t, 20. * ms) assert_allclose(G.t, 20. * ms) assert_allclose(net.t, 20. * ms) device.build(directory=None, with_output=False) # Everything should of course still be accessible assert_allclose(defaultclock.t, 20. * ms) assert_allclose(G.t, 20. * ms) assert_allclose(net.t, 20. * ms) reset_device()
def test_event_monitor(): G = NeuronGroup(3, '''dv/dt = rate : 1 rate: Hz''', events={'my_event': 'v>1'}) G.run_on_event('my_event', 'v=0') # We don't use 100 and 1000Hz, because then the membrane potential would # be exactly at 1 after 10 resp. 100 timesteps. Due to floating point # issues this will not be exact, G.rate = [101, 0, 1001] * Hz mon = EventMonitor(G, 'my_event') net = Network(G, mon) net.run(10 * ms) event_trains = mon.event_trains() assert_allclose(mon.t[mon.i == 0], [9.9] * ms) assert len(mon.t[mon.i == 1]) == 0 assert_allclose(mon.t[mon.i == 2], np.arange(10) * ms + 0.9 * ms) assert_allclose(mon.t_[mon.i == 0], np.array([9.9 * float(ms)])) assert len(mon.t_[mon.i == 1]) == 0 assert_allclose(mon.t_[mon.i == 2], (np.arange(10) + 0.9) * float(ms)) assert_allclose(event_trains[0], [9.9] * ms) assert len(event_trains[1]) == 0 assert_allclose(event_trains[2], np.arange(10) * ms + 0.9 * ms) assert_array_equal(mon.count, np.array([1, 0, 10])) i, t = mon.it i_, t_ = mon.it_ assert_array_equal(i, mon.i) assert_array_equal(i, i_) assert_array_equal(t, mon.t) assert_array_equal(t_, mon.t_) with pytest.raises(KeyError): event_trains[3] with pytest.raises(KeyError): event_trains[-1] with pytest.raises(KeyError): event_trains['string']
def test_storing_loading(): set_device('cpp_standalone', build_on_run=False) G = NeuronGroup( 10, '''v : volt x : 1 n : integer b : boolean''') v = np.arange(10) * volt x = np.arange(10, 20) n = np.arange(20, 30) b = np.array([True, False]).repeat(5) G.v = v G.x = x G.n = n G.b = b S = Synapses( G, G, '''v_syn : volt x_syn : 1 n_syn : integer b_syn : boolean''') S.connect(j='i') S.v_syn = v S.x_syn = x S.n_syn = n S.b_syn = b run(0 * ms) device.build(directory=None, with_output=False) assert_allclose(G.v[:], v) assert_allclose(S.v_syn[:], v) assert_allclose(G.x[:], x) assert_allclose(S.x_syn[:], x) assert_allclose(G.n[:], n) assert_allclose(S.n_syn[:], n) assert_allclose(G.b[:], b) assert_allclose(S.b_syn[:], b) reset_device()
def test_state_monitor(): # Unique name to get the warning even for repeated runs of the test unique_name = 'neurongroup_' + str(uuid.uuid4()).replace('-', '_') # Check that all kinds of variables can be recorded G = NeuronGroup(2, '''dv/dt = -v / (10*ms) : 1 f = clip(v, 0.1, 0.9) : 1 rate: Hz''', threshold='v>1', reset='v=0', refractory=2 * ms, name=unique_name) G.rate = [100, 1000] * Hz G.v = 1 S = Synapses(G, G, 'w: siemens') S.connect(True) S.w = 'j*nS' # A bit peculiar, but in principle one should be allowed to record # nothing except for the time nothing_mon = StateMonitor(G, [], record=True) no_record = StateMonitor(G, 'v', record=False) # Use a single StateMonitor v_mon = StateMonitor(G, 'v', record=True) v_mon1 = StateMonitor(G, 'v', record=[1]) # Use a StateMonitor for specified variables multi_mon = StateMonitor(G, ['v', 'f', 'rate'], record=True) multi_mon1 = StateMonitor(G, ['v', 'f', 'rate'], record=[1]) # Use a StateMonitor recording everything all_mon = StateMonitor(G, True, record=True) # Record synapses with explicit indices (the only way allowed in standalone) synapse_mon = StateMonitor(S, 'w', record=np.arange(len(G)**2)) run(10 * ms) # Check time recordings assert_array_equal(nothing_mon.t, v_mon.t) assert_array_equal(nothing_mon.t_, np.asarray(nothing_mon.t)) assert_array_equal(nothing_mon.t_, v_mon.t_) assert_allclose(nothing_mon.t, np.arange(len(nothing_mon.t)) * defaultclock.dt) assert_array_equal(no_record.t, v_mon.t) # Check v recording assert_allclose(v_mon.v.T, np.exp(np.tile(-v_mon.t, (2, 1)).T / (10 * ms))) assert_allclose(v_mon.v_.T, np.exp(np.tile(-v_mon.t_, (2, 1)).T / float(10 * ms))) assert_array_equal(v_mon.v, multi_mon.v) assert_array_equal(v_mon.v_, multi_mon.v_) assert_array_equal(v_mon.v, all_mon.v) assert_array_equal(v_mon.v[1:2], v_mon1.v) assert_array_equal(multi_mon.v[1:2], multi_mon1.v) assert len(no_record.v) == 0 # Other variables assert_array_equal( multi_mon.rate_.T, np.tile(np.atleast_2d(G.rate_), (multi_mon.rate.shape[1], 1))) assert_array_equal(multi_mon.rate[1:2], multi_mon1.rate) assert_allclose(np.clip(multi_mon.v, 0.1, 0.9), multi_mon.f) assert_allclose(np.clip(multi_mon1.v, 0.1, 0.9), multi_mon1.f) assert all(all_mon[0].not_refractory[:] == True), ( 'not_refractory should ' 'be True, but got' '(not_refractory, v):' '%s ' % str((all_mon.not_refractory, all_mon.v))) # Synapses assert_allclose(synapse_mon.w[:], np.tile(S.j[:] * nS, (synapse_mon.w[:].shape[1], 1)).T)