# Namespace Issues with PoissonGroup

**URL:** <https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484>\
**Category:** Support\
**Tags:** equations, namespace\
**Created:** [20 August 2025 13:32 UTC](https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484 "2025-08-20T13:32:47Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![flmuk](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/flmuk/32/910_2.png) [@flmuk](https://brian.discourse.group/u/flmuk)\
**Post date:** [20 August 2025 13:32 UTC](https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484/1 "2025-08-20T13:32:47Z")

</div>

# Problem

I’m encountering a persistent `KeyError` when trying to use a state variable from one `NeuronGroup` to dynamically modulate the `rates` of a `PoissonGroup`. I’ve tried providing the necessary objects in the namespace both at object creation and at runtime, but the error persists, and I’m struggling to understand what I’m missing.  
I want to avoid using a `TimedArray`, since my simulations are long and if I pre-allocate a `TimedArray` I run out of memory. Any help would be appreciated!

# Minimal code to reproduce the problem

```python
#!/usr/bin/env python
from brian2 import *
import matplotlib.pyplot as plt

# 1. --- Parameters ---
duration = 10*second
track_length = 100*cm
speed = 20*cm/second
N_inputs = 50

# 2. --- Define the Brian2 Objects ---

# This group provides the dynamic variables
animal_eqs = '''
pos = (speed * t) % track_length : meter (constant over dt)
is_on : 1 (constant)
'''
animal = NeuronGroup(1, animal_eqs, name='animal')

# The rates string links to 'pos' and 'is_on'
rates_eq = '''
is_on * (200*Hz * (1 + sin(2*pi*i/N_inputs + 2*pi*pos/track_length))/2) + 2*Hz
'''

# Create a dedicated namespace that points to the other objects
input_namespace = {
    'animal': animal,
    'N_inputs': N_inputs
}

# Provide this namespace during the object's creation
inputs = PoissonGroup(N_inputs, rates=rates_eq,
                      namespace=input_namespace, name='inputs')

# 3. --- Set up Monitors and Network ---
spike_mon = SpikeMonitor(inputs)
pos_mon = StateMonitor(animal, 'pos', record=True)
net = Network(collect())

# 4. --- Run the Simulation in Phases ---
print("Running Phase 1: Spatial input is OFF...")
animal.is_on = 0.
net.run(duration/2, namespace=locals())

print("Running Phase 2: Spatial input is ON...")
animal.is_on = 1.
net.run(duration/2, namespace=locals())

# 5. --- Plot the Results ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 7), sharex=True,
                               gridspec_kw={'height_ratios': [3, 1]})

ax1.plot(spike_mon.t/second, spike_mon.i, '.k', ms=3)
ax1.axvline(5.0, ls='--', color='red', lw=2, label='is_on = 1')
ax1.set_title("Input Neuron Firing (Gated by 'is_on')")
ax1.set_ylabel("Neuron Index")
ax1.legend()
ax1.grid(axis='y', alpha=0.2)

ax2.plot(pos_mon.t/second, pos_mon.pos[0]/cm)
ax2.set_title("Animal Position on Track")
ax2.set_xlabel("Time (s)")
ax2.set_ylabel("Position (cm)")

plt.tight_layout()
plt.show()

```

# Full traceback of error

```bash
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/core/network.py:1003, in Network.before_run(self, run_namespace)
   1002 try:
-> 1003 obj.before_run(run_namespace)
   1004 except Exception as ex:

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/input/poissongroup.py:132, in PoissonGroup.before_run(self, run_namespace)
    131 identifiers = get_identifiers(expr)
--> 132 variables = self.resolve_all(
    133 identifiers, run_namespace, user_identifiers=identifiers
    134 )
    135 unit = parse_expression_dimensions(rates_var.expr, variables)

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/groups/group.py:801, in Group.resolve_all(self, identifiers, run_namespace, user_identifiers, additional_variables)
    800 for identifier in identifiers:
--> 801 resolved[identifier] = self._resolve(
    802 identifier,
    803 user_identifier=identifier in user_identifiers,
    804 additional_variables=additional_variables,
    805 run_namespace=run_namespace,
    806 )
    807 return resolved

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/groups/group.py:756, in Group._resolve(self, identifier, run_namespace, user_identifier, additional_variables)
    754 # We did not find the name internally, try to resolve it in the external
    755 # namespace
--> 756 return self._resolve_external(identifier, run_namespace=run_namespace)

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/groups/group.py:890, in Group._resolve_external(self, identifier, run_namespace, user_identifier, internal_variable)
    889 error_msg = f'The identifier "{identifier}" could not be resolved.'
--> 890 raise KeyError(error_msg)
    892 elif len(matches) > 1:
    893 # Possibly, all matches refer to the same object

KeyError: 'The identifier "pos" could not be resolved.'

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

BrianObjectException Traceback (most recent call last)
Cell In[2], line 44
     42 print("Running Phase 1: Spatial input is OFF...")
     43 animal.is_on = 0.
---> 44 net.run(duration/2, namespace=locals())
     46 print("Running Phase 2: Spatial input is ON...")
     47 animal.is_on = 1.

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/core/base.py:346, in device_override.<locals>.device_override_decorator.<locals>.device_override_decorated_function(*args, **kwds)
    344 return getattr(curdev, name)(*args, **kwds)
    345 else:
--> 346 return func(*args, **kwds)

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/units/fundamentalunits.py:2652, in check_units.<locals>.do_check_units.<locals>.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 ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/core/network.py:1141, in Network.run(self, duration, report, report_period, namespace, profile, level)
   1138 if namespace is None:
   1139 namespace = get_local_namespace(level=level + 3)
-> 1141 self.before_run(namespace)
   1143 if len(all_objects) == 0:
   1144 return # TODO: raise an error? warning?

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/core/base.py:346, in device_override.<locals>.device_override_decorator.<locals>.device_override_decorated_function(*args, **kwds)
    344 return getattr(curdev, name)(*args, **kwds)
    345 else:
--> 346 return func(*args, **kwds)

File ~/miniconda3/envs/brian/lib/python3.13/site-packages/brian2/core/network.py:1005, in Network.before_run(self, run_namespace)
   1003 obj.before_run(run_namespace)
   1004 except Exception as ex:
-> 1005 raise BrianObjectException(
   1006 "An error occurred when preparing an object.", obj
   1007 ) from ex
   1009 # Check that no object has been run as part of another network before
   1010 for obj in all_objects:

BrianObjectException: Error encountered with object named 'inputs'.
Object was created here (most recent call only, full details in debug log):
  File '<ipython-input-2-c4adc19fee79>', line 32, in <module>
    inputs = PoissonGroup(N_inputs, rates=rates_eq,

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

```

Environment info:

- Python 3.13.2

- Brian2 2.8.0

- Linux OS

---

<div class="post-metadata">

**Author:** ![Mrigesh\_Thakur](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/mrigesh_thakur/32/856_2.png) [@Mrigesh\_Thakur](https://brian.discourse.group/u/Mrigesh_Thakur)\
**Post date:** [23 August 2025 04:15 UTC](https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484/2 "2025-08-23T04:15:21Z")

</div>

Hi @flmuk ,

What seems to be the problem is how Brian2 resolves external variables for a `PoissonGroup`. When you provide the `rates` as a string expression, Brian2 needs to know exactly where to find every variable in that string (like `pos` and `is_on`) at the moment it prepares the simulation.

Your initial approach of providing a namespace `{'animal': animal}` was on the right track, but Brian2 doesn’t automatically look _inside_ the `animal` object to find `pos`. It’s looking for a top-level variable named `pos` in the namespace and can’t find it, which causes the `KeyError`.

The correct way to handle this is to make the link explicit by creating a namespace that maps the variable names directly to the specific variable objects within the `animal` group. This gives the `PoissonGroup` a direct and efficient pointer to the values it needs, without you having to use a memory-intensive `TimedArray`.

Here is the corrected and working version of your code:

```python
#!/usr/bin/env python
from brian2 import *
import matplotlib.pyplot as plt

# 1. --- Parameters ---
duration = 10*second
track_length = 100*cm
speed = 20*cm/second
N_inputs = 50

# 2. --- Define the Brian2 Objects ---

# This group provides the dynamic variables
animal_eqs = '''
pos = (speed * t) % track_length : meter (constant over dt)
is_on : 1 (constant)
'''
animal = NeuronGroup(1, animal_eqs, name='animal')

# The rates string remains the same
rates_eq = '''
is_on * (200*Hz * (1 + sin(2*pi*i/N_inputs + 2*pi*pos/track_length))/2) + 2*Hz
'''

# **THE FIX: Create a namespace that maps names directly to the variable objects**
input_namespace = {
    'pos': animal.variables['pos'],
    'is_on': animal.variables['is_on'],
    'N_inputs': N_inputs,
    'track_length': track_length
}

# Provide this correctly structured namespace during the object's creation
inputs = PoissonGroup(N_inputs, rates=rates_eq,
                      namespace=input_namespace, name='inputs')

# 3. --- Set up Monitors and Network ---
spike_mon = SpikeMonitor(inputs)
pos_mon = StateMonitor(animal, 'pos', record=True)
net = Network(collect())

# 4. --- Run the Simulation in Phases ---
print("Running Phase 1: Spatial input is OFF...")
animal.is_on = 0.
net.run(duration/2) # No runtime namespace is needed now

print("Running Phase 2: Spatial input is ON...")
animal.is_on = 1.
net.run(duration/2) # No runtime namespace is needed now

# 5. --- Plot the Results ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 7), sharex=True,
                               gridspec_kw={'height_ratios': [3, 1]})

ax1.plot(spike_mon.t/second, spike_mon.i, '.k', ms=3)
ax1.axvline(5.0, ls='--', color='red', lw=2, label='is_on = 1')
ax1.set_title("Input Neuron Firing (Gated by 'is_on')")
ax1.set_ylabel("Neuron Index")
ax1.legend()
ax1.grid(axis='y', alpha=0.2)

ax2.plot(pos_mon.t/second, pos_mon.pos[0]/cm)
ax2.set_title("Animal Position on Track")
ax2.set_xlabel("Time (s)")
ax2.set_ylabel("Position (cm)")

plt.tight_layout()
plt.show()

```

By explicitly mapping `'pos': animal.variables['pos']`, you’re telling the `PoissonGroup` exactly where to look for the `pos` variable, which resolves the error at the compilation stage.

Hope this helps 😁

---

<div class="post-metadata">

**Author:** ![mstimberg](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/mstimberg/32/11_2.png) [@mstimberg](https://brian.discourse.group/u/mstimberg)\
**Post date:** [1 September 2025 13:24 UTC](https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484/3 "2025-09-01T13:24:22Z")

</div>

Hi everyone (just came back from holiday and only now going through old posts…). The way Brian objects interact with variables not defined as part of their own equations is sometimes non-trivial, and I think we could do a better job explaining it. The `namespace` can only be used for two specific things: references to constant values and to user-defined functions. In both cases, specifying things via `namespace` is not necessary if the variable/function is “visible” from where the `run` function is called. So in principle, you could use your `is_on` variable defined outside any group as it is:

```python
# 4. --- Run the Simulation in Phases ---
print("Running Phase 1: Spatial input is OFF...")
is_on = 0.
net.run(duration/2)

print("Running Phase 2: Spatial input is ON...")
is_on = 1.
net.run(duration/2)

```

This would work without any `namespace` definition, because the default namespace is picking up variables from the point where `net.run` is called (it is only necessary to use it when you e.g. structure your code with functions that don’t have direct access to the value, or if the values are stored in a dictionary or similar cases). The problem is that this would work for `is_on`, since it is a constant, but it does not work for `pos` which is a function of time. You are correct that this has to be declared within a group, but you could also declare it as part of the `PoissonGroup` rate itself:

```python
rates_eq = '''
is_on * (200*Hz * (1 + sin(2*pi*i/N_inputs + 2*pi*((speed * t) % track_length)/track_length))/2) + 2*Hz
'''

```

(I replaced `pos` by its definition). This should work, but is not as convenient as what you had in mind – for example, you cannot reuse the `pos` variable anywhere else, or record it with a monitor. The best solution for your problem is therefore probably to not use `PoissonGroup` at all, but rather write an equivalent `NeuronGroup` (as we mention in passing in the documentation: [Input stimuli — Brian 2 2.9.0.post70 documentation](https://brian2.readthedocs.io/en/latest/user/input.html#more-on-poisson-inputs)) .

```python
# This group provides both the dynamic variables and the spikes
animal_rate_eqs = '''
pos = (speed * t) % track_length : meter (constant over dt)
is_on : 1 (constant)
rates = is_on * (200*Hz * (1 + sin(2*pi*i/N_inputs + 2*pi*pos/track_length))/2) + 2*Hz : Hz
'''
animal_input = NeuronGroup(N_inputs, animal_eqs,
                           threshold="rand()<rates*dt", reset="",
                           name='animal_input')
# ...
print("Running Phase 1: Spatial input is OFF...")
animal_input.is_on = 0.
net.run(duration/2)
# ...

```

Now, this is slightly more inefficient compared to your solution, since it calculates the position for each of the inputs, instead of once. I don’t think this will make a noticeable difference, but if you prefer to keep the `animal` and `input` groups separate, you can do this as well, replacing the `PoissonGroup` by a `NeuronGroup` as described earlier and then use “linked variables” to connect the two groups.

Hopefully that gives you an idea of how to proceed!

---

<div class="post-metadata">

**Author:** ![flmuk](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/flmuk/32/910_2.png) [@flmuk](https://brian.discourse.group/u/flmuk)\
**Post date:** [16 September 2025 14:38 UTC](https://brian.discourse.group/t/namespace-issues-with-poissongroup/1484/4 "2025-09-16T14:38:34Z")

</div>

Yes, thank you very much!
