Source code for brian2.input.poissongroup

"""
Implementation of `PoissonGroup`.
"""

import numpy as np

from brian2.core.spikesource import SpikeSource
from brian2.core.variables import Subexpression, Variables
from brian2.groups.group import Group
from brian2.groups.neurongroup import Thresholder
from brian2.groups.subgroup import Subgroup
from brian2.parsing.expressions import parse_expression_dimensions
from brian2.units.fundamentalunits import check_units, fail_for_dimension_mismatch
from brian2.units.stdunits import Hz
from brian2.utils.stringtools import get_identifiers

__all__ = ["PoissonGroup"]


[docs] class PoissonGroup(Group, SpikeSource): """ Poisson spike source Parameters ---------- N : int Number of neurons rates : `Quantity`, str Single rate, array of rates of length N, or a string expression evaluating to a rate. This string expression will be evaluated at every time step, it can therefore be time-dependent (e.g. refer to a `TimedArray`). dt : `Quantity`, optional The time step to be used for the simulation. Cannot be combined with the `clock` argument. clock : `Clock`, optional The update clock to be used. If neither a clock, nor the `dt` argument is specified, the `defaultclock` will be used. when : str, optional When to run within a time step, defaults to the ``'thresholds'`` slot. See :ref:`scheduling` for possible values. order : int, optional The priority of of this group for operations occurring at the same time step and in the same scheduling slot. Defaults to 0. name : str, optional Unique name, or use poissongroup, poissongroup_1, etc. """ add_to_magic_network = True @check_units(rates=Hz) def __init__( self, N, rates, dt=None, clock=None, when="thresholds", order=0, namespace=None, name="poissongroup*", codeobj_class=None, ): Group.__init__( self, dt=dt, clock=clock, when=when, order=order, namespace=namespace, name=name, ) self.codeobj_class = codeobj_class self._N = N = int(N) # TODO: In principle, it would be nice to support Poisson groups with # refactoriness, but we can't currently, since the refractoriness # information is reset in the state updater which we are not using # We could either use a specific template or simply not bother and make # users write their own NeuronGroup (with threshold rand() < rates*dt) # for more complex use cases. self.variables = Variables(self) # standard variables self.variables.add_constant("N", value=self._N) self.variables.add_arange("i", self._N, constant=True, read_only=True) self.variables.add_array("_spikespace", size=N + 1, dtype=np.int32) self.variables.create_clock_variables(self._clock) # The firing rates if isinstance(rates, str): self.variables.add_subexpression("rates", dimensions=Hz.dim, expr=rates) else: self.variables.add_array("rates", size=N, dimensions=Hz.dim) self._rates = rates self.start = 0 self.stop = N self._refractory = False self.events = {"spike": "rand() < rates * dt"} self.thresholder = {"spike": Thresholder(self)} self.contained_objects.append(self.thresholder["spike"]) self._enable_group_attributes() if not isinstance(rates, str): self.rates = rates def __getitem__(self, item): if not isinstance(item, slice): raise TypeError("Subgroups can only be constructed using slicing syntax") start, stop, step = item.indices(self._N) if step != 1: raise IndexError("Subgroups have to be contiguous") if start >= stop: raise IndexError( f"Illegal start/end values for subgroup, {int(start)}>={int(stop)}" ) return Subgroup(self, start, stop)
[docs] def before_run(self, run_namespace=None): rates_var = self.variables["rates"] if isinstance(rates_var, Subexpression): # Check that the units of the expression make sense expr = rates_var.expr identifiers = get_identifiers(expr) variables = self.resolve_all( identifiers, run_namespace, user_identifiers=identifiers ) unit = parse_expression_dimensions(rates_var.expr, variables) fail_for_dimension_mismatch( unit, Hz, "The expression provided for " "PoissonGroup's 'rates' " "argument, has to have units " "of Hz", ) super().before_run(run_namespace)
@property def spikes(self): """ The spikes returned by the most recent thresholding operation. """ # Note that we have to directly access the ArrayVariable object here # instead of using the Group mechanism by accessing self._spikespace # Using the latter would cut _spikespace to the length of the group spikespace = self.variables["_spikespace"].get_value() return spikespace[: spikespace[-1]] def __repr__(self): classname = self.__class__.__name__ return f"{classname}({self.N}, rates={self._rates!r})"