Skip to main content

SynapticMesh

Struct SynapticMesh 

Source
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

Source

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.

Source

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.

Source

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.

Source

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: true if neuron i fired this tick. Length must equal neuron_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
  1. A spike from neuron s on tick t delivers current to every target of s on tick t + delay. A delay of 0 arrives in the return value of this very call. This assumes the delay buffer can hold the graph’s delays, which SynapticMesh::new guarantees. with_max_delay rejects a buffer smaller than the graph’s longest delay at construction (debug and release).
  2. 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), but SynapticGraph::from_descriptors takes 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.

Source

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_spikes and output must both have length neuron_count().
  • output is overwritten with this tick’s delivered currents. It is not accumulated into; values present before the call are discarded on success.
  • source_spikes (&[bool]) and output (&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]);
Source

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-finite weight * activation products, 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.

Source

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_activations and output must both have length neuron_count().
  • output is overwritten with this tick’s delivered currents. It is not accumulated into; values present before the call are discarded on success.
  • source_activations and output cannot 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.

Source

pub fn tick(&self) -> u64

Current simulation tick.

Source

pub fn neuron_count(&self) -> usize

Number of neurons in the mesh.

Source

pub fn synapse_count(&self) -> usize

Total number of synapses.

Source

pub fn sparsity(&self) -> f32

Sparsity of the connectivity matrix.

Source

pub fn max_delay(&self) -> usize

Maximum delay in ticks.

Source

pub fn mean_degree(&self) -> f32

Mean out-degree of the network.

Source

pub fn graph(&self) -> &SynapticGraph

Immutable access to the underlying graph.

Source

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.

Source

pub fn reset(&mut self)

Reset delay buffer and tick counter.

Source

pub fn to_gpu_arrays(&self) -> (Vec<u32>, Vec<u32>, Vec<f32>, Vec<u16>)

Export the CSR arrays + delays for GPU upload.

Trait Implementations§

Source§

impl Clone for SynapticMesh

Source§

fn clone(&self) -> SynapticMesh

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SynapticMesh

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for SynapticMesh

Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for SynapticMesh

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.