sc_neurocore_engine/neurons/biophysical/
mainen_sejnowski.rs1#[derive(Clone, Debug)]
13pub struct MainenSejnowskiNeuron {
14 pub vs: f64,
15 pub va: f64,
16 pub m: f64,
17 pub h: f64,
18 pub n: f64,
19 pub kappa: f64,
20 pub g_na: f64,
21 pub g_k: f64,
22 pub g_l: f64,
23 pub e_na: f64,
24 pub e_k: f64,
25 pub e_l: f64,
26 pub c_s: f64,
27 pub c_a: f64,
28 pub dt: f64,
29 pub v_threshold: f64,
30}
31
32impl MainenSejnowskiNeuron {
33 pub fn new() -> Self {
34 Self {
35 vs: -65.0,
36 va: -65.0,
37 m: 0.05,
38 h: 0.6,
39 n: 0.3,
40 kappa: 10.0,
41 g_na: 3000.0,
42 g_k: 1500.0,
43 g_l: 1.0,
44 e_na: 50.0,
45 e_k: -90.0,
46 e_l: -70.0,
47 c_s: 1.0,
48 c_a: 0.1,
49 dt: 0.005,
50 v_threshold: -20.0,
51 }
52 }
53 pub fn step(&mut self, current: f64) -> i32 {
54 let v_prev = self.vs;
55 for _ in 0..20 {
56 let x_am = self.va + 25.0;
58 let am = if x_am.abs() < 1e-6 {
59 0.182 * 9.0
60 } else {
61 0.182 * x_am / (1.0 - (-(x_am) / 9.0).exp() + 1e-12)
62 };
63 let bm = if x_am.abs() < 1e-6 {
64 0.124 * 9.0
65 } else {
66 -0.124 * x_am / (1.0 - ((x_am) / 9.0).exp() + 1e-12)
67 };
68 let x_ah = self.va + 40.0;
69 let ah = if x_ah.abs() < 1e-6 {
70 0.024 * 5.0
71 } else {
72 0.024 * x_ah / (1.0 - (-(x_ah) / 5.0).exp() + 1e-12)
73 };
74 let x_bh = self.va + 65.0;
75 let bh = if x_bh.abs() < 1e-6 {
76 0.0091 * 5.0
77 } else {
78 -0.0091 * x_bh / (1.0 - ((x_bh) / 5.0).exp() + 1e-12)
79 };
80 let x_an = self.va - 20.0;
81 let an = if x_an.abs() < 1e-6 {
82 0.02 * 9.0
83 } else {
84 0.02 * x_an / (1.0 - (-(x_an) / 9.0).exp() + 1e-12)
85 };
86 let bn = if x_an.abs() < 1e-6 {
87 0.002 * 9.0
88 } else {
89 -0.002 * x_an / (1.0 - ((x_an) / 9.0).exp() + 1e-12)
90 };
91 self.m = (self.m + (am * (1.0 - self.m) - bm * self.m) * self.dt).clamp(0.0, 1.0);
92 self.h = (self.h + (ah * (1.0 - self.h) - bh * self.h) * self.dt).clamp(0.0, 1.0);
93 self.n = (self.n + (an * (1.0 - self.n) - bn * self.n) * self.dt).clamp(0.0, 1.0);
94 let i_na = self.g_na * self.m.powi(3) * self.h * (self.va - self.e_na);
95 let i_k = self.g_k * self.n * (self.va - self.e_k);
96 let i_l_s = self.g_l * (self.vs - self.e_l);
97 self.vs = (self.vs
98 + (-i_l_s + self.kappa * (self.va - self.vs) + current) / self.c_s * self.dt)
99 .clamp(-200.0, 200.0);
100 self.va = (self.va
101 + (-i_na - i_k + self.kappa * (self.vs - self.va)) / self.c_a * self.dt)
102 .clamp(-200.0, 200.0);
103 }
104 if self.vs >= self.v_threshold && v_prev < self.v_threshold {
105 1
106 } else {
107 0
108 }
109 }
110 pub fn reset(&mut self) {
111 self.vs = -65.0;
112 self.va = -65.0;
113 self.m = 0.05;
114 self.h = 0.6;
115 self.n = 0.3;
116 }
117}
118impl Default for MainenSejnowskiNeuron {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn default_matches_constructor_state() {
130 let default = MainenSejnowskiNeuron::default();
131 let constructed = MainenSejnowskiNeuron::new();
132 assert_eq!(default.vs, constructed.vs);
133 }
134
135 #[test]
136 fn removable_rate_singularities_use_finite_limits() {
137 for voltage in [-25.0, -40.0, -65.0, 20.0] {
138 let mut n = MainenSejnowskiNeuron::new();
139 n.va = voltage;
140 let spike = n.step(0.0);
141 assert!(matches!(spike, 0 | 1));
142 }
143 }
144
145 #[test]
146 fn mainen_fires() {
147 let mut n = MainenSejnowskiNeuron::new();
148 let t: i32 = (0..5000).map(|_| n.step(500.0)).sum();
149 assert!(t > 0);
150 }
151
152 #[test]
154 fn mainen_stable_without_input() {
155 let mut n = MainenSejnowskiNeuron::new();
158 for _ in 0..500 {
159 n.step(0.0);
160 }
161 assert!(n.vs.is_finite());
162 assert!(n.va.is_finite());
163 }
164 #[test]
165 fn mainen_reset_clears_state() {
166 let mut n = MainenSejnowskiNeuron::new();
167 for _ in 0..100 {
168 n.step(500.0);
169 }
170 n.reset();
171 assert!((n.vs - (-65.0)).abs() < 1e-10);
172 assert!((n.va - (-65.0)).abs() < 1e-10);
173 }
174 #[test]
175 fn mainen_moderate_input_stable() {
176 let mut n = MainenSejnowskiNeuron::new();
178 for _ in 0..200 {
179 n.step(500.0);
180 }
181 let _ = n.vs; }
185 #[test]
186 fn mainen_two_compartments_coupled() {
187 let n = MainenSejnowskiNeuron::new();
188 assert!(n.kappa > 0.0, "coupling should be positive");
190 }
191 #[test]
192 fn mainen_weak_negative_no_crash() {
193 let mut n = MainenSejnowskiNeuron::new();
194 for _ in 0..200 {
195 n.step(-10.0);
196 }
197 assert!(n.vs.is_finite());
199 }
200 #[test]
201 fn mainen_nan_no_panic() {
202 let mut n = MainenSejnowskiNeuron::new();
203 n.step(f64::NAN);
204 }
205}