Skip to main content

sc_neurocore_engine/neurons/trivial/
parametric_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 — Parametric LIF Neuron
8
9/// Parametric LIF — learnable decay via sigmoid(a). Fang et al. 2021.
10#[derive(Clone, Debug)]
11pub struct ParametricLIFNeuron {
12    pub v: f64,
13    pub a: f64,
14    pub threshold: f64,
15}
16
17impl ParametricLIFNeuron {
18    pub fn new(a: f64, threshold: f64) -> Self {
19        Self {
20            v: 0.0,
21            a,
22            threshold,
23        }
24    }
25
26    pub fn step(&mut self, current: f64) -> i32 {
27        let alpha = 1.0 / (1.0 + (-self.a).exp());
28        let spike = if self.v >= self.threshold { 1 } else { 0 };
29        self.v = alpha * self.v * (1.0 - spike as f64) + current;
30        spike
31    }
32
33    pub fn reset(&mut self) {
34        self.v = 0.0;
35    }
36}
37
38impl Default for ParametricLIFNeuron {
39    fn default() -> Self {
40        Self::new(0.0, 1.0)
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn plif_fires() {
50        let mut n = ParametricLIFNeuron::default();
51        let total: i32 = (0..20).map(|_| n.step(1.5)).sum();
52        assert!(total > 0);
53    }
54    #[test]
55    fn plif_silent_without_input() {
56        let mut n = ParametricLIFNeuron::default();
57        let t: i32 = (0..100).map(|_| n.step(0.0)).sum();
58        assert_eq!(t, 0);
59    }
60    #[test]
61    fn plif_reset_clears_state() {
62        let mut n = ParametricLIFNeuron::default();
63        for _ in 0..20 {
64            n.step(1.5);
65        }
66        n.reset();
67        assert!((n.v - 0.0).abs() < 1e-10);
68    }
69    #[test]
70    fn plif_bounded() {
71        let mut n = ParametricLIFNeuron::default();
72        for _ in 0..1000 {
73            n.step(100.0);
74        }
75        assert!(n.v.is_finite());
76    }
77    #[test]
78    fn plif_nan_no_panic() {
79        ParametricLIFNeuron::default().step(f64::NAN);
80    }
81}