stdp_facetshw_synapse_hom

stdp_facetshw_synapse_hom#

class brainpy.state.stdp_facetshw_synapse_hom(*args, **kwargs)#

FACETS/BrainScaleS hardware STDP synapse spec (NEST stdp_facetshw_synapse_hom).

A hardware-constrained STDP model with a 4-bit discrete weight updated only at periodic readout events, not at every spike. Between readouts two analogue charges accumulate from nearest-neighbour pre/post pairings:

\[ \begin{align}\begin{aligned}a_\text{causal} \mathrel{+}= \exp\!\Big(\tfrac{-(t_\text{post}-t_\text{pre})}{\tau_+}\Big) \quad\text{(first post since the last pre)}\\a_\text{acausal} \mathrel{+}= \exp\!\Big(\tfrac{-(t_\text{pre}-t_\text{post})}{\tau_-}\Big) \quad\text{(nearest post before this pre)}\end{aligned}\end{align} \]

At a readout (the first pre spike that crosses next_readout) the weight is quantised to a LUT index e = round(w / w_\text{ple}), two boolean evaluation functions compare the charges against thresholds, and one look-up table maps e:

\[\text{eval}_k = \frac{a_{tl} + b^k_2 a_\text{causal} + b^k_1 a_\text{acausal}} {1 + b^k_2 + b^k_1} > \frac{a_{th} + b^k_0 a_\text{causal} + b^k_3 a_\text{acausal}} {1 + b^k_0 + b^k_3}\]

with config bits \(b^k = `\ ``configbit_k`\). (eval0, eval1) selects the table: (T,F)->lookuptable_0 (potentiation), (F,T)->lookuptable_1 (depression), (T,T)->lookuptable_2 (default identity), (F,F) leaves the index unchanged. The new weight is e' * w_\text{ple} — so the weight is re-quantised at every readout even when no table fires. reset_pattern then zeroes the charges (per branch, causal/acausal), and next_readout advances by whole readout_cycle_durations past the spike. With the default config bits and a_thresh_th == a_thresh_tl, eval0 reduces to a_causal > a_thresh_th and eval1 to a_acausal > a_thresh_th.

Warning

Default-weight footgun. With Wmax=100 the quantum is w_ple = Wmax/15 6.667; the default weight=1.0 quantises to index round(1/6.667)=0 and the first readout zeroes it. Choose a weight on (or near) the LUT grid — e.g. 5 * Wmax/15 33.33.

Parameters:
  • weight (ArrayLike or Quantity, optional) – Per-edge weight (pA; bare numbers are pA). Default 1.0 pA — see the footgun warning above.

  • delay (Quantity, optional) – Homogeneous axonal delay (> 0). Default 1.0 ms.

  • receptor_type (int, optional) – Postsynaptic receptor port (>= 0). Default 0.

  • tau_plus (Quantity, optional) – Causal-charge (K+) trace constant (> 0). Default 20.0 ms.

  • tau_minus (Quantity, optional) – Acausal-charge (K-) trace constant (> 0). Default 20.0 ms.

  • Wmax (float, optional) – Weight bound, used for the default quantum. Default 100.0.

  • a_thresh_th (float, optional) – Upper/lower charge thresholds in the evaluation functions. Default 21.835.

  • a_thresh_tl (float, optional) – Upper/lower charge thresholds in the evaluation functions. Default 21.835.

  • lookuptable_0 (sequence of int, optional) – The 16-entry weight-update tables (potentiation / depression / identity). Each entry must index back into the table ([0, 15]).

  • lookuptable_1 (sequence of int, optional) – The 16-entry weight-update tables (potentiation / depression / identity). Each entry must index back into the table ([0, 15]).

  • lookuptable_2 (sequence of int, optional) – The 16-entry weight-update tables (potentiation / depression / identity). Each entry must index back into the table ([0, 15]).

  • configbit_0 (sequence of int, optional) – Four 0/1 bits parameterising eval0 / eval1.

  • configbit_1 (sequence of int, optional) – Four 0/1 bits parameterising eval0 / eval1.

  • reset_pattern (sequence of int, optional) – Six 0/1 flags: reset (causal, acausal) after a lookuptable_0 / _1 / _2 update respectively. Default all-on.

  • weight_per_lut_entry (float, optional) – The weight quantum. Default Wmax / (lut_size - 1).

  • synapses_per_driver (int, optional) – Synapses served by one hardware weight-update controller. Default 50.

  • driver_readout_time (float, optional) – Time (ms) one controller takes per synapse. Default 15.0.

Notes

Single-driver scope. For a single edge (and any edge count up to synapses_per_driver) NEST’s readout_cycle_duration = int((no_synapses-1)/synapses_per_driver + 1) * driver_readout_time collapses to driver_readout_time and every synapse’s first readout offset is 0, which is what this spec models (next_readout starts at 0). True multi-driver round-robin grouping for E > synapses_per_driver (staggered per-synapse offsets) is out of scope.

Deferred accumulation. NEST accumulates the send’s pairing after the readout, so a readout never sees the charge from the pair that triggered it. The kernel reproduces this by capturing the causal term at the post (causal_pending) and folding it — together with the acausal term — only at the next pre, after that pre’s readout. Exact pre/post coincidence follows the substrate second-latest convention and is not asserted against NEST.

Parity note. The charge-accumulation / LUT-readout pairing convention, the NEST keys and single-driver scope, and the parity test are documented in STDP parity: where state lives and how spikes pair (stdp_facetshw_synapse_hom — hardware (FACETS) charge accumulation + LUT readout).

References

Examples

>>> import brainunit as u
>>> from brainpy.state import stdp_facetshw_synapse_hom
>>> s = stdp_facetshw_synapse_hom(weight=33.333, Wmax=100.0)
>>> round(s.weight_per_lut_entry, 3)
6.667
>>> s._weight_to_entry(33.333)        # nearest 4-bit index
5
>>> sorted(s.edge_state_init())
['a_acausal', 'a_causal', 'causal_pending', 'next_readout', 'post_seen', 'pre_seen']