sc_neurocore_engine/neurons/biophysical/
bertram_phantom.rs1#[derive(Clone, Debug)]
13pub struct BertramPhantomBurster {
14 pub v: f64,
15 pub s1: f64,
16 pub s2: f64,
17 pub g_ca: f64,
18 pub g_k: f64,
19 pub g_s1: f64,
20 pub g_s2: f64,
21 pub g_l: f64,
22 pub e_ca: f64,
23 pub e_k: f64,
24 pub e_l: f64,
25 pub c_m: f64,
26 pub v_m: f64,
27 pub s_m: f64,
28 pub v_n: f64,
29 pub s_n: f64,
30 pub v_s1: f64,
31 pub s_s1: f64,
32 pub v_s2: f64,
33 pub s_s2: f64,
34 pub tau_s1: f64,
35 pub tau_s2: f64,
36 pub dt: f64,
37 pub v_threshold: f64,
38}
39
40impl BertramPhantomBurster {
41 pub fn new() -> Self {
42 Self {
43 v: -50.0,
44 s1: 0.1,
45 s2: 0.1,
46 g_ca: 3.6,
47 g_k: 10.0,
48 g_s1: 4.0,
49 g_s2: 4.0,
50 g_l: 0.2,
51 e_ca: 25.0,
52 e_k: -75.0,
53 e_l: -40.0,
54 c_m: 5.3,
55 v_m: -20.0,
56 s_m: 12.0,
57 v_n: -16.0,
58 s_n: 5.6,
59 v_s1: -40.0,
60 s_s1: 10.0,
61 v_s2: -42.0,
62 s_s2: 0.4,
63 tau_s1: 20000.0,
64 tau_s2: 100000.0,
65 dt: 0.5,
66 v_threshold: -20.0,
67 }
68 }
69 pub fn step(&mut self, current: f64) -> i32 {
70 let v_prev = self.v;
71 let m_inf = 1.0 / (1.0 + (-(self.v - self.v_m) / self.s_m).exp());
72 let n_inf = 1.0 / (1.0 + (-(self.v - self.v_n) / self.s_n).exp());
73 let s1_inf = 1.0 / (1.0 + (-(self.v - self.v_s1) / self.s_s1).exp());
74 let s2_inf = 1.0 / (1.0 + (-(self.v - self.v_s2) / self.s_s2).exp());
75 let i_ca = self.g_ca * m_inf * (self.v - self.e_ca);
76 let i_k = self.g_k * n_inf * (self.v - self.e_k);
77 let i_s1 = self.g_s1 * self.s1 * (self.v - self.e_k);
78 let i_s2 = self.g_s2 * self.s2 * (self.v - self.e_k);
79 let i_l = self.g_l * (self.v - self.e_l);
80 self.v += (-i_ca - i_k - i_s1 - i_s2 - i_l + current) / self.c_m * self.dt;
81 self.s1 += (s1_inf - self.s1) / self.tau_s1 * self.dt;
82 self.s2 += (s2_inf - self.s2) / self.tau_s2 * self.dt;
83 if self.v >= self.v_threshold && v_prev < self.v_threshold {
84 1
85 } else {
86 0
87 }
88 }
89 pub fn reset(&mut self) {
90 self.v = -50.0;
91 self.s1 = 0.1;
92 self.s2 = 0.1;
93 }
94}
95impl Default for BertramPhantomBurster {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn default_matches_constructor_state() {
107 let default = BertramPhantomBurster::default();
108 let constructed = BertramPhantomBurster::new();
109 assert_eq!(default.v, constructed.v);
110 }
111
112 #[test]
113 fn bertram_fires() {
114 let mut n = BertramPhantomBurster::new();
115 let t: i32 = (0..10000).map(|_| n.step(200.0)).sum();
116 assert!(t > 0);
117 }
118
119 #[test]
121 fn bertram_silent_without_input() {
122 let mut n = BertramPhantomBurster::new();
123 for _ in 0..500 {
124 n.step(0.0);
125 }
126 assert!(n.v.is_finite());
127 }
128 #[test]
129 fn bertram_reset_clears_state() {
130 let mut n = BertramPhantomBurster::new();
131 for _ in 0..1000 {
132 n.step(200.0);
133 }
134 n.reset();
135 assert!((n.v - (-50.0)).abs() < 1e-10);
136 assert!((n.s1 - 0.1).abs() < 1e-10);
137 assert!((n.s2 - 0.1).abs() < 1e-10);
138 }
139 #[test]
140 fn bertram_extreme_bounded() {
141 let mut n = BertramPhantomBurster::new();
142 for _ in 0..200 {
143 n.step(1e4);
144 }
145 assert!(n.v.is_finite());
146 }
147 #[test]
148 fn bertram_dual_slow_vars() {
149 let mut n = BertramPhantomBurster::new();
150 for _ in 0..10000 {
151 n.step(200.0);
152 }
153 assert!(n.s1 >= 0.0 && n.s1 <= 1.0, "s1={}", n.s1);
155 assert!(n.s2 >= 0.0 && n.s2 <= 1.0, "s2={}", n.s2);
156 }
157 #[test]
158 fn bertram_negative_no_crash() {
159 let mut n = BertramPhantomBurster::new();
160 for _ in 0..200 {
161 n.step(-100.0);
162 }
163 assert!(n.v.is_finite());
164 }
165 #[test]
166 fn bertram_nan_no_panic() {
167 let mut n = BertramPhantomBurster::new();
168 n.step(f64::NAN);
169 }
170}