mip_generator#
- class brainpy.state.mip_generator(in_size=1, rate=Quantity(0., 'Hz'), p_copy=1.0, start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), rng_seed=0, name=None)#
Correlated spike trains from a Multiple Interaction Process (MIP).
mip_generatorreproduces NEST’smip_generatordevice by combining one shared parent Poisson process with independent copy operations for each child output train.1. Parent-child process model and derivation
Let \(r = \mathrm{rate}\) in spikes/s and simulation step \(\Delta t\) in ms. For each step \(n\):
\[N_n \sim \mathrm{Poisson}(\lambda), \qquad \lambda = r \, \Delta t / 1000.\]For each child train \(i \in \{1,\dots,M\}\) and each parent spike \(m \in \{1,\dots,N_n\}\), draw \(B_{i,m} \sim \mathrm{Bernoulli}(p_{\mathrm{copy}})\) independently across \(i\) and \(m\). The emitted multiplicity is
\[K_{i,n} = \sum_{m=1}^{N_n} B_{i,m}.\]Marginally, \(K_{i,n}\) is Poisson with parameter \(p_{\mathrm{copy}} \lambda\) (Poisson thinning), so each child has mean rate \(p_{\mathrm{copy}} r\). Shared parent fluctuations induce cross-child covariance:
\[\mathrm{Cov}(K_{i,n}, K_{j,n}) = p_{\mathrm{copy}}^2 \lambda,\quad \mathrm{Var}(K_{i,n}) = p_{\mathrm{copy}} \lambda,\quad \rho_{ij} = p_{\mathrm{copy}} \quad (i \neq j).\]2. Source-equivalent sampling order and computational implications
The update path mirrors
models/mip_generator.cpp:Check whether the stimulation device is active at current step.
Draw parent multiplicity from the parent Poisson process.
For each output train, run explicit Bernoulli trials for each parent spike and count copied spikes.
This implementation intentionally preserves NEST’s explicit Bernoulli loop (instead of vectorised Binomial sampling). Runtime per active step is \(O(M N_n)\) random comparisons in the general case, with fast paths for
p_copy <= 0andp_copy >= 1. RNG sampling usesnumpy.random.Generator(seeded byrng_seed), so draws are CPU NumPy-based rather than JAX-key-based.3. Timing semantics and grid constraints
Activity follows NEST stimulation-device semantics:
\[t_{\min} < t \le t_{\max}, \qquad t_{\min} = \mathrm{origin} + \mathrm{start},\quad t_{\max} = \mathrm{origin} + \mathrm{stop}.\]Therefore
startis exclusive andstopis inclusive. Internally, finite times are projected to integer steps with \(\mathrm{round}(t / \Delta t)\) and checked ast_min_step < curr_step <= t_max_step. Finiteorigin,start, andstopmust be on the simulation grid (absolute tolerance1e-12intime/dtratio), otherwiseValueErroris raised.- Parameters:
in_size (
Size, optional) – Output size specification consumed bybrainstate.nn.Dynamics.self.varshapeis derived fromin_sizeand determines the exact shape of arrays emitted byupdate(). Each element ofself.varshapecorresponds to one child process. Default is1.rate (
ArrayLike, optional) – Scalar parent Poisson rate \(r\) in spikes/s (Hz), shape()after conversion. Accepts a single-element numericArrayLikeor asaiunit.Quantityconvertible tou.Hz. Must satisfyrate >= 0. Default is0.0 * u.Hz.p_copy (
ArrayLike, optional) – Scalar copy probability \(p_{\mathrm{copy}}\) for each parent spike and each child process, shape()after conversion. Must be scalar-convertible tofloat64and satisfy0 <= p_copy <= 1. Default is1.0.start (
ArrayLike, optional) – Scalar relative start time in ms (exclusive lower bound after addingorigin), shape()after conversion. Must be scalar-convertible tofloat64and, whendtis available, representable on the simulation grid. Default is0.0 * u.ms.stop (
ArrayLikeorNone, optional) – Scalar relative stop time in ms (inclusive upper bound after addingorigin), shape()after conversion.Nonemaps to+inf. If finite, must be scalar-convertible and grid-representable whendtis available. Must satisfystop >= startafter conversion. Default isNone.origin (
ArrayLike, optional) – Scalar time offset in ms added to bothstartandstop, shape()after conversion. Must be scalar-convertible and grid-representable whendtis available. Default is0.0 * u.ms.rng_seed (
int, optional) – Seed passed tonumpy.random.SeedSequenceand split into two independent RNG streams (parent Poisson and child-copy Bernoulli). Default is0.name (
strorNone, optional) – Optional dynamics node name passed tobrainstate.nn.Dynamics.
Parameter Mapping
Table 37 Parameter mapping to model symbols# Parameter
Default
Math symbol
Semantics
rate0.0 * u.Hz\(r\)
Parent Poisson intensity in spikes/s.
p_copy1.0\(p_{\mathrm{copy}}\)
Copy probability per parent spike and per child train.
start0.0 * u.ms\(t_{\mathrm{start,rel}}\)
Relative exclusive lower activity bound.
stopNone\(t_{\mathrm{stop,rel}}\)
Relative inclusive upper activity bound;
Nonemaps to+\infty.origin0.0 * u.ms\(t_0\)
Time offset added to
startandstop.in_size1\(M\)
Number/shape of child processes (
M = prod(varshape)).rng_seed0Entropy source for parent/child RNG stream initialization.
- Raises:
ValueError – If
rate < 0; ifp_copyis outside[0, 1]; ifstop < start; if scalar conversion fails due to non-scalar inputs; or if finiteorigin/start/stopare not multiples ofdtwhen simulation resolution is available.TypeError – If conversion of unitful inputs to
u.Hzoru.msis invalid.KeyError – At update time, if the simulation environment does not provide required entries such as
dtviabrainstate.environ.get_dt().
Notes
Outputs are multiplicities
0, 1, 2, ...per discrete step, matching NESTSpikeEventmultiplicity semantics rather than binary spike flags.init_state()creates two independent RNG instances to mirror NEST’s separation of parent and child stochastic paths.set()updates cached timing boundaries immediately whendtis already available inbrainstate.environ.
Examples
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... gen = brainpy.state.mip_generator( ... in_size=(2, 3), ... rate=800.0 * u.Hz, ... p_copy=0.25, ... start=5.0 * u.ms, ... stop=40.0 * u.ms, ... rng_seed=7, ... ) ... with brainstate.environ.context(t=10.0 * u.ms): ... counts = gen.update() ... _ = counts.shape, counts.dtype
>>> import brainpy >>> import saiunit as u >>> gen = brainpy.state.mip_generator(rate=1200.0 * u.Hz, p_copy=0.1) >>> gen.set(start=2.0 * u.ms, stop=None, origin=1.0 * u.ms) >>> params = gen.get() >>> _ = params['rate'], params['p_copy'], params['stop']
See also
poisson_generatorIndependent Poisson trains without shared parent process.
poisson_generator_psPrecise-time Poisson generator with dead time.
inhomogeneous_poisson_generatorTime-varying Poisson rate generator.
References
- init_state(batch_size=None, **kwargs)[source]#
Initialize RNG state for parent and child stochastic paths.
Spawns two independent
numpy.random.Generatorinstances fromrng_seedvianumpy.random.SeedSequence, mirroring NEST’s separation of parent Poisson draws and per-child Bernoulli draws.- Parameters:
- Raises:
ValueError – If
rng_seedcannot be consumed bynumpy.random.SeedSequence.TypeError – If
rng_seedhas an invalid type for NumPy RNG initialization.
- set(*, rate=<object object>, p_copy=<object object>, start=<object object>, stop=<object object>, origin=<object object>)[source]#
Update public generator parameters with NEST-compatible semantics.
Any parameter left at the internal sentinel
_UNSETretains its current value. All provided values are validated and converted before any attribute is mutated, so the generator state remains consistent on failure. Ifdtis currently available inbrainstate.environ, the cached step bounds are recomputed immediately after mutation.- Parameters:
rate (
ArrayLikeorobject, optional) – New scalar parent Poisson rate in Hz. If omitted, keep current value. Must satisfyrate >= 0after scalar conversion.p_copy (
ArrayLikeorobject, optional) – New scalar copy probability in[0, 1]. If omitted, keep current value.start (
ArrayLikeorobject, optional) – New scalar relative start time in ms. If omitted, keep current value.stop (
ArrayLike,None, orobject, optional) – New scalar relative stop time in ms.Nonemaps to+inf. If omitted, keep current value.origin (
ArrayLikeorobject, optional) – New scalar time origin in ms. If omitted, keep current value.
- Raises:
ValueError – If any provided parameter is non-scalar, violates parameter constraints (for example
p_copyoutside[0, 1]orstop < start), or finite times are off the simulation grid whendtis available.TypeError – If unit conversion or scalar coercion fails for provided values.
- simulate(n_steps)[source]#
Run
n_stepssimulation steps in one vectorised NumPy call.Equivalent to calling
update()in a loop withbrainstate.environ.context(t=k*dt)fork = 0, 1, ..., n_steps-1, but avoids per-step Python overhead by batching all random draws.- Parameters:
n_steps (
int) – Number of simulation steps to run. Assumes step indexkcorresponds to timet = k * dt.- Returns:
out – Integer array of shape
(n_steps, *self.varshape)with spike multiplicities per step and per child train.- Return type:
numpy.ndarray
Notes
Binomial(n, p)is used in place ofnindependentBernoulli(p)trials — statistically equivalent but faster.
- update()[source]#
Advance one simulation step and emit child spike multiplicities.
Executes the source-equivalent MIP sampling pipeline: lazily initialises state if needed, refreshes the timing/rate cache when
dtchanges, gates activity with \(t_{\min} < t \le t_{\max}\), draws parent spike multiplicity from \(\mathrm{Poisson}(r \Delta t / 1000)\), then independently copies each parent spike into each child train with probabilityp_copy.- Returns:
out – NumPy
int64array of shapeself.varshape. Entries are per-step spike multiplicities for each child train. Returns all zeros when the generator is inactive, whenrate <= 0, or when the parent draw yields zero spikes.- Return type:
jax.Array- Raises:
KeyError – If the simulation context does not provide
dtrequired bybrainstate.environ.get_dt().ValueError – If finite timing parameters are not aligned to the simulation grid after a
dtchange.TypeError – If simulation-time values in the environment cannot be converted to scalar milliseconds.