sc_neurocore_engine/neurons/biophysical/
golomb_fs.rs1#[derive(Clone, Debug)]
13pub struct GolombFSNeuron {
14 pub v: f64,
15 pub h: f64,
16 pub n: f64,
17 pub p: f64,
18 pub g_na: f64,
19 pub g_k: f64,
20 pub g_kv3: f64,
21 pub g_l: f64,
22 pub e_na: f64,
23 pub e_k: f64,
24 pub e_l: f64,
25 pub dt: f64,
26 pub v_threshold: f64,
27}
28
29impl GolombFSNeuron {
30 pub fn new() -> Self {
31 Self {
32 v: -65.0,
33 h: 0.9,
34 n: 0.1,
35 p: 0.0,
36 g_na: 112.5,
37 g_k: 225.0,
38 g_kv3: 150.0,
39 g_l: 0.25,
40 e_na: 50.0,
41 e_k: -90.0,
42 e_l: -70.0,
43 dt: 0.01,
44 v_threshold: -20.0,
45 }
46 }
47 fn derivatives(&self, v: f64, h: f64, n: f64, p: f64, current: f64) -> [f64; 4] {
52 let m_inf = 1.0 / (1.0 + (-(v + 24.0) / 11.5).exp());
53 let h_inf = 1.0 / (1.0 + ((v + 58.3) / 6.7).exp());
54 let n_inf = 1.0 / (1.0 + (-(v + 12.4) / 6.8).exp());
55 let p_inf = 1.0 / (1.0 + (-(v + 3.0) / 8.0).exp());
56 let tau_h = 0.5 + 14.0 / (1.0 + ((v + 60.0) / 12.0).exp());
57 let tau_n = 0.087 + 11.4 / (1.0 + ((v + 14.6) / 8.6).exp());
58 let tau_p = 0.1 + 4.0 / (1.0 + ((v + 25.0) / 10.0).exp());
59 let dh = (h_inf - h) / tau_h;
60 let dn = (n_inf - n) / tau_n;
61 let dp = (p_inf - p) / tau_p;
62 let i_na = self.g_na * m_inf * m_inf * m_inf * h * (v - self.e_na);
63 let i_k = self.g_k * n * n * n * n * (v - self.e_k);
64 let i_kv3 = self.g_kv3 * p * p * (v - self.e_k);
65 let i_l = self.g_l * (v - self.e_l);
66 let dv = -i_na - i_k - i_kv3 - i_l + current;
67 [dv, dh, dn, dp]
68 }
69
70 fn rk4_substep(&self, s: [f64; 4], current: f64) -> [f64; 4] {
73 let dt = self.dt;
74 let k1 = self.derivatives(s[0], s[1], s[2], s[3], current);
75 let k2 = self.derivatives(
76 s[0] + 0.5 * dt * k1[0],
77 s[1] + 0.5 * dt * k1[1],
78 s[2] + 0.5 * dt * k1[2],
79 s[3] + 0.5 * dt * k1[3],
80 current,
81 );
82 let k3 = self.derivatives(
83 s[0] + 0.5 * dt * k2[0],
84 s[1] + 0.5 * dt * k2[1],
85 s[2] + 0.5 * dt * k2[2],
86 s[3] + 0.5 * dt * k2[3],
87 current,
88 );
89 let k4 = self.derivatives(
90 s[0] + dt * k3[0],
91 s[1] + dt * k3[1],
92 s[2] + dt * k3[2],
93 s[3] + dt * k3[3],
94 current,
95 );
96 let mut out = [0.0_f64; 4];
97 for i in 0..4 {
98 out[i] = s[i] + dt * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) / 6.0;
99 }
100 out
101 }
102
103 pub fn step(&mut self, current: f64) -> i32 {
104 let v_prev = self.v;
105 let mut s = [self.v, self.h, self.n, self.p];
106 for _ in 0..10 {
107 s = self.rk4_substep(s, current);
108 }
109 self.v = s[0];
110 self.h = s[1];
111 self.n = s[2];
112 self.p = s[3];
113 if self.v >= self.v_threshold && v_prev < self.v_threshold {
114 1
115 } else {
116 0
117 }
118 }
119 pub fn reset(&mut self) {
120 self.v = -65.0;
121 self.h = 0.9;
122 self.n = 0.1;
123 self.p = 0.0;
124 }
125}
126impl Default for GolombFSNeuron {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn default_matches_constructor_state() {
138 let default = GolombFSNeuron::default();
139 let constructed = GolombFSNeuron::new();
140 assert_eq!(default.v, constructed.v);
141 }
142
143 #[test]
144 fn golomb_fires() {
145 let mut n = GolombFSNeuron::new();
146 let t: i32 = (0..2000).map(|_| n.step(200.0)).sum();
147 assert!(t > 0);
148 }
149
150 #[test]
152 fn golomb_silent_without_input() {
153 let mut n = GolombFSNeuron::new();
154 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
155 assert_eq!(t, 0);
156 }
157 #[test]
158 fn golomb_reset_clears_state() {
159 let mut n = GolombFSNeuron::new();
160 for _ in 0..100 {
161 n.step(200.0);
162 }
163 n.reset();
164 assert!((n.v - (-65.0)).abs() < 1e-10);
165 }
166 #[test]
167 fn golomb_extreme_bounded() {
168 let mut n = GolombFSNeuron::new();
170 for _ in 0..200 {
171 n.step(200.0);
172 }
173 assert!(n.v.is_finite());
174 }
175 #[test]
176 fn golomb_kv3_enables_fast_spiking() {
177 let mut n = GolombFSNeuron::new();
179 let t: i32 = (0..5000).map(|_| n.step(300.0)).sum();
180 assert!(t > 0, "Golomb FS should fire with high input, got {}", t);
181 }
182 #[test]
183 fn golomb_negative_no_crash() {
184 let mut n = GolombFSNeuron::new();
185 for _ in 0..200 {
186 n.step(-100.0);
187 }
188 assert!(n.v.is_finite());
189 }
190 #[test]
191 fn golomb_nan_no_panic() {
192 let mut n = GolombFSNeuron::new();
193 n.step(f64::NAN);
194 }
195}