Source code for brian2.input.poissoninput
"""
Implementation of `PoissonInput`.
"""
from brian2.core.variables import Variables
from brian2.groups.group import CodeRunner
from brian2.units.fundamentalunits import (
DimensionMismatchError,
check_units,
get_dimensions,
have_same_dimensions,
)
from brian2.units.stdunits import Hz
from .binomial import BinomialFunction
__all__ = ["PoissonInput"]
[docs]class PoissonInput(CodeRunner):
"""
PoissonInput(target, target_var, N, rate, weight, when='synapses', order=0)
Adds independent Poisson input to a target variable of a `Group`. For large
numbers of inputs, this is much more efficient than creating a
`PoissonGroup`. The synaptic events are generated randomly during the
simulation and are not preloaded and stored in memory. All the inputs must
target the same variable, have the same frequency and same synaptic weight.
All neurons in the target `Group` receive independent realizations of
Poisson spike trains.
Parameters
----------
target : `Group`
The group that is targeted by this input.
target_var : str
The variable of `target` that is targeted by this input.
N : int
The number of inputs
rate : `Quantity`
The rate of each of the inputs
weight : str or `Quantity`
Either a string expression (that can be interpreted in the context of
`target`) or a `Quantity` that will be added for every event to
the `target_var` of `target`. The unit has to match the unit of
`target_var`
when : str, optional
When to update the target variable during a time step. Defaults to
the `synapses` scheduling slot. See :ref:`scheduling` for possible values.
order : int, optional
The priority of of the update compared to other operations occurring at
the same time step and in the same scheduling slot. Defaults to 0.
"""
@check_units(N=1, rate=Hz)
def __init__(self, target, target_var, N, rate, weight, when="synapses", order=0):
if target_var not in target.variables:
raise KeyError(f"{target_var} is not a variable of {target.name}")
self._weight = weight
self._target_var = target_var
if isinstance(weight, str):
weight = f"({weight})"
else:
weight_dims = get_dimensions(weight)
target_dims = target.variables[target_var].dim
# This will be checked automatically in the abstract code as well
# but doing an explicit check here allows for a clearer error
# message
if not have_same_dimensions(weight_dims, target_dims):
raise DimensionMismatchError(
"The provided weight does not "
"have the same unit as the "
f"target variable '{target_var}'",
weight_dims,
target_dims,
)
weight = repr(weight)
self._N = N
self._rate = rate
binomial_sampling = BinomialFunction(
N, rate * target.clock.dt, name="poissoninput_binomial*"
)
code = f"{target_var} += {binomial_sampling.name}()*{weight}"
self._stored_dt = target.dt_[:] # make a copy
# FIXME: we need an explicit reference here for on-the-fly subgroups
# For example: PoissonInput(group[:N], ...)
self._group = target
CodeRunner.__init__(
self,
group=target,
template="stateupdate",
code=code,
user_code="",
when=when,
order=order,
name="poissoninput*",
clock=target.clock,
)
self.variables = Variables(self)
self.variables._add_variable(binomial_sampling.name, binomial_sampling)
rate = property(fget=lambda self: self._rate, doc="The rate of each input")
N = property(fget=lambda self: self._N, doc="The number of inputs")
target_var = property(
fget=lambda self: self._target_var, doc="The targetted variable"
)
weight = property(fget=lambda self: self._weight, doc="The synaptic weight")
[docs] def before_run(self, run_namespace):
if self._group.dt_ != self._stored_dt:
raise NotImplementedError(
f"The dt used for simulating {self.group.name} "
"changed after the PoissonInput source was "
"created."
)
CodeRunner.before_run(self, run_namespace=run_namespace)