sc_neurocore_engine/neurons/hardware/
neurogrid.rs1#[derive(Clone, Debug)]
11pub struct NeuroGridNeuron {
12 pub v_s: f64,
13 pub v_d: f64,
14 pub tau_s: f64,
15 pub tau_d: f64,
16 pub g_c: f64,
17 pub delta_t: f64,
18 pub v_rest: f64,
19 pub v_threshold: f64,
20 pub v_peak: f64,
21 pub v_reset: f64,
22 pub dt: f64,
23}
24
25impl NeuroGridNeuron {
26 pub fn new() -> Self {
27 Self {
28 v_s: -65.0,
29 v_d: -65.0,
30 tau_s: 20.0,
31 tau_d: 50.0,
32 g_c: 0.5,
33 delta_t: 2.0,
34 v_rest: -65.0,
35 v_threshold: -50.0,
36 v_peak: 20.0,
37 v_reset: -65.0,
38 dt: 0.1,
39 }
40 }
41 fn valid(&self) -> bool {
42 self.v_s.is_finite()
43 && self.v_d.is_finite()
44 && self.tau_s.is_finite()
45 && self.tau_s > 0.0
46 && self.tau_d.is_finite()
47 && self.tau_d > 0.0
48 && self.g_c.is_finite()
49 && self.g_c >= 0.0
50 && self.delta_t.is_finite()
51 && self.delta_t > 0.0
52 && self.v_rest.is_finite()
53 && self.v_threshold.is_finite()
54 && self.v_peak.is_finite()
55 && self.v_reset.is_finite()
56 && self.dt.is_finite()
57 && self.dt > 0.0
58 }
59
60 fn derivatives(&self, v_s: f64, v_d: f64, current: f64) -> (f64, f64) {
61 let v_s_eff = v_s.min(self.v_peak);
62 let dv_d = (-(v_d - self.v_rest) + current - self.g_c * (v_d - v_s_eff)) / self.tau_d;
63 let exp_arg = ((v_s_eff - self.v_threshold) / self.delta_t).min(20.0);
64 let exp_term = self.delta_t * exp_arg.exp();
65 let dv_s = (-(v_s_eff - self.v_rest) + exp_term + self.g_c * (v_d - v_s_eff)) / self.tau_s;
66 (dv_s, dv_d)
67 }
68
69 fn rk4_substep(&self, v_s: f64, v_d: f64, current: f64) -> (f64, f64) {
70 let dt = self.dt;
71 let (k1s, k1d) = self.derivatives(v_s, v_d, current);
72 let (k2s, k2d) = self.derivatives(v_s + 0.5 * dt * k1s, v_d + 0.5 * dt * k1d, current);
73 let (k3s, k3d) = self.derivatives(v_s + 0.5 * dt * k2s, v_d + 0.5 * dt * k2d, current);
74 let (k4s, k4d) = self.derivatives(v_s + dt * k3s, v_d + dt * k3d, current);
75 (
76 v_s + dt * (k1s + 2.0 * k2s + 2.0 * k3s + k4s) / 6.0,
77 v_d + dt * (k1d + 2.0 * k2d + 2.0 * k3d + k4d) / 6.0,
78 )
79 }
80
81 pub fn step(&mut self, current: f64) -> i32 {
82 if !current.is_finite() || !self.valid() {
83 return 0;
84 }
85 let (next_v_s, next_v_d) = self.rk4_substep(self.v_s, self.v_d, current);
86 if !next_v_s.is_finite() || !next_v_d.is_finite() {
87 return 0;
88 }
89 self.v_d = next_v_d;
90 if next_v_s >= self.v_peak {
91 self.v_s = self.v_reset;
92 1
93 } else {
94 self.v_s = next_v_s;
95 0
96 }
97 }
98 pub fn reset(&mut self) {
99 self.v_s = -65.0;
100 self.v_d = -65.0;
101 }
102}
103impl Default for NeuroGridNeuron {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn neurogrid_fires() {
115 let mut n = NeuroGridNeuron::new();
116 let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
117 assert!(t > 0);
118 }
119 #[test]
120 fn neurogrid_silent() {
121 let mut n = NeuroGridNeuron::new();
122 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
123 assert_eq!(t, 0);
124 }
125 #[test]
126 fn neurogrid_reset() {
127 let mut n = NeuroGridNeuron::new();
128 for _ in 0..100 {
129 n.step(500.0);
130 }
131 n.reset();
132 assert!((n.v_s - (-65.0)).abs() < 1e-10);
133 }
134 #[test]
135 fn neurogrid_bounded() {
136 let mut n = NeuroGridNeuron::new();
137 for _ in 0..2000 {
138 n.step(1e4);
139 }
140 assert!(n.v_s.is_finite());
141 }
142 #[test]
143 fn neurogrid_nan_no_panic() {
144 NeuroGridNeuron::new().step(f64::NAN);
145 }
146 #[test]
147 fn neurogrid_rk4_anchor() {
148 let mut n = NeuroGridNeuron::new();
149 let spikes: i32 = (0..20_000).map(|_| n.step(100.0)).sum();
150 assert_eq!(spikes, 94);
151 assert!(n.v_s.is_finite());
152 assert!(n.v_d.is_finite());
153 }
154 #[test]
155 fn neurogrid_invalid_input_preserves_state() {
156 let mut n = NeuroGridNeuron::new();
157 for _ in 0..10 {
158 n.step(100.0);
159 }
160 let old = (n.v_s, n.v_d);
161 assert_eq!(n.step(f64::INFINITY), 0);
162 assert_eq!((n.v_s, n.v_d), old);
163 }
164}