Implementing the Wilson Neuron

Description of problem

I am trying to implement the Wilson Neuron model at the moment and seem to be running into an issue. Essentially what I want to do is inject an external current (I=3 for 0.1 ms). I am keeping all the equation definitions unitless for simplicity and as per instructions given to me by my professor. I have set:

  • default_clock.dt = 0.025*ms
  • v_0 = -0.75
  • r_0 = 0.2
  • tau_v = 0.97*ms
  • tau_r = 5.6*ms
  • tau_h = 99.0*ms
  • dt = 0.05*ms

And the equations for the differentials look like this:

The objective is to plot voltage vs time

Minimal code to reproduce problem

start_scope()

tau_v = 0.97 * ms
tau_r = 5.6 * ms
tau_h = 99.0 * ms

v_0 = -0.75
r_0 = 0.2
h_0 = 0.0

defaultclock.dt = 0.025 * ms

eqn = '''
dv/dt = (-(17.81 + 47.58*v + 33.80*(v**2))*(v - 0.48) - (26*r*(v + 0.95)) - 13*h*(v + 0.95) + I_ext)/tau_v : 1
dr/dt = (-r + 1.29*v + 0.79 + 3.30*((v + 0.38)**2))/tau_r : 1
dh/dt = (-h + 11*(v + 0.754)*(v + 0.69))/tau_h : 1
I_ext : 1
'''

# Create the neuron group
neuron = NeuronGroup(1, eqn, method='euler')
neuron.v = v_0
neuron.r = r_0
neuron.h = h_0
neuron.I_ext = 0.0  # Initial external current

# Set up monitors
v_probe = StateMonitor(neuron, 'v', record=True)
r_probe = StateMonitor(neuron, 'r', record=True)
h_probe = StateMonitor(neuron, 'h', record=True)

# Run the simulation
run(5 * ms)
neuron.I_ext = 3.0
run(0.1 * ms)
neuron.I_ext = 0.0
run(20 * ms)

plt.figure(figsize=(10, 6))
plt.plot(v_probe.t / ms, v_probe.v[0], label='Voltage (v)')
plt.xlabel('Time (ms)')
plt.ylabel('State Variables (Unitless)')
plt.title('Wilson Neuron Dynamics')
plt.legend()
plt.grid()
plt.show()

What you have already tried

The code above is pretty much all that I have tried, I just tried removing units, adding units back (for example adding and removing units on tau_r, tau_v and tau_h) to see if that’d make it work. I tried different bracketing on the equations to see if that was causing some sort of syntax issue. I also tried different methods like euler, exact, rk4 but they all give the same output.

Full traceback of error (if relevant)


AttributeError Traceback (most recent call last)
File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:198, in BrianASTRenderer.render_Call(self, node)
197 node.scalar = False
→ 198 if node.func.id in self.variables:
199 funcvar = self.variables[node.func.id]

AttributeError: ‘Attribute’ object has no attribute ‘id’

During handling of the above exception, another exception occurred:

SyntaxError Traceback (most recent call last)
File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/network.py:1002, in Network.before_run(self, run_namespace)
1001 try:
→ 1002 obj.before_run(run_namespace)
1003 except Exception as ex:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/groups/group.py:1266, in CodeRunner.before_run(self, run_namespace)
1265 def before_run(self, run_namespace):
→ 1266 self.create_code_objects(run_namespace)
1267 super().before_run(run_namespace)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/groups/group.py:1259, in CodeRunner.create_code_objects(self, run_namespace)
1256 def create_code_objects(self, run_namespace):
1257 # By default, we only have one code object for each CodeRunner.
1258 # Overwrite this function to use more than one.
→ 1259 code_object = self.create_default_code_object(run_namespace)
1260 if code_object:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/groups/group.py:1240, in CodeRunner.create_default_code_object(self, run_namespace)
1239 else:
→ 1240 self.codeobj = create_runner_codeobj(
1241 group=self.group,
1242 code=self.abstract_code,
1243 user_code=self.user_code,
1244 template_name=self.template,
1245 name=f"{self.name}_codeobject*",
1246 check_units=self.check_units,
1247 additional_variables=additional_variables,
1248 needed_variables=self.needed_variables,
1249 run_namespace=run_namespace,
1250 template_kwds=self.template_kwds,
1251 override_conditional_write=self.override_conditional_write,
1252 codeobj_class=self.codeobj_class,
1253 )
1254 return self.codeobj

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/codegen/codeobject.py:484, in create_runner_codeobj(group, code, template_name, run_namespace, user_code, variable_indices, name, check_units, needed_variables, additional_variables, template_kwds, override_conditional_write, codeobj_class)
482 variables[var_index] = all_variables[var_index]
→ 484 return device.code_object(
485 owner=group,
486 name=name,
487 abstract_code=code,
488 variables=variables,
489 template_name=template_name,
490 variable_indices=all_variable_indices,
491 template_kwds=template_kwds,
492 codeobj_class=codeobj_class,
493 override_conditional_write=override_conditional_write,
494 compiler_kwds=compiler_kwds,
495 )

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/devices/device.py:324, in Device.code_object(self, owner, name, abstract_code, variables, template_name, variable_indices, codeobj_class, template_kwds, override_conditional_write, compiler_kwds)
320 logger.diagnostic(
321 f"{name} abstract code:\n{indent(code_representation(abstract_code))}"
322 )
→ 324 scalar_code, vector_code, kwds = generator.translate(
325 abstract_code, dtype=prefs[“core.default_float_dtype”]
326 )
327 # Add the array names as keywords as well

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/codegen/generators/base.py:273, in CodeGenerator.translate(self, code, dtype)
272 for ac_name, ac_code in code.items():
→ 273 statements = make_statements(
274 ac_code, self.variables, dtype, optimise=True, blockname=ac_name
275 )
276 scalar_statements[ac_name], vector_statements[ac_name] = statements

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/utils/caching.py:107, in cached..cached_func(*args, **kwds)
106 func._cache_statistics.misses += 1
→ 107 func._cache[cache_key] = func(*args, **kwds)
108 return func._cache[cache_key]

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/codegen/translation.py:429, in make_statements(code, variables, dtype, optimise, blockname)
428 if optimise and prefs.codegen.loop_invariant_optimisations:
→ 429 scalar_statements, vector_statements = optimise_statements(
430 scalar_statements, vector_statements, variables, blockname=blockname
431 )
433 return scalar_statements, vector_statements

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/codegen/optimisation.py:117, in optimise_statements(scalar_statements, vector_statements, variables, blockname)
116 # Now check if boolean simplification can be carried out
→ 117 complexity_std = expression_complexity(new_expr, simplifier.variables)
118 idents = get_identifiers(new_expr)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/codegen/optimisation.py:60, in expression_complexity(expr, variables)
59 def expression_complexity(expr, variables):
—> 60 return brian_ast(expr, variables).complexity

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:121, in brian_ast(expr, variables)
120 renderer = BrianASTRenderer(variables)
→ 121 return renderer.render_node(node)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:231, in BrianASTRenderer.render_BinOp(self, node)
230 node.right.parent = weakref.proxy(node)
→ 231 node.left = self.render_node(node.left)
232 node.right = self.render_node(node.right)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:232, in BrianASTRenderer.render_BinOp(self, node)
231 node.left = self.render_node(node.left)
→ 232 node.right = self.render_node(node.right)
233 # TODO: we could capture some syntax errors here, e.g. bool+bool
234 # captures, e.g. int+float->float

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:232, in BrianASTRenderer.render_BinOp(self, node)
231 node.left = self.render_node(node.left)
→ 232 node.right = self.render_node(node.right)
233 # TODO: we could capture some syntax errors here, e.g. bool+bool
234 # captures, e.g. int+float->float

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:231, in BrianASTRenderer.render_BinOp(self, node)
230 node.right.parent = weakref.proxy(node)
→ 231 node.left = self.render_node(node.left)
232 node.right = self.render_node(node.right)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:139, in BrianASTRenderer.render_node(self, node)
138 try:
→ 139 return getattr(self, methname)(node)
140 except AttributeError:

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:231, in BrianASTRenderer.render_BinOp(self, node)
230 node.right.parent = weakref.proxy(node)
→ 231 node.left = self.render_node(node.left)
232 node.right = self.render_node(node.right)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/parsing/bast.py:141, in BrianASTRenderer.render_node(self, node)
140 except AttributeError:
→ 141 raise SyntaxError(f"Unknown syntax: {nodename}")

SyntaxError: Unknown syntax: Call

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

BrianObjectException Traceback (most recent call last)
Cell In[31], line 33
30 h_probe = StateMonitor(neuron, ‘h’, record=True)
32 # Run the simulation
—> 33 run(5 * ms)
34 neuron.I_ext = 3.0
35 run(0.1 * ms)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/units/fundamentalunits.py:2652, in check_units..do_check_units..new_f(*args, **kwds)
2642 error_message = (
2643 f"Function ‘{f.name}’ "
2644 “expected a quantity with unit "
2645 f”{unit} for argument ‘{k}’ but got "
2646 f"‘{value}’"
2647 )
2648 raise DimensionMismatchError(
2649 error_message, get_dimensions(newkeyset[k])
2650 )
→ 2652 result = f(*args, **kwds)
2653 if “result” in au:
2654 if isinstance(au[“result”], Callable) and au[“result”] not in (
2655 bool,
2656 np.bool_,
2657 ):

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/magic.py:407, in run(duration, report, report_period, namespace, profile, level)
334 @check_units(duration=second, report_period=second)
335 def run(
336 duration,
(…)
341 level=0,
342 ):
343 “”"
344 run(duration, report=None, report_period=10*second, namespace=None, level=0)
345
(…)
405 intended use. See MagicNetwork for more details.
406 “”"
→ 407 return magic_network.run(
408 duration,
409 report=report,
410 report_period=report_period,
411 namespace=namespace,
412 profile=profile,
413 level=2 + level,
414 )

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/magic.py:248, in MagicNetwork.run(self, duration, report, report_period, namespace, profile, level)
238 def run(
239 self,
240 duration,
(…)
245 level=0,
246 ):
247 self._update_magic_objects(level=level + 1)
→ 248 Network.run(
249 self,
250 duration,
251 report=report,
252 report_period=report_period,
253 namespace=namespace,
254 profile=profile,
255 level=level + 1,
256 )

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/base.py:335, in device_override..device_override_decorator..device_override_decorated_function(*args, **kwds)
333 return getattr(curdev, name)(*args, **kwds)
334 else:
→ 335 return func(*args, **kwds)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/units/fundamentalunits.py:2652, in check_units..do_check_units..new_f(*args, **kwds)
2642 error_message = (
2643 f"Function ‘{f.name}’ "
2644 “expected a quantity with unit "
2645 f”{unit} for argument ‘{k}’ but got "
2646 f"‘{value}’"
2647 )
2648 raise DimensionMismatchError(
2649 error_message, get_dimensions(newkeyset[k])
2650 )
→ 2652 result = f(*args, **kwds)
2653 if “result” in au:
2654 if isinstance(au[“result”], Callable) and au[“result”] not in (
2655 bool,
2656 np.bool_,
2657 ):

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/network.py:1138, in Network.run(self, duration, report, report_period, namespace, profile, level)
1135 if namespace is None:
1136 namespace = get_local_namespace(level=level + 3)
→ 1138 self.before_run(namespace)
1140 if len(all_objects) == 0:
1141 return # TODO: raise an error? warning?

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/base.py:335, in device_override..device_override_decorator..device_override_decorated_function(*args, **kwds)
333 return getattr(curdev, name)(*args, **kwds)
334 else:
→ 335 return func(*args, **kwds)

File ~/anaconda3/envs/syde_552/lib/python3.9/site-packages/brian2/core/network.py:1004, in Network.before_run(self, run_namespace)
1002 obj.before_run(run_namespace)
1003 except Exception as ex:
→ 1004 raise BrianObjectException(
1005 “An error occurred when preparing an object.”, obj
1006 ) from ex
1008 # Check that no object has been run as part of another network before
1009 for obj in all_objects:

BrianObjectException: Error encountered with object named ‘neurongroup_1_stateupdater’.
Object was created here (most recent call only, full details in debug log):
File ‘/tmp/ipykernel_404216/754336726.py’, line 21, in
neuron = NeuronGroup(1, eqn, method=‘euler’)

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

Hi @zain-altaf. Actually, the model looks great, there does not seem to be anything wrong with it. I can run it and get


which looks like what you want, right?

I think the reason for your errors is a mismatch in versions. Are you using Brian’s latest version (i.e. 2.8.x)? If yes, you also have to use Python ≥ 3.10 and from your error message I can see that you are still on Python 3.9. We are following the policy of other scientific software packages for the versions of Python we support (see NEP 29 — Recommend Python and NumPy version support as a community policy standard — NumPy Enhancement Proposals). Quite often, Brian will work fine with older versions as well, but it seems that in your case it doesn’t. If if is complicated to update the Python version, another option would be to downgrade Brian to version 2.6 which is still compatible with Python 3.9 – for your model, the old version should work just as well, I think.