Using the value of a variable at the previous timestep

Description of problem

Hello Brian2 Team,

I would like to compute a variable based on whether a current inside my neuron is increasing to a threshold value in a specific way and compute the same variable if this current is decreasing to this threshold value in another way. The current is computed using a differential equation.
In order to determine the monotonicity of the current I need to compare the value of the current at the current timestep to the value of the current at the previous timestep. I am not entirely sure if this is possible. I could not find anything related to how the order in which the equations are written in the equations string influences the order of execution in the run, just the part related to events which is not of interest to my situation.

Thank you very much!

Minimal code to reproduce problem

What you have aready tried

I tried writing an equation which stores the value of the current at this timestep right at the end of the equations string and then use this previously stored value when computing my variable, in an equation written in the same string, but before the storage of the value of the current at this timestep. I am getting some errors related to sympy.

‘model’: ‘’’
a = 0+1*(I_prev_dt<I)*(I>I_th)
I_prev_dt = I

Expected output (if relevant)

Actual output (if relevant)

Full traceback of error (if relevant)

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/core/ in before_run(self, run_namespace)
891 try:
→ 892 obj.before_run(run_namespace)
893 except Exception as ex:

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/groups/ in before_run(self, run_namespace)
1134 def before_run(self, run_namespace):
→ 1135 self.create_code_objects(run_namespace)
1136 super(CodeRunner, self).before_run(run_namespace)

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/groups/ in create_code_objects(self, run_namespace)
1127 # Overwrite this function to use more than one.
→ 1128 code_object = self.create_default_code_object(run_namespace)
1129 if code_object:

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/groups/ in create_default_code_object(self, run_namespace)
1099 def create_default_code_object(self, run_namespace):
→ 1100 self.update_abstract_code(run_namespace=run_namespace)
1101 # If the CodeRunner has variables, add them

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/groups/ in update_abstract_code(self, run_namespace)
248 if len( > 0:
→ 249 stateupdate_output = StateUpdateMethod.apply_stateupdater(,
250 variables,

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/utils/ in cached_func(*args, **kwds)
100 func._cache_statistics.misses += 1
→ 101 func._cache[cache_key] = func(*args, **kwds)
102 return func._cache[cache_key]

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/stateupdaters/ in apply_stateupdater(equations, variables, method, method_options, group_name)
221 start_time = time.time()
→ 222 code = stateupdater(equations, variables, method_options)
223 method_time = time.time() - start_time

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/stateupdaters/ in call(self, eqs, variables, method_options)
→ 599 substituted_expressions = eqs.get_substituted_expressions(variables)

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/equations/ in get_substituted_expressions(self, variables, include_subexpressions)
→ 768 new_sympy_expr = str_to_sympy(eq.expr.code, variables).xreplace(substitutions)
769 new_str_expr = sympy_to_str(new_sympy_expr)

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in xreplace(self, rule)
1181 “”"
→ 1182 value, _ = self._xreplace(rule)
1183 return value

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in _xreplace(self, rule)
1196 if _xreplace is not None:
→ 1197 a_xr = _xreplace(rule)
1198 args.append(a_xr[0])

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in _xreplace(self, rule)
1203 if changed:
→ 1204 return self.func(*args), True
1205 return self, False

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in wrapper(*args, **kwargs)
93 try:
—> 94 retval = cfunc(*args, **kwargs)
95 except TypeError:

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in new(cls, *args, **options)
—> 47 c_part, nc_part, order_symbols = cls.flatten(args)
48 is_commutative = not nc_part

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/sympy/core/ in flatten(cls, seq)
338 b1, e1 = o1.as_base_exp()
→ 339 b2, e2 = o.as_base_exp()
340 new_exp = e1 + e2

AttributeError: ‘BooleanFalse’ object has no attribute ‘as_base_exp’

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

BrianObjectException Traceback (most recent call last)
/var/folders/_2/nt_47hxs7x3g2_3gdchdpkzh0000gn/T/ipykernel_11305/ in
1 if name == ‘main’:
----> 2 train_results, df_train_results = main()

/var/folders/2/nt_47hxs7x3g2_3gdchdpkzh0000gn/T/ipykernel_11305/ in main()
67 pool = Pool(processes = (pa.helpers.cpu_count() - 1))
—> 68 results =, configs)
69 store_time =
70 store_date_time = store_time.strftime("%m

~/miniconda3/envs/sim_clone/lib/python3.8/multiprocessing/ in map(self, func, iterable, chunksize)
362 in a list that is returned.
363 ‘’’
→ 364 return self._map_async(func, iterable, mapstar, chunksize).get()
366 def starmap(self, func, iterable, chunksize=None):

~/miniconda3/envs/sim_clone/lib/python3.8/multiprocessing/ in get(self, timeout)
769 return self._value
770 else:
→ 771 raise self._value
773 def _set(self, i, obj):

~/miniconda3/envs/sim_clone/lib/python3.8/multiprocessing/ in worker(inqueue, outqueue, initializer, initargs, maxtasks, wrap_exception)
123 job, i, func, args, kwds = task
124 try:
→ 125 result = (True, func(*args, **kwds))
126 except Exception as e:
127 if wrap_exception and func is not _helper_reraises_exception:

~/miniconda3/envs/sim_clone/lib/python3.8/multiprocessing/ in mapstar(args)
47 def mapstar(args):
—> 48 return list(map(*args))
50 def starmapstar(args):

~/Documents/Work/ in run_experiment(params)
263 if params[‘learning’] == 1:
264 print("[Train]: Experiment starts running…")
→ 265
266 print("[Train]: Experiment DONE!")

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/core/ in device_override_decorated_function(*args, **kwds)
289 curdev = get_device()
290 if hasattr(curdev, name):
→ 291 return getattr(curdev, name)(*args, **kwds)
292 else:
293 return func(*args, **kwds)

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/devices/cpp_standalone/ in network_run(self, net, duration, report, report_period, namespace, profile, level, **kwds)
1365 namespace = get_local_namespace(level=level+2)
→ 1367 net.before_run(namespace)
1368 self.synapses |= {s for s in net.objects
1369 if isinstance(s, Synapses)}

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/core/ in device_override_decorated_function(*args, **kwds)
291 return getattr(curdev, name)(*args, **kwds)
292 else:
→ 293 return func(*args, **kwds)
295 device_override_decorated_function.original_function = func

~/miniconda3/envs/sim_clone/lib/python3.8/site-packages/brian2/core/ in before_run(self, run_namespace)
892 obj.before_run(run_namespace)
893 except Exception as ex:
→ 894 raise BrianObjectException(“An error occurred when preparing an object.”, obj) from ex
896 # Check that no object has been run as part of another network before

BrianObjectException: Error encountered with object named ‘Core_0_stateupdater’.
Object was created here (most recent call only, full details in debug log):
File ‘~/Documents/Work/’, line 139, in init
self.neurons = NeuronGroup(num_neurons, **eq(), name=name)

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

Hi @ifodor. This is not super-convenient at the moment, but the way to store a previous value is to add a variable like I_prev : amp to your equations, and then store the current value in it before it gets updated (if you include I_prev = I in your equations, it literally means that I_prev is identical to I, so both will have the same values):

neuron.run_regularly('I_prev = I')

You can then refer to this variable in your model. You might need to shift this operation to another point in the schedule, by setting the when argument, but that depends on the details of how you update the current.

Regarding the sympy error, this is because it is not “mathematical” to multiply numbers with a boolean expression like I_prev_dt < I. Interpreting this value as 0 or 1 is a programming language convention. To make this explicit and get rid of the sympy error, use int(I_prev_dt < I) instead.

Hope that helps, best

Hello, Marcel

Thank you very much for your answer.

This was helpful. In the end I didn’t use this exact implementation due to an incorrect understanding from my side of what was expected to implement. It though gave me a good understanding of the run_regularly mechanism which I used further.

I wish you all the best!

1 Like