Brian 2 documentation
For the main documentation about defining neural models, see the document :doc:`../../user/models`.
The syntax for specifying neuron models in a NeuronGroup changed in several
details. In general, a string-based syntax (that was already optional in Brian 1)
consistently replaces the use of classes (e.g. VariableThreshold
) or
guessing (e.g. which variable does threshold=50*mV
check).
String-based thresholds are now the only possible option and replace all the methods of defining threshold/reset in Brian 1:
Brian 1 | Brian 2 |
---|---|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold=-50*mV,
reset=-70*mV)
|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold='v > -50*mV',
reset='v = -70*mV')
|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold=Threshold(-50*mV, state='v'),
reset=Reset(-70*mV, state='w'))
|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold='v > -50*mV',
reset='v = -70*mV')
|
group = NeuronGroup(N, '''dv/dt = -v / tau : volt
dvt/dt = -vt / tau : volt
vr : volt''',
threshold=VariableThreshold(state='v',
threshold_state='vt'),
reset=VariableThreshold(state='v',
resetvaluestate='vr'))
|
group = NeuronGroup(N, '''dv/dt = -v / tau : volt
dvt/dt = -vt / tau : volt
vr : volt''',
threshold='v > vt',
reset='v = vr')
|
group = NeuronGroup(N, 'rate : Hz',
threshold=PoissonThreshold(state='rate'))
|
group = NeuronGroup(N, 'rate : Hz',
threshold='rand()<rate*dt')
|
There's no direct equivalent for the "functional threshold/reset" mechanism from
Brian 1. In simple cases, they can be implemented using the general string
expression/statement mechanism (note that in Brian 1, reset=myreset
is
equivalent to reset=FunReset(myreset)
):
Brian 1 | Brian 2 |
---|---|
def myreset(P,spikes):
P.v_[spikes] = -70*mV+rand(len(spikes))*5*mV
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold=-50*mV,
reset=myreset)
|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold='v > -50*mV',
reset='-70*mV + rand()*5*mV')
|
def mythreshold(v):
return (v > -50*mV) & (rand(N) > 0.5)
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold=SimpleFunThreshold(mythreshold,
state='v'),
reset=-70*mV)
|
group = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold='v > -50*mV and rand() > 0.5',
reset='v = -70*mV')
|
For more complicated cases, you can use the general mechanism for
:ref:`user_functions` that Brian 2 provides. The only caveat is that you'd have
to provide an implementation of the function in the code generation target
language which is by default C++ or Cython. However, in the default
:ref:`runtime` mode, you can chose different code generation targets for
different parts of your simulation. You can thus switch the code generation
target for the threshold/reset mechanism to numpy
while leaving the default
target for the rest of the simulation in place. The details of this process and
the correct definition of the functions (e.g. global_reset
needs a "dummy"
return value) are somewhat cumbersome at the moment and we plan to make them
more straightforward in the future. Also note that if you use this kind of
mechanism extensively, you'll lose all the performance advantage that Brian 2's
code generation mechanism provides (in addition to not being able to use
:ref:`cpp_standalone` mode at all).
Brian 1 | Brian 2 |
---|---|
def single_threshold(v):
# Only let a single neuron spike
crossed_threshold = np.nonzero(v > -50*mV)[0]
should_spike = np.zeros(len(P), dtype=np.bool)
if len(crossed_threshold):
choose = np.random.randint(len(crossed_threshold))
should_spike[crossed_threshold[choose]] = True
return should_spike
def global_reset(P, spikes):
# Reset everything
if len(spikes):
P.v_[:] = -70*mV
neurons = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold=SimpleFunThreshold(single_threshold,
state='v'),
reset=global_reset)
|
@check_units(v=volt, result=bool)
def single_threshold(v):
pass # ... (identical to Brian 1)
@check_units(spikes=1, result=1)
def global_reset(spikes):
# Reset everything
if len(spikes):
neurons.v_[:] = -0.070
neurons = NeuronGroup(N, 'dv/dt = -v / tau : volt',
threshold='single_threshold(v)',
reset='dummy = global_reset(i)')
# Set the code generation target for threshold/reset only:
neuron.thresholder['spike'].codeobj_class = NumpyCodeObject
neuron.resetter['spike'].codeobj_class = NumpyCodeObject
|
For an example how to translate EmpiricalThreshold
, see the section on
"Refractoriness" below.
For a detailed description of Brian 2's refractoriness mechanism see :doc:`../../user/refractoriness`.
In Brian 1, refractoriness was tightly linked with the reset mechanism and
some combinations of refractoriness and reset were not allowed. The standard
refractory mechanism had two effects during the refractoriness: it prevented the
refractory cell from spiking and it clamped a state variable (normally the
membrane potential of the cell). In Brian 2, refractoriness is independent of
reset and the two effects are specified separately: the refractory
keyword
specifies the time (or an expression evaluating to a time) during which the
cell does not spike, and the (unless refractory)
flag marks one or more
variables to be clamped during the refractory period. To correctly translate
the standard refractory mechanism from Brian 1, you'll therefore need to
specify both:
Brian 1 | Brian 2 |
---|---|
group = NeuronGroup(N, 'dv/dt = (I - v)/tau : volt',
threshold=-50*mV,
reset=-70*mV,
refractory=3*ms)
|
group = NeuronGroup(N, 'dv/dt = (I - v)/tau : volt (unless refractory)',
threshold='v > -50*mV',
reset='v = -70*mV',
refractory=3*ms)
|
More complex refractoriness mechanisms based on SimpleCustomRefractoriness
and CustomRefractoriness
can be translatated using string expressions or
user-defined functions, see the remarks in the preceding section on "Threshold
and Reset".
Brian 2 no longer has an equivalent to the EmpiricalThreshold
class (which
detects at the first threshold crossing but ignores all following threshold
crossings for a certain time after that). However, the standard refractoriness
mechanism can be used to implement the same behaviour, since it does not
reset/clamp any value if not explicitly asked for it (which would be fatal for
Hodgkin-Huxley type models):
Brian 1 | Brian 2 |
---|---|
group = NeuronGroup(N,'''
dv/dt = (I_L - I_Na - I_K + I)/Cm : volt
...''',
threshold=EmpiricalThreshold(threshold=20*mV,
refractory=1*ms,
state='v'))
|
group = NeuronGroup(N,'''
dv/dt = (I_L - I_Na - I_K + I)/Cm : volt
...''',
threshold='v > -20*mV',
refractory=1*ms)
|
The class NeuronGroup in Brian 2 does no longer provide a subgroup
method,
the only way to construct subgroups is therefore the slicing syntax (that works
in the same way as in Brian 1):
Brian 1 | Brian 2 |
---|---|
group = NeuronGroup(4000, ...)
group_exc = group.subgroup(3200)
group_inh = group.subgroup(800)
|
group = NeuronGroup(4000, ...)
group_exc = group[:3200]
group_inh = group[3200:]
|
For a description of Brian 2's mechanism to link variables between groups, see :ref:`linked_variables`.
Linked variables need to be explicitly annotated with the (linked)
flag in
Brian 2:
Brian 1 | Brian 2 |
---|---|
group1 = NeuronGroup(N,
'dv/dt = -v / tau : volt')
group2 = NeuronGroup(N,
'''dv/dt = (-v + w) / tau : volt
w : volt''')
group2.w = linked_var(group1, 'v')
|
group1 = NeuronGroup(N,
'dv/dt = -v / tau : volt')
group2 = NeuronGroup(N,
'''dv/dt = (-v + w) / tau : volt
w : volt (linked)''')
group2.w = linked_var(group1, 'v')
|