Simulator#

class brainpy.state.network.Simulator(*args, **kwargs)#

Explicit NEST-flavored network builder and runner.

Parameters:

dt (brainunit.Quantity) – Simulation timestep; set into brainstate.environ at construction.

Examples

>>> import brainunit as u
>>> from brainpy.state import iaf_psc_alpha, poisson_generator, spike_recorder
>>> from brainpy.state import Simulator, all_to_all
>>> sim = Simulator(dt=0.1 * u.ms)
>>> pop = sim.create(iaf_psc_alpha, 10)
>>> noise = sim.create(poisson_generator, rate=8000. * u.Hz)
>>> rec = sim.create(spike_recorder)
>>> sim.connect(noise, pop, weight=20. * u.pA, delay=1.5 * u.ms, rule=all_to_all)
>>> sim.connect(pop, rec)
>>> res = sim.simulate(100. * u.ms)
>>> rate = res.rate(rec)
connect(pre, post, *, rule=<brainpy.state._AllToAll object>, weight=None, delay=None, comm='dense', receptor_type=None, synapse=None, vt=None, allow_autapses=True, allow_multapses=True, seed=None)[source]#

Connect pre to post (or register a recorder tap).

comm='sparse' routes the connectivity through a sparse CSR event matmul (memory-light for large fan-out); 'dense' (default) uses a dense weight matrix. Both yield identical results for the same rule/seed.

receptor_type='uniform' routes each edge to a uniformly-drawn receptor port of a multi-receptor post population (iaf_psc_exp_multisynapse).

synapse=<spec> builds a plastic EventPlasticProj from a rebuilt _nest synapse spec (static_synapse, the tsodyks* family, quantal_stp_synapse); weight/delay here override the spec’s defaults. synapse=None (default) keeps the static EventProjection path unchanged.

connect(dopa_pool, vt) (reverse direction, post a volume_transmitter view) registers each presynaptic segment as a dopaminergic source on the transmitter and builds no projection. vt=<volume_transmitter view> binds a transmitter to a synapse spec that reads a broadcast signal (signal_reads, e.g. stdp_dopamine_synapse); such a spec raises if no vt is supplied.

Analog recorders (voltmeter / multimeter) are connected in NEST’s reversed direction — connect(recorder, pop) — because the recorder observes the population. This registers a per-step State tap; no projection is built.

Returns:

The projection handle(s) built by this call (a single handle when one projection is built, a list for multi-segment fan-out). A plastic handle (synapse=spec) can be passed to record_weight(). Recorder-tap connects (and current injectors) return None.

Return type:

EventProjection or EventPlasticProj or list or None

cont(duration, *, dt=None)[source]#

Continue the rollout for duration WITHOUT re-initialising state.

Unlike simulate(), state persists across calls (biological time accumulates), so a host loop can interleave Python work between chunks — read this window’s recordings, rewrite a host_drive schedule, or overwrite static weights via get_connections(...).set('weight', ...) — while the compiled per-chunk for_loop is reused (no recompile as long as only State contents change). Lazily initialises on the first call (or after reset_rollout()). Each call returns a SimulationResult for that window, whose times are absolute (offset by the accumulated rollout clock).

create(model_cls, size=1, *, params=None, positions=None, **kw)[source]#

Instantiate a population/device and return a NodeView.

Generators are deferred (realised per target at connect()) so each target receives an independent train, mirroring NEST fan-out.

positions=<spatial layer> attaches node coordinates to a neuron population (NEST Create(model, positions=spatial.grid/free(...))). A grid() / concrete free() layer derives the population size from its coordinates (the size argument is ignored); a deferred free layer (sampled from a distribution) draws size positions. The coordinates are stored under id(population) so a spatial_pairwise_bernoulli() rule can be bound to them at connect().

get_connections(source=None, target=None, synapse=None)[source]#

Enumerate realized synapses across projections (NEST GetConnections).

Returns a SynapseCollection over the edges matching the filters, letting you read and write weights / delays without holding each projection handle. The collection is a lazy view: weights and delays are re-read from the live projections on each get(), so a query made before simulate() still reflects post-simulation evolved plastic weights.

Parameters:
  • source (NodeView, optional) – Keep only edges whose presynaptic neuron lies in this view (matched by population identity and population-local index). None keeps all.

  • target (NodeView, optional) – Keep only edges whose postsynaptic neuron lies in this view. None keeps all.

  • synapse (str, optional) – Keep only projections with this synapse-model name ('static_synapse' for the static event path, else the plastic spec’s class name such as 'stdp_synapse'). None keeps all.

Returns:

A filtered, lazy view over the matching edges (empty if none match).

Return type:

SynapseCollection

See also

connect

Build the projections this enumerates.

Examples

>>> import brainunit as u
>>> from brainpy import state as bp
>>> sim = bp.Simulator(dt=0.1 * u.ms)
>>> exc = sim.create(bp.iaf_psc_exp, 4)
>>> _ = sim.connect(exc, exc, rule=bp.all_to_all, weight=20. * u.pA,
...                 allow_autapses=False, comm='sparse')
>>> conns = sim.get_connections(source=exc, target=exc)
>>> len(conns)
12
>>> bool(u.math.allclose(conns.get('weight'), 20. * u.pA))
True
get_position(view)[source]#

Node coordinates of a spatial population/view (NEST GetPosition).

Parameters:

view (NodeView) – A single-segment view over a population created with create(positions=...).

Returns:

(n, ndim) coordinates (length units) in the view’s node order.

Return type:

Quantity

Raises:

ValueError – If the population was not created with positions=.

Examples

>>> from brainpy import state as bp
>>> import brainunit as u
>>> sim = bp.Simulator(dt=0.1 * u.ms)
>>> pop = sim.create(bp.iaf_psc_alpha, positions=bp.spatial.grid([4, 3], extent=[2.0, 1.5]))
>>> [round(float(v), 2) for v in u.get_magnitude(sim.get_position(pop).to(u.um))[0]]
[-0.75, 0.5]
record_weight(proj)[source]#

Register a per-step weight tap on a plastic projection.

proj is the handle returned by connect(..., synapse=spec). After simulate(), read the stacked (n_steps, n_edges) weight trajectory (CSR sorted-by-pre edge order) via SimulationResult.weight_trace(). Mirrors the analog-recorder tap, but reads the projection’s weight State rather than a population’s recordable.

Returns:

The same proj, for chaining.

Return type:

EventPlasticProj

Raises:

TypeError – If proj is not a plastic projection (only connect(..., synapse=) builds one; the static path has no plastic weight State to record).

reset_rollout(*, dt=None)[source]#

Initialise all state and start a fresh persistent rollout at t = 0.

Calls brainstate.nn.init_all_states(self) and zeroes the accumulated rollout clock (_base_t / _base_i). simulate() calls this for you; call it explicitly before a cont() loop to (re)start cleanly.

simulate(duration, *, dt=None)[source]#

Run for duration from a freshly initialised state.

Spike recorders are stacked as (n_steps, n_recorded) arrays; analog recorders (voltmeter / multimeter) tap their target population’s State each step (after the update) into (n_steps, n_recorded) traces keyed by recordable. The run’s time axis is exposed as res.times.

This re-initialises ALL state (init_all_states) and runs one window from t = 0. To continue a rollout across windows without re-initialising — interleaving host-side work between chunks (read recordings, rewrite host_drive schedules, overwrite static weights) — use cont().

tripartite_connect(pre, post, third, *, conn_spec, third_factor_conn_spec, syn_specs=None, seed=None, comm='dense', allow_autapses=True, allow_multapses=True)[source]#

Wire a tripartite pre -> post + astrocyte network (NEST TripartiteConnect).

Samples the primary pre -> post edges once via conn_spec and shares that single realization across three projections (NEST’s tripartite semantics):

  1. primary pre -> post (the direct synapse, syn_specs['primary']).

  2. For each realized primary edge (pre_i -> post_j), the third_factor_conn_spec (third_factor_bernoulli_with_pool()) runs a Bernoulli(p) trial; if it succeeds the edge is paired with one astrocyte drawn from post_j’s pool, creating third_in pre_i -> astro (syn_specs['third_in'], delta IP3) and third_out astro -> post_j (syn_specs['third_out'], a sic_connection).

Reuses the existing static EventProjection path for primary + third_in and the merged sic_connection (as_current) path for third_out; no new deposit primitive. Each arm is registered for get_connections().

Parameters:
  • pre (NodeView) – Single-population views for the presynaptic, postsynaptic, and astrocyte populations. pre may be a prefix slice (e.g. the Brunel excitatory population neurons[:N_ex]).

  • post (NodeView) – Single-population views for the presynaptic, postsynaptic, and astrocyte populations. pre may be a prefix slice (e.g. the Brunel excitatory population neurons[:N_ex]).

  • third (NodeView) – Single-population views for the presynaptic, postsynaptic, and astrocyte populations. pre may be a prefix slice (e.g. the Brunel excitatory population neurons[:N_ex]).

  • conn_spec (ConnRule) – The primary one-directional rule, e.g. pairwise_bernoulli() or fixed_indegree().

  • third_factor_conn_spec (_ThirdFactorBernoulliWithPool) – The astrocyte-pool rule from third_factor_bernoulli_with_pool().

  • syn_specs (dict, optional) – {'primary': {...}, 'third_in': {...}, 'third_out': {...}}; each value is a dict of connect() keyword arguments (weight, delay, receptor_type, synapse). third_out typically carries {'synapse': sic_connection(...)}. Missing entries default to {} (a plain unit-weight static delta).

  • seed (int, optional) – Base PRNG seed for the whole tripartite sample (primary + pairing + pool). The same seed reproduces the same realized edges.

  • comm ({'dense', 'sparse'}, default 'dense') – Communication mode for the primary / third_in arms; third_out (the sic_connection) always rides 'dense' (a graded current).

  • allow_autapses (bool, default True) – Passed to the primary conn_spec.sample.

  • allow_multapses (bool, default True) – Passed to the primary conn_spec.sample.

Returns:

(primary_proj, third_in_proj, third_out_proj). third_in_proj and third_out_proj are None when no primary edge was paired (e.g. p=0).

Return type:

tuple

Examples

>>> import brainunit as u
>>> from brainpy import state as bp
>>> sim = bp.Simulator(dt=0.1 * u.ms)
>>> pre = sim.create(bp.aeif_cond_alpha_astro, 10, params={'I_e': 1000.0 * u.pA})
>>> post = sim.create(bp.aeif_cond_alpha_astro, 10, params={'I_e': 1000.0 * u.pA})
>>> astro = sim.create(bp.astrocyte_lr_1994, 5, params={'delta_IP3': 0.2})
>>> prim, tin, tout = sim.tripartite_connect(
...     pre, post, astro,
...     conn_spec=bp.pairwise_bernoulli(1.0),
...     third_factor_conn_spec=bp.third_factor_bernoulli_with_pool(
...         p=1.0, pool_size=1, pool_type='block'),
...     syn_specs={'primary': {'weight': 1.0 * u.nS, 'receptor_type': 1},
...                'third_in': {'weight': 1.0},
...                'third_out': {'synapse': bp.sic_connection(weight=1.0)}},
...     seed=0)
>>> tout._channel_label
'I_SIC'