Skip to main content

sc_neurocore_engine/neurons/simple_spiking/
lnm.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 — Learnable Neuron Model
8
9//! Learnable parameterized neuron dynamics.
10
11/// Learnable Neuron Model (LNM) — parameterised activation + decay.
12#[derive(Clone, Debug)]
13pub struct LearnableNeuronModel {
14    pub v: f64,
15    pub alpha: f64,
16    pub beta: f64,
17    pub gamma: f64,
18    pub v_threshold: f64,
19    pub f_slope: f64,
20    pub f_shift: f64,
21}
22
23impl LearnableNeuronModel {
24    pub fn new() -> Self {
25        Self {
26            v: 0.0,
27            alpha: 0.9,
28            beta: 0.1,
29            gamma: 0.05,
30            v_threshold: 1.0,
31            f_slope: 5.0,
32            f_shift: 0.5,
33        }
34    }
35    pub fn step(&mut self, current: f64) -> i32 {
36        let f_v = 1.0 / (1.0 + (-(self.f_slope * (self.v - self.f_shift))).exp());
37        self.v = self.alpha * self.v + self.beta * current + self.gamma * f_v;
38        if self.v >= self.v_threshold {
39            self.v = 0.0;
40            1
41        } else {
42            0
43        }
44    }
45    pub fn reset(&mut self) {
46        self.v = 0.0;
47    }
48}
49impl Default for LearnableNeuronModel {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn default_matches_constructor_state() {
61        let default = LearnableNeuronModel::default();
62        let constructed = LearnableNeuronModel::new();
63        assert_eq!(default.v, constructed.v);
64    }
65
66    #[test]
67    fn lnm_fires() {
68        let mut n = LearnableNeuronModel::new();
69        let t: i32 = (0..50).map(|_| n.step(2.0)).sum();
70        assert!(t > 0);
71    }
72
73    #[test]
74    fn lnm_reset_clears_state() {
75        let mut n = LearnableNeuronModel::new();
76        for _ in 0..50 {
77            n.step(2.0);
78        }
79        n.reset();
80        assert!((n.v - 0.0).abs() < 1e-10);
81    }
82
83    #[test]
84    fn lnm_bounded() {
85        let mut n = LearnableNeuronModel::new();
86        for _ in 0..1000 {
87            n.step(100.0);
88        }
89        assert!(n.v.is_finite());
90    }
91
92    #[test]
93    fn lnm_nan_no_panic() {
94        LearnableNeuronModel::new().step(f64::NAN);
95    }
96}