sc_neurocore_engine/neuron/
exp_if.rs1#[derive(Clone, Debug)]
11pub struct ExpIfNeuron {
12 pub v: f64,
13 pub v_rest: f64,
14 pub v_reset: f64,
15 pub v_threshold: f64,
16 pub v_rh: f64,
17 pub delta_t: f64,
18 pub tau: f64,
19 pub dt: f64,
20 pub refractory_period: f64,
21 pub refractory_remaining: f64,
22 pub source_profile: bool,
24 pub inv_delta_t: f64,
25 pub dt_div_tau: f64,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub enum ExpIfError {
31 InvalidInput,
32 InvalidState,
33 NonFiniteUpdate,
34}
35
36pub type ExpIfCompleteTrace = (Vec<f64>, Vec<f64>, Vec<u8>);
38
39impl Default for ExpIfNeuron {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl ExpIfNeuron {
46 pub fn new() -> Self {
47 Self {
48 v: -65.0,
49 v_rest: -65.0,
50 v_reset: -68.0,
51 v_threshold: 30.0,
52 v_rh: -59.9,
53 delta_t: 3.48,
54 tau: 10.0,
55 dt: 0.02,
56 refractory_period: 0.0,
57 refractory_remaining: 0.0,
58 source_profile: false,
59 inv_delta_t: 1.0 / 3.48,
60 dt_div_tau: 0.02 / 10.0,
61 }
62 }
63
64 pub fn fourcaud_trocme_2003() -> Self {
66 Self {
67 v_threshold: -30.0,
68 dt: 0.01,
69 refractory_period: 1.7,
70 source_profile: true,
71 dt_div_tau: 0.01 / 10.0,
72 ..Self::new()
73 }
74 }
75
76 pub fn step(&mut self, current: f64) -> i32 {
78 self.try_step(current).unwrap_or(0)
79 }
80
81 pub fn try_step(&mut self, current: f64) -> Result<i32, ExpIfError> {
83 if !self.v.is_finite()
84 || !current.is_finite()
85 || !self.v_rest.is_finite()
86 || !self.v_reset.is_finite()
87 || !self.v_threshold.is_finite()
88 || !self.v_rh.is_finite()
89 || !self.delta_t.is_finite()
90 || !self.tau.is_finite()
91 || !self.dt.is_finite()
92 || !self.refractory_period.is_finite()
93 || !self.refractory_remaining.is_finite()
94 || self.delta_t <= 0.0
95 || self.tau <= 0.0
96 || self.dt <= 0.0
97 || self.refractory_period < 0.0
98 || self.refractory_remaining < 0.0
99 || self.refractory_remaining > self.refractory_period
100 || self.v_threshold <= self.v_rh
101 || self.v >= self.v_threshold
102 || self.v_rest >= self.v_threshold
103 || self.v_reset >= self.v_threshold
104 || (self.source_profile
105 && (self.v_rest != -65.0
106 || self.v_reset != -68.0
107 || self.v_threshold != -30.0
108 || self.v_rh != -59.9
109 || self.delta_t != 3.48
110 || self.tau != 10.0
111 || self.dt >= 0.02
112 || self.refractory_period != 1.7))
113 {
114 return Err(if !current.is_finite() {
115 ExpIfError::InvalidInput
116 } else {
117 ExpIfError::InvalidState
118 });
119 }
120
121 if self.refractory_remaining > 0.0 {
122 self.refractory_remaining = (self.refractory_remaining - self.dt).max(0.0);
123 self.v = self.v_reset;
124 return Ok(0);
125 }
126
127 let inv_delta_t = 1.0 / self.delta_t;
128 let k1 = self.rhs(self.v, current, inv_delta_t);
129 let predictor = self.v + self.dt * k1;
130 let k2 = self.rhs(
131 if self.source_profile {
132 predictor
133 } else {
134 self.v + 0.5 * self.dt * k1
135 },
136 current,
137 inv_delta_t,
138 );
139 let (k3, k4, next_v) = if self.source_profile {
140 (0.0, 0.0, self.v + 0.5 * self.dt * (k1 + k2))
141 } else {
142 let k3 = self.rhs(self.v + 0.5 * self.dt * k2, current, inv_delta_t);
143 let k4 = self.rhs(self.v + self.dt * k3, current, inv_delta_t);
144 (
145 k3,
146 k4,
147 self.v + (self.dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4),
148 )
149 };
150 if !k1.is_finite()
151 || !k2.is_finite()
152 || !k3.is_finite()
153 || !k4.is_finite()
154 || !next_v.is_finite()
155 {
156 return Err(ExpIfError::NonFiniteUpdate);
157 }
158
159 self.inv_delta_t = inv_delta_t;
160 self.dt_div_tau = self.dt / self.tau;
161 if next_v >= self.v_threshold {
162 self.v = self.v_reset;
163 self.refractory_remaining = self.refractory_period;
164 Ok(1)
165 } else {
166 self.v = next_v;
167 Ok(0)
168 }
169 }
170
171 pub fn simulate_complete(
173 &mut self,
174 n_steps: usize,
175 current: f64,
176 ) -> Result<ExpIfCompleteTrace, ExpIfError> {
177 let mut candidate = self.clone();
178 let mut voltage = Vec::with_capacity(n_steps);
179 let mut refractory = Vec::with_capacity(n_steps);
180 let mut events = Vec::with_capacity(n_steps);
181 for _ in 0..n_steps {
182 let event = candidate.try_step(current)?;
183 voltage.push(candidate.v);
184 refractory.push(candidate.refractory_remaining);
185 events.push(event as u8);
186 }
187 *self = candidate;
188 Ok((voltage, refractory, events))
189 }
190
191 pub fn reset(&mut self) {
192 self.v = self.v_rest;
193 self.refractory_remaining = 0.0;
194 }
195
196 fn rhs(&self, v: f64, current: f64, inv_delta_t: f64) -> f64 {
197 if !v.is_finite() {
198 return f64::NAN;
199 }
200 let bounded_v = v.min(self.v_threshold);
201 let exp_arg = (bounded_v - self.v_rh) * inv_delta_t;
202 let exp_term = self.delta_t * exp_arg.exp();
203 (-(bounded_v - self.v_rest) + exp_term + current) / self.tau
204 }
205}
206
207#[cfg(test)]
208#[path = "exp_if_tests.rs"]
209mod tests;