Skip to main content

sc_neurocore_engine/neurons/hardware/
spinnaker_lif.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — SpiNNaker LIF Neuron Emulator
8
9/// SpiNNaker LIF — ARM Cortex-M4 digital LIF with refractory. Furber et al. 2014.
10#[derive(Clone, Debug)]
11pub struct SpiNNakerLIFNeuron {
12    pub v: f64,
13    pub v_rest: f64,
14    pub v_reset: f64,
15    pub v_threshold: f64,
16    pub tau_m: f64,
17    pub i_offset: f64,
18    pub tau_refrac: f64,
19    pub refrac_count: f64,
20    pub dt: f64,
21}
22
23impl SpiNNakerLIFNeuron {
24    pub fn new() -> Self {
25        Self {
26            v: -70.0,
27            v_rest: -70.0,
28            v_reset: -70.0,
29            v_threshold: -50.0,
30            tau_m: 20.0,
31            i_offset: 0.0,
32            tau_refrac: 2.0,
33            refrac_count: 0.0,
34            dt: 1.0,
35        }
36    }
37    pub fn step(&mut self, current: f64) -> i32 {
38        if self.refrac_count > 0.0 {
39            self.refrac_count -= self.dt;
40            return 0;
41        }
42        self.v += (-(self.v - self.v_rest) + current + self.i_offset) / self.tau_m * self.dt;
43        if self.v >= self.v_threshold {
44            self.v = self.v_reset;
45            self.refrac_count = self.tau_refrac;
46            1
47        } else {
48            0
49        }
50    }
51    pub fn reset(&mut self) {
52        self.v = self.v_rest;
53        self.refrac_count = 0.0;
54    }
55}
56impl Default for SpiNNakerLIFNeuron {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn spinnaker_fires() {
68        let mut n = SpiNNakerLIFNeuron::new();
69        let t: i32 = (0..200).map(|_| n.step(30.0)).sum();
70        assert!(t > 0);
71    }
72    #[test]
73    fn spinnaker_silent() {
74        let mut n = SpiNNakerLIFNeuron::new();
75        let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
76        assert_eq!(t, 0);
77    }
78    #[test]
79    fn spinnaker_reset() {
80        let mut n = SpiNNakerLIFNeuron::new();
81        for _ in 0..50 {
82            n.step(30.0);
83        }
84        n.reset();
85        assert!((n.v - n.v_rest).abs() < 1e-10);
86    }
87    #[test]
88    fn spinnaker_bounded() {
89        let mut n = SpiNNakerLIFNeuron::new();
90        for _ in 0..1000 {
91            n.step(1e4);
92        }
93        assert!(n.v.is_finite());
94    }
95    #[test]
96    fn spinnaker_nan_no_panic() {
97        SpiNNakerLIFNeuron::new().step(f64::NAN);
98    }
99}