| name | brainevent |
| description | BrainEvent is the event-driven communication and plasticity layer for BrainX spiking models. Use this skill to translate neural projections, synaptic efficacy, binary firing events, spike-driven postsynaptic input, fixed or probabilistic fan-in or fan-out, and activity-dependent weight changes into `BinaryArray`, dense, CSR, CSC, JITC, fixed-degree connectivity, and plasticity operators, or to route custom event operators. |
Purpose and boundary
Use BrainEvent to represent binary spikes and communicate them through dense, explicit sparse, generated, or fixed-degree connectivity. Route neuron, synapse, channel, state, and neural-mass dynamics to their owning BrainX packages; never use BinaryArray for analog activity or BrainEvent as a complete simulator.
Underlying principle of BrainEvent
BinaryArray represents neuron spikes as boolean or 0/1 data. In spikes @ connectivity, BrainEvent processes only active presynaptic spikes and accumulates their weighted contributions into postsynaptic input.
Connectivity represents synaptic wiring. It states which presynaptic neurons project to which postsynaptic neurons; its weights state each synapse's sign and strength.
Plasticity operators represent activity-dependent changes in synaptic strength. Pre- and postsynaptic spikes and spike traces update stored weights.
Represent and multiply binary events
BinaryArray wraps boolean or 0/1 event data so multiplication visits active presynaptic rows and preserves a uniform call site across connectivity representations.
| API | Description |
|---|
BinaryArray(value) | Use at the binary event boundary; it wraps a boolean or 0/1 vector or matrix, dispatches later products to event-driven implementations, and remains compatible with JAX transformations. |
spikes @ connectivity | Use after the spike dimension and connectivity input dimension agree; it accumulates the connectivity rows selected by active events and returns the weighted postsynaptic input. |
import brainevent
import jax.numpy as jnp
pre_spikes = brainevent.BinaryArray([1, 0, 1, 0, 1])
weights = jnp.array([
[0.5, 0.2, 0.1],
[0.3, 0.4, 0.2],
[0.1, 0.5, 0.3],
[0.2, 0.1, 0.4],
[0.4, 0.3, 0.5],
])
post_input = pre_spikes @ weights
active_rows = jnp.array([0, 2, 4])
assert jnp.allclose(post_input, weights[active_rows].sum(axis=0))
Use a dense array only when its storage cost is acceptable. Open references/sparse-formats.md when explicit edges should be compressed, or references/connectivity-variants.md when random or fixed-degree structure should not be materialized densely.
API structure overview
Python API
| Category | Responsibility |
|---|
| Event Array Types | Represent binary events with EventRepresentation and BinaryArray. |
| Sparse Matrix Data Structures | Store explicit, generated, or fixed-degree connectivity with CSR, CSC, JITC*, and fixed-connection structures. |
| Matrix Operations | Apply event-driven dense, sparse, generated, fixed-connectivity, and plasticity operations. |
| Custom Kernel Framework | Define and load JAX-compatible custom kernels for CPU and accelerator backends. |
| Error Classes | Diagnose mathematical, kernel availability, compilation, fallback, execution, and CUDA installation failures. |
| Utility Functions | Convert sparse indices, generate LFSR random values, benchmark operations, and define transformation or type-conversion helpers. |
| Configuration API | Configure Numba parallelism, LFSR selection, and backend behavior. |
Custom kernels
| Category | Responsibility |
|---|
arg_spec System | Declare kernel arguments, returned buffers, streams, and scalar attributes. |
| C++ API | Use BrainEvent tensors, dtypes, validation macros, and dispatch macros in C++ kernels. |
| Compiler Options | Control optimization, fast math, extra compiler flags, and CUDA graph support. |
| Caching | Control compiled-kernel cache keys, storage, rebuilds, and reuse. |
Choose a connectivity representation
Choose the representation before constructing it; every family supports the same BinaryArray @ connectivity call site but has a different storage and mutation contract.
| Representation | Use when | Avoid when |
|---|
| Dense JAX/NumPy array | The matrix is small or genuinely dense, roughly more than 25% nonzero, or arbitrary per-edge values require the simplest storage. | A large matrix is mostly zero. |
CSR / CSC | Edges are explicit, fixed, reusable, inspectable, or mutable. Use CSR for row-oriented work and CSC for column-oriented work. | Random connectivity is too large to materialize. |
JITC* | Connectivity is random with fixed probability and must be regenerated from compact parameters and a stable seed. | Individual edges must be inspected, mutated, or learned. |
FixedNumPerPre / FixedNumPerPost | Each neuron has a fixed number of outputs or inputs and that topology should be encoded directly. | Connection counts vary per neuron. |
Use explicit CSR or CSC for stored edges, JITC* for random and huge connectivity, and fixed-degree structures for constant fan-in or fan-out.
Build explicit sparse connectivity
CSR and CSC store explicit sparse connectivity for row- or column-oriented operations.
| API | Description |
|---|
CSR(data, indices=None, indptr=None, *, shape, ...) | Use for explicit row-oriented sparse connectivity and the normal forward BinaryArray @ connectivity path; it stores nonzero values, column indices, and row pointers. |
CSC(data, indices=None, indptr=None, *, shape, ...) | Use for explicit column-oriented sparse connectivity or transpose-centered work; it stores nonzero values, row indices, and column pointers. |
import brainevent
import jax.numpy as jnp
connectivity = brainevent.CSR(
(
jnp.array([0.5, 0.2, 0.7, 0.4]),
jnp.array([0, 1, 0, 1]),
jnp.array([0, 1, 2, 3, 4]),
),
shape=(4, 2),
)
spikes = brainevent.BinaryArray([1, 0, 1, 0])
postsynaptic_input = spikes @ connectivity
assert postsynaptic_input.shape == (2,)
This example constructs only CSR; do not duplicate it for every format. Open references/sparse-formats.md when explicit edges must be imported, CSC orientation is required, or stored formats must be converted.
Generate random connectivity
JITC matrices store a probability, weight-distribution parameters, and a seed, then regenerate the required connections during computation instead of storing individual edges.
| API | Description |
|---|
JITCScalarR(weight, prob=None, seed=None, *, shape, ...) | Use for row-oriented connectivity with one shared nonzero weight; it regenerates a reproducible graph from prob and seed. |
JITCScalarC(weight, prob=None, seed=None, *, shape, ...) | Use for the column-oriented form of shared-weight generated connectivity. |
JITCNormalR(loc, scale=None, prob=None, seed=None, *, shape, ...) | Use for row-oriented generated weights drawn from a normal distribution. |
JITCNormalC(loc, scale=None, prob=None, seed=None, *, shape, ...) | Use for the column-oriented normal-weight form. |
JITCUniformR(low, high=None, prob=None, seed=None, *, shape, ...) | Use for row-oriented generated weights bounded by a uniform distribution. |
JITCUniformC(low, high=None, prob=None, seed=None, *, shape, ...) | Use for the column-oriented uniform-weight form. |
import brainevent
import jax.numpy as jnp
n_pre = 100_000
n_post = 100_000
connectivity = brainevent.JITCScalarR(
(0.5, 0.01, 7),
shape=(n_pre, n_post),
)
spikes = brainevent.BinaryArray(
jnp.zeros(n_pre, dtype=bool).at[::1000].set(True)
)
postsynaptic_input = spikes @ connectivity
assert postsynaptic_input.shape == (n_post,)
This example constructs only JITCScalarR. Keep the seed stable when the realized graph must remain reproducible. Open references/connectivity-variants.md when choosing a weight distribution, row/column orientation, or benchmarking an uncertain contraction.
Encode fixed-degree connectivity
Fixed-degree structures store one connection count per relevant neuron population, so choose the class by whether the invariant is fan-in or fan-out.
| API | Description |
|---|
FixedNumPerPre(data, indices=None, *, shape, ...) | Use when every presynaptic neuron has the same number of outputs; it stores data and target indices with shape (num_pre, connections_per_pre). |
FixedNumPerPost(data, indices=None, *, shape, ...) | Use when every postsynaptic neuron receives the same number of inputs; it stores data and source indices with shape (num_post, connections_per_post). |
FixedPostNumConn | Recognize as the deprecated alias of FixedNumPerPre; migrate new code to the current name. |
FixedPreNumConn | Recognize as the deprecated alias of FixedNumPerPost; migrate new code to the current name. |
import brainevent
import jax.numpy as jnp
connectivity = brainevent.FixedNumPerPre(
(
jnp.array([
[0.5, 0.2],
[0.4, 0.1],
[0.3, 0.6],
[0.7, 0.2],
]),
jnp.array([
[0, 2],
[1, 2],
[0, 1],
[1, 2],
]),
),
shape=(4, 3),
)
spikes = brainevent.BinaryArray([1, 0, 1, 0])
postsynaptic_input = spikes @ connectivity
assert postsynaptic_input.shape == (3,)
This example constructs only FixedNumPerPre. Open references/connectivity-variants.md when fixed fan-in is required, a deprecated alias appears in existing code, or the stored index shape must be checked.
Apply event-driven synaptic plasticity
Plasticity combines binary spike triggers with decaying activity traces to update only stored synaptic weights; keep the connectivity topology fixed and choose the operator by storage format and trigger direction.
| API | Description |
|---|
update_csr_on_binary_pre(...) | Use when presynaptic spikes trigger updates to explicit CSR weights; it visits stored outgoing synapses, applies the postsynaptic trace and bounds, and returns updated CSR data without changing indices or indptr. |
update_csr_on_binary_post(...) | Use when postsynaptic spikes trigger updates to weights stored in CSR order; provide a CSC view plus weight_indices mapping CSC positions back to CSR data, and it returns the updated CSR-ordered values. |
update_dense_on_binary_pre(...) | Use for a small dense matrix when presynaptic spikes trigger updates; it returns the updated dense weights. |
update_dense_on_binary_post(...) | Use for a small dense matrix when postsynaptic spikes trigger updates; it returns the updated dense weights. |
import brainevent
import jax.numpy as jnp
weights = brainevent.CSR(
(
jnp.array([0.2, 0.4, 0.3, 0.5]),
jnp.array([0, 1, 1, 0]),
jnp.array([0, 2, 3, 4]),
),
shape=(3, 2),
)
new_data = brainevent.update_csr_on_binary_pre(
weight=weights.data,
indices=weights.indices,
indptr=weights.indptr,
pre_spike=jnp.array([True, False, True]),
post_trace=jnp.array([0.02, 0.01]),
w_min=0.0,
w_max=1.0,
shape=weights.shape,
)
updated = brainevent.CSR(
(new_data, weights.indices, weights.indptr),
shape=weights.shape,
)
assert updated.data.shape == weights.data.shape
assert jnp.array_equal(updated.indices, weights.indices)
assert jnp.array_equal(updated.indptr, weights.indptr)
Open references/synaptic-plasticity.md when implementing decaying traces, a CSR STDP loop, or a complete adaptive network; open the operations API linked there for exact postsynaptic and dense update signatures.
Transform and verify the product
Keep the complete event-driven product inside the JAX transform, then verify shape, orientation, and reproducibility rather than relying on a class suffix alone.
| API | Description |
|---|
jax.jit(function) | Use to compile a complete event-driven product; it traces BinaryArray and connectivity PyTrees and returns a compiled callable for compatible shapes. |
jax.vmap(function, ...) | Use to batch independent event-driven products; it maps the same communication rule over the selected array axis. |
brainevent.benchmark_function(function, ...) | Use when row- versus column-oriented performance is uncertain; it benchmarks the actual workload and returns timing statistics. |
import jax
@jax.jit
def communicate(spikes, connectivity):
return spikes @ connectivity
postsynaptic_input = communicate(spikes, connectivity)
Verify postsynaptic_input.shape == connectivity.shape[1:] for vector input and confirm that repeated JITC runs with the same seed reproduce the intended graph.
Reference routing
| Reference | Open when |
|---|
references/sparse-formats.md | Open when explicit connectivity must be imported, converted between CSR and CSC, or oriented for row or column access; it contains the construction, conversion, and storage invariants. |
references/connectivity-variants.md | Open when choosing among all six JITC distribution/orientation variants or between fixed fan-in and fan-out; it contains constructor semantics, index shapes, deprecated alias mapping, seed rules, and benchmarking guidance. |
references/synaptic-plasticity.md | Open when pre- or postsynaptic events must update stored CSR or dense weights; it contains all four public update variants and one CSR STDP pattern. |
references/custom-operators-cpu.md | Open when a custom operation targets CPU; it contains Numba CPU, raw C++, CPU registration and transformation rules, the C++ ABI, compilation, caching, diagnostics, and verification workflows. |
references/custom-operators-gpu.md | Open when a custom operation targets GPU; it contains Numba CUDA, Warp, raw CUDA, GPU and multi-backend registration, stream and ABI rules, compiler controls, caching, diagnostics, and verification workflows. |
Application script examples
| Reference | Open when |
|---|
references/scripts/coba_ei_teaching.py | Open for the shared BrainEvent and BrainPy teaching example; it uses BinaryArray with interchangeable FixedNumPerPre, CSR, and dense connectivity for efficient event-driven communication into BrainPy LIFRef, Expon, and COBA dynamics inside one compiled BrainState loop. |
references/scripts/102_EI_net_1996.py | Open for a complete high-level E/I network already using brainpy.state.AlignPostProj and brainstate.nn.EventFixedProb; it preserves unit-aware weights, initialization, compiled time loops, and visualization. |
references/scripts/204_joglekar_2018_propagation.py | Open for delayed spikes, area mapping, and vmapped JITCScalarC communication; it preserves delays, seeds, external-data assumptions, and BrainPy compatibility details. |