Skip to main content

sc_neurocore_engine/neurons/trivial/
gated_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 — Gated LIF Neuron
8
9/// Gated LIF — learnable decay and input gates.
10#[derive(Clone, Debug)]
11pub struct GatedLIFNeuron {
12    pub v: f64,
13    pub gate_v: f64,
14    pub gate_i: f64,
15    pub v_threshold: f64,
16    pub dt: f64,
17}
18
19impl GatedLIFNeuron {
20    pub fn new(gate_v: f64, gate_i: f64, v_threshold: f64) -> Self {
21        Self {
22            v: 0.0,
23            gate_v,
24            gate_i,
25            v_threshold,
26            dt: 1.0,
27        }
28    }
29
30    pub fn step(&mut self, current: f64) -> i32 {
31        self.v = self.gate_v * self.v + self.gate_i * current;
32        if self.v >= self.v_threshold {
33            self.v -= self.v_threshold;
34            1
35        } else {
36            0
37        }
38    }
39
40    pub fn reset(&mut self) {
41        self.v = 0.0;
42    }
43}
44
45impl Default for GatedLIFNeuron {
46    fn default() -> Self {
47        Self::new(0.9, 1.0, 1.0)
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn gated_lif_fires() {
57        let mut n = GatedLIFNeuron::default();
58        let total: i32 = (0..20).map(|_| n.step(0.5)).sum();
59        assert!(total > 0);
60    }
61    #[test]
62    fn gated_lif_silent_without_input() {
63        let mut n = GatedLIFNeuron::default();
64        let t: i32 = (0..100).map(|_| n.step(0.0)).sum();
65        assert_eq!(t, 0);
66    }
67    #[test]
68    fn gated_lif_reset_clears_state() {
69        let mut n = GatedLIFNeuron::default();
70        for _ in 0..20 {
71            n.step(0.5);
72        }
73        n.reset();
74        assert!((n.v - 0.0).abs() < 1e-10);
75    }
76    #[test]
77    fn gated_lif_bounded() {
78        let mut n = GatedLIFNeuron::default();
79        for _ in 0..1000 {
80            n.step(100.0);
81        }
82        assert!(n.v.is_finite());
83    }
84    #[test]
85    fn gated_lif_nan_no_panic() {
86        GatedLIFNeuron::default().step(f64::NAN);
87    }
88}