sc_neurocore_engine/neurons/biophysical/
bertram_phantom.rs1#[path = "sc_three_state_phantom.rs"]
12mod sc_three_state_phantom;
13pub use sc_three_state_phantom::SCThreeStatePhantomBurster;
14
15#[derive(Clone, Debug)]
16pub struct BertramPhantomBurster {
17 pub v: f64,
18 pub n: f64,
19 pub s1: f64,
20 pub s2: f64,
21 pub lambda_n: f64,
22 pub g_ca: f64,
23 pub g_k: f64,
24 pub g_s1: f64,
25 pub g_s2: f64,
26 pub g_l: f64,
27 pub e_ca: f64,
28 pub e_k: f64,
29 pub e_l: f64,
30 pub c_m: f64,
31 pub v_m: f64,
32 pub s_m: f64,
33 pub v_n: f64,
34 pub s_n: f64,
35 pub v_s1: f64,
36 pub s_s1: f64,
37 pub v_s2: f64,
38 pub s_s2: f64,
39 pub tau_n_bar: f64,
40 pub tau_s1: f64,
41 pub tau_s2: f64,
42 pub dt: f64,
43 pub v_threshold: f64,
44}
45
46impl BertramPhantomBurster {
47 pub fn new() -> Self {
48 Self {
49 v: -43.0,
50 n: 0.03,
51 s1: 0.1,
52 s2: 0.434,
53 lambda_n: 1.1,
54 g_ca: 280.0,
55 g_k: 1300.0,
56 g_s1: 20.0,
57 g_s2: 32.0,
58 g_l: 25.0,
59 e_ca: 100.0,
60 e_k: -80.0,
61 e_l: -40.0,
62 c_m: 4524.0,
63 v_m: -22.0,
64 s_m: 7.5,
65 v_n: -9.0,
66 s_n: 10.0,
67 v_s1: -40.0,
68 s_s1: 0.5,
69 v_s2: -42.0,
70 s_s2: 0.4,
71 tau_n_bar: 9.09,
72 tau_s1: 1000.0,
73 tau_s2: 120_000.0,
74 dt: 0.5,
75 v_threshold: -20.0,
76 }
77 }
78
79 fn boltz(v: f64, midpoint: f64, slope: f64) -> f64 {
80 1.0 / (1.0 + ((midpoint - v) / slope).exp())
81 }
82
83 fn derivatives(&self, state: [f64; 4], current: f64) -> [f64; 4] {
84 let [v, n, s1, s2] = state;
85 let m_inf = Self::boltz(v, self.v_m, self.s_m);
86 let n_inf = Self::boltz(v, self.v_n, self.s_n);
87 let s1_inf = Self::boltz(v, self.v_s1, self.s_s1);
88 let s2_inf = Self::boltz(v, self.v_s2, self.s_s2);
89 let tau_n = self.tau_n_bar / (1.0 + ((v - self.v_n) / self.s_n).exp());
90 let i_ca = self.g_ca * m_inf * (v - self.e_ca);
91 let i_k = self.g_k * n * (v - self.e_k);
92 let i_s1 = self.g_s1 * s1 * (v - self.e_k);
93 let i_s2 = self.g_s2 * s2 * (v - self.e_k);
94 let i_l = self.g_l * (v - self.e_l);
95 [
96 (-i_ca - i_k - i_s1 - i_s2 - i_l + current) / self.c_m,
97 self.lambda_n * (n_inf - n) / tau_n,
98 (s1_inf - s1) / self.tau_s1,
99 (s2_inf - s2) / self.tau_s2,
100 ]
101 }
102
103 fn shifted(state: [f64; 4], derivative: [f64; 4], scale: f64) -> [f64; 4] {
104 std::array::from_fn(|index| state[index] + scale * derivative[index])
105 }
106
107 pub fn step(&mut self, current: f64) -> i32 {
108 let previous_v = self.v;
109 let state = [self.v, self.n, self.s1, self.s2];
110 let k1 = self.derivatives(state, current);
111 let k2 = self.derivatives(Self::shifted(state, k1, 0.5 * self.dt), current);
112 let k3 = self.derivatives(Self::shifted(state, k2, 0.5 * self.dt), current);
113 let k4 = self.derivatives(Self::shifted(state, k3, self.dt), current);
114 let next: [f64; 4] = std::array::from_fn(|index| {
115 state[index]
116 + self.dt * (k1[index] + 2.0 * k2[index] + 2.0 * k3[index] + k4[index]) / 6.0
117 });
118 self.v = next[0];
119 self.n = next[1];
120 self.s1 = next[2];
121 self.s2 = next[3];
122 i32::from(self.v >= self.v_threshold && previous_v < self.v_threshold)
123 }
124
125 pub fn reset(&mut self) {
126 self.v = -43.0;
127 self.n = 0.03;
128 self.s1 = 0.1;
129 self.s2 = 0.434;
130 }
131}
132
133impl Default for BertramPhantomBurster {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn source_defaults_and_dynamic_n() {
145 let mut model = BertramPhantomBurster::new();
146 let previous_n = model.n;
147 model.step(0.0);
148 assert_ne!(model.n, previous_n);
149 }
150
151 #[test]
152 fn reset_restores_source_state() {
153 let mut model = BertramPhantomBurster::new();
154 model.step(200.0);
155 model.reset();
156 assert_eq!(
157 [model.v, model.n, model.s1, model.s2],
158 [-43.0, 0.03, 0.1, 0.434]
159 );
160 }
161}