KeyError: 'The identifier "a" could not be resolved.' - Izhikevich Model

Description of problem

I am hoping to build a small-worlds network of Izhikevich neurons. I already have a function that defines a connectivity matrix and I have a separate function that creates the network. However, I am running into a problem and I am unsure how to debug this. Apologies if this is blatantly obvious, I am very new to Brian2

Minimal code to reproduce problem

# create a network of izhikevich  neurons in brian2
def create_network(connectivity_matrix):
    start_scope()

    N = len(connectivity_matrix)

    # params
    a = 0.02
    b = 0.2
    c = -65
    d = 8

    izhikevich_eqs = '''
    dv/dt = (0.04 * v**2 + 5 * v + 140 - u + I)/ms : 1 (unless refractory)
    du/dt = a * (b * v - u)/ms : 1
    I_syn : 1
    '''



    # define neuron group
    G = NeuronGroup(N, model=izhikevich_eqs, threshold='v >= 30', reset='v = c; u = u + d', refractory=2*ms)

    # set initial conditions
    G.v = -65
    G.u = b * G.v



    # define synaptic connections
    S = Synapses(G, G, 'w : 1', on_pre='I_syn_post += w')
    S.connect(i=np.where(connectivity_matrix)[0],
              j=np.where(connectivity_matrix)[1])
    
    S.w = 'rand() * 2 - 1'
    
    return G, S

def run_simulation(G, S, duration=100*ms):
    # Start a new scope for the simulation
    start_scope()

    
    state_monitor = StateMonitor(G, 'v', record=True)

    # Include the StateMonitor in the network
    net = Network(G, S, state_monitor)

    
    net.run(duration)

    
    for i in range(len(G)):
        plt.plot(state_monitor.t, state_monitor.v[i], label=f'Neuron {i}')

    plt.xlabel('Time (ms)')
    plt.ylabel('Membrane Potential')
    plt.legend()
    plt.show()

What you have aready tried

I have tried defining the parameter values (a,b,c,d) in the variable izhikevich_eqs, such that:
izhikevich_eqs = ‘’’
dv/dt = (0.04 * v**2 + 5 * v + 140 - u + I)/ms : 1 (unless refractory)
du/dt = a * (b * v - u)/ms : 1
I_syn : 1
a = 0.02
b = 0.2
c = -65
d = 8
‘’’

Expected output (if relevant)

Actual output (if relevant)

Full traceback of error (if relevant)

ERROR      Brian 2 encountered an unexpected error. If you think this is a bug in Brian 2, please report this issue either to the discourse forum at <http://brian.discourse.group/>, or to the issue tracker at <https://github.com/brian-team/brian2/issues>. Please include this file with debug information in your report: /tmp/brian_debug_i547thrf.log  Additionally, you can also include a copy of the script that was run, available at: /tmp/brian_script_yo2y24s2.py You can also include a copy of the redirected std stream outputs, available at '/tmp/brian_stdout_1k7jo7rx.log' and '/tmp/brian_stderr_9gmfcjmm.log'. Thanks! [brian2]
Traceback (most recent call last):
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/core/network.py", line 996, in before_run
    obj.before_run(run_namespace)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/groups/neurongroup.py", line 990, in before_run
    self.equations.check_units(self, run_namespace=run_namespace)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/equations/equations.py", line 1164, in check_units
    resolved_namespace = group.resolve_all(
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/groups/group.py", line 800, in resolve_all
    resolved[identifier] = self._resolve(
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/groups/group.py", line 755, in _resolve
    return self._resolve_external(identifier, run_namespace=run_namespace)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/groups/group.py", line 889, in _resolve_external
    raise KeyError(error_msg)
KeyError: 'The identifier "a" could not be resolved.'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/fiach/Documents/test.py", line 140, in <module>
    main()
  File "/home/fiach/Documents/test.py", line 134, in main
    run_simulation(G, S)
  File "/home/fiach/Documents/test.py", line 91, in run_simulation
    net.run(duration)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/core/base.py", line 335, in device_override_decorated_function
    return func(*args, **kwds)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/units/fundamentalunits.py", line 2780, in new_f
    result = f(*args, **kwds)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/core/network.py", line 1132, in run
    self.before_run(namespace)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/core/base.py", line 335, in device_override_decorated_function
    return func(*args, **kwds)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/core/network.py", line 998, in before_run
    raise BrianObjectException(
brian2.core.base.BrianObjectException: Error encountered with object named 'neurongroup'.
Object was created here (most recent call only, full details in debug log):
  File '/home/fiach/Documents/test.py', line 64, in create_network
    G = NeuronGroup(N, model=izhikevich_eqs, threshold='v >= 30', reset='v = c; u = u + d', refractory=2*ms)

An error occurred when preparing an object. (See above for original error message and traceback.)

Hi @foneill. This is unfortunately not as obvious as it should be… Constants that are defined outside the equations (a, b, etc. in your example), need to be “visible” at the point where run (or net.run) is called. In your case, these variables are only available within the scope of create_network, but not within run_simulation where you call net.run.

One solution would be to move your definition of these variables to run_simulation, but I think you’d agree that this feels wrong in the sense that these variables are tied to the model itself and not to running the simulation (e.g., you might want to swap out the model later). A better solution would therefore be to either

Include the values in the equation

Directly include the parameter values in the equation. One way that keeps the flexibility of changing these values as arguments of the create_network function would be to use:

izhikevich_eqs = '''...'''
izhikevich_eqs = Equations(izhikevich_eqs, a=0.02, b=0.2, c=-65, d=8)

Note that this is equivalent to simply replacing the values by hand (i.e. writing du/dt = 0.02 * (0.2 * v - u)/ms, etc.), but it is more readable and nicer to adapt to other values.

Use the namespace argument

Instead of getting the values of parameters from the surrounding context, you can also explicitly provide them to a Network or to individual groups such as NeuronGroup. The parameters are then “bundled” with the object, and there is no need to “resolve” them from the context surrounding the net.run call. In your example, you’d keep the create_network function as it is, but provide the namespace argument to the NeuronGroup initializer:

    G = NeuronGroup(N, model=izhikevich_eqs,
                    threshold='v >= 30', reset='v = c; u = u + d', refractory=2*ms,
                    namespace={'a': a, 'b': b, 'c': c, 'd': d})

We document this feature here: Namespaces — Brian 2 2.5.4 documentation

Hope that clears things up!

Hi @mstimberg,

Thank you, this is very helpful!

1 Like