pub struct SynapticMesh { /* private fields */ }Expand description
Top-level synaptic wiring orchestrator.
Owns the network topology (graph with weights, delays, polarities) and the temporal delay buffer. Converts source spikes into time-delayed synaptic currents delivered to target neurons.
Deserialization requires the graph and buffer neuron counts to agree,
the buffer capacity to be at least the graph’s maximum delay, and
tick to equal delay_buffer.current_tick. Both ticks must leave
headroom so one propagate cannot land on usize::MAX. Inconsistent
timestamps are rejected rather than repaired.
Implementations§
Source§impl SynapticMesh
impl SynapticMesh
Sourcepub fn new(graph: SynapticGraph) -> Self
pub fn new(graph: SynapticGraph) -> Self
Create a new mesh from a pre-built graph.
The delay buffer is sized to the graph’s maximum delay.
Sourcepub fn with_max_delay(graph: SynapticGraph, max_delay: usize) -> Self
pub fn with_max_delay(graph: SynapticGraph, max_delay: usize) -> Self
Create a mesh with a custom maximum delay (overriding graph’s max).
Useful when you want headroom for future dynamic delay changes.
max_delay must be at least graph.max_delay(). A smaller buffer
is rejected at construction — before any propagate call — rather
than wrapping an oversized delay onto an earlier tick. Use
SynapticMesh::new to size the buffer from the graph automatically,
or SynapticMesh::try_with_max_delay for a recoverable error.
§Panics
Panics if max_delay is smaller than the graph’s maximum delay, or
if max_delay + 1 overflows usize.
Sourcepub fn try_with_max_delay(
graph: SynapticGraph,
max_delay: usize,
) -> Result<Self>
pub fn try_with_max_delay( graph: SynapticGraph, max_delay: usize, ) -> Result<Self>
Fallible counterpart of SynapticMesh::with_max_delay.
Returns MeshError::DelayError when max_delay cannot hold every
synapse delay in graph, or when the ring depth would overflow.
Sourcepub fn propagate(&mut self, source_spikes: &[bool]) -> Result<Vec<f32>>
pub fn propagate(&mut self, source_spikes: &[bool]) -> Result<Vec<f32>>
Propagate spikes through the mesh for one tick.
§Arguments
source_spikes— boolean spike vector:trueif neuronifired this tick. Length must equalneuron_count().
§Returns
Synaptic current vector of length neuron_count() — the total
incoming current at each neuron for this tick (including delayed
spikes from previous ticks).
§Delivery contract
- A spike from neuron
son ticktdelivers current to every target ofson tickt + delay. Adelayof 0 arrives in the return value of this very call. This assumes the delay buffer can hold the graph’s delays, whichSynapticMesh::newguarantees.with_max_delayrejects a buffer smaller than the graph’s longest delay at construction (debug and release). - The magnitude is the synapse’s weight and the sign is that
synapse’s
Polarity, so an inhibitory synapse subtracts. The generators assign polarity per source neuron (Dale’s law), butSynapticGraph::from_descriptorstakes each descriptor’s polarity as given and does not check that one source’s synapses agree.
3. Currents arriving at the same neuron on the same tick are summed.
4. Propagation is one hop: received current never becomes a spike
by itself. You own the neuron model — threshold the returned
currents and feed the result into the next call to build a
multi-layer loop.
5. Delivery is deterministic: the same graph and the same spike
sequence produce the same currents, including after reset.
use synaptic_wiring::mesh::SynapticMesh;
use synaptic_wiring::topology::SynapticGraph;
use synaptic_wiring::types::{Polarity, SynapseDescriptor};
// 0 ──(w=0.75, delay 0, excitatory)──▶ 1
// 0 ──(w=0.50, delay 2, excitatory)──▶ 2
let graph = SynapticGraph::from_descriptors(
3,
&[
SynapseDescriptor { source: 0, target: 1, weight: 0.75, delay: 0, polarity: Polarity::Excitatory },
SynapseDescriptor { source: 0, target: 2, weight: 0.50, delay: 2, polarity: Polarity::Excitatory },
],
)?;
let mut mesh = SynapticMesh::new(graph);
// Tick 0: neuron 0 fires — only the delay-0 synapse lands.
assert_eq!(mesh.propagate(&[true, false, false])?, vec![0.0, 0.75, 0.0]);
// Tick 1: nothing in flight arrives yet.
assert_eq!(mesh.propagate(&[false; 3])?, vec![0.0, 0.0, 0.0]);
// Tick 2: the delay-2 synapse arrives at neuron 2.
assert_eq!(mesh.propagate(&[false; 3])?, vec![0.0, 0.0, 0.50]);tests/propagate_contract.rs pins this contract — destination, sign,
magnitude, and delivery tick — against a fixed four-neuron graph, and
is a copyable starting point for your own simulation loop.
This allocating wrapper is source-compatible with existing callers.
Fixed-rate loops that already own an output buffer should use
Self::propagate_into instead.
Sourcepub fn propagate_into(
&mut self,
source_spikes: &[bool],
output: &mut [f32],
) -> Result<()>
pub fn propagate_into( &mut self, source_spikes: &[bool], output: &mut [f32], ) -> Result<()>
Propagate spikes into a caller-owned output buffer.
Functionally identical to Self::propagate: same destinations,
signs, magnitudes, and delivery ticks. Writes this tick’s synaptic
currents into output instead of allocating a new Vec<f32>.
After Self::new, a successful call performs no heap allocations.
§Buffer contract
source_spikesandoutputmust both have lengthneuron_count().outputis overwritten with this tick’s delivered currents. It is not accumulated into; values present before the call are discarded on success.source_spikes(&[bool]) andoutput(&mut [f32]) cannot alias. Rust’s borrow checker rejects overlapping borrows of the same memory.
§Error atomicity
If this method returns Err, the mesh tick, delay buffers, and
output are left unchanged. Length mismatches are rejected before any
mutation.
use synaptic_wiring::mesh::SynapticMesh;
use synaptic_wiring::topology::SynapticGraph;
use synaptic_wiring::types::{Polarity, SynapseDescriptor};
let graph = SynapticGraph::from_descriptors(
2,
&[SynapseDescriptor {
source: 0,
target: 1,
weight: 0.75,
delay: 0,
polarity: Polarity::Excitatory,
}],
)?;
let mut mesh = SynapticMesh::new(graph);
let spikes = [true, false];
let mut currents = [0.0; 2];
mesh.propagate_into(&spikes, &mut currents)?;
assert_eq!(currents, [0.0, 0.75]);Sourcepub fn propagate_graded(
&mut self,
source_activations: &[f32],
) -> Result<Vec<f32>>
pub fn propagate_graded( &mut self, source_activations: &[f32], ) -> Result<Vec<f32>>
Propagate with floating-point spike strengths instead of binary.
§Arguments
source_activations— activation level for each neuron. Non-zero values are treated as spikes; the activation value scales the synaptic weight. Finite signed activations are allowed (a negative activation inverts the delivered current). NaN, ±infinity, non-finiteweight * activationproducts, and non-finite per-slot aggregates (including current already in the delay buffer) are rejected before any buffer or tick mutation.
This allocating wrapper is source-compatible with existing callers.
Fixed-rate loops that already own an output buffer should use
Self::propagate_graded_into instead.
Sourcepub fn propagate_graded_into(
&mut self,
source_activations: &[f32],
output: &mut [f32],
) -> Result<()>
pub fn propagate_graded_into( &mut self, source_activations: &[f32], output: &mut [f32], ) -> Result<()>
Propagate graded activations into a caller-owned output buffer.
Functionally identical to Self::propagate_graded: same destinations,
signs, magnitudes, and delivery ticks, including the same rejection of
non-finite activations, products, and per-slot aggregates. Writes this
tick’s synaptic currents into output instead of allocating a new
Vec<f32>. After Self::new, a successful call reuses internal
scratch and performs no heap allocations.
§Buffer contract
source_activationsandoutputmust both have lengthneuron_count().outputis overwritten with this tick’s delivered currents. It is not accumulated into; values present before the call are discarded on success.source_activationsandoutputcannot alias. Rust’s borrow checker rejects overlapping&[f32]/&mut [f32]borrows of the same memory, so in-place scaling of the output slice is not possible through this API.
§Error atomicity
If this method returns Err, the mesh tick, delay buffers, and
output are left unchanged. Length mismatches and non-finite
activations, products, or aggregates are rejected before any mutation.
Sourcepub fn neuron_count(&self) -> usize
pub fn neuron_count(&self) -> usize
Number of neurons in the mesh.
Sourcepub fn synapse_count(&self) -> usize
pub fn synapse_count(&self) -> usize
Total number of synapses.
Sourcepub fn mean_degree(&self) -> f32
pub fn mean_degree(&self) -> f32
Mean out-degree of the network.
Sourcepub fn graph(&self) -> &SynapticGraph
pub fn graph(&self) -> &SynapticGraph
Immutable access to the underlying graph.
Sourcepub fn topology_digest(&self) -> TopologyDigest
pub fn topology_digest(&self) -> TopologyDigest
Deterministic digest of the logical topology (not tick or delay-buffer state).
Equivalent to SynapticGraph::topology_digest on Self::graph.
Trait Implementations§
Source§impl Clone for SynapticMesh
impl Clone for SynapticMesh
Source§fn clone(&self) -> SynapticMesh
fn clone(&self) -> SynapticMesh
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more