1use rand::{RngExt, SeedableRng};
12use rand_xoshiro::Xoshiro256PlusPlus;
13
14#[derive(Clone, Debug)]
16pub struct PoissonNeuron {
17 pub rate_hz: f64,
18 pub dt_ms: f64,
19 rng: Xoshiro256PlusPlus,
20}
21
22impl PoissonNeuron {
23 pub fn new(rate_hz: f64, dt_ms: f64, seed: u64) -> Self {
24 Self {
25 rate_hz,
26 dt_ms,
27 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
28 }
29 }
30 pub fn step(&mut self, rate_override: f64) -> i32 {
31 let r = if rate_override < 0.0 {
32 self.rate_hz
33 } else {
34 rate_override
35 };
36 let p = r * self.dt_ms / 1000.0;
37 if self.rng.random::<f64>() < p {
38 1
39 } else {
40 0
41 }
42 }
43 pub fn reset(&mut self) {}
44}
45
46#[derive(Clone, Debug)]
48pub struct InhomogeneousPoissonNeuron {
49 pub dt_ms: f64,
50 rng: Xoshiro256PlusPlus,
51}
52
53impl InhomogeneousPoissonNeuron {
54 pub fn new(dt_ms: f64, seed: u64) -> Self {
55 Self {
56 dt_ms,
57 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
58 }
59 }
60 pub fn step(&mut self, rate_hz: f64) -> i32 {
61 let p = rate_hz.max(0.0) * self.dt_ms / 1000.0;
62 if self.rng.random::<f64>() < p {
63 1
64 } else {
65 0
66 }
67 }
68 pub fn reset(&mut self) {}
69}
70
71#[derive(Clone, Debug)]
73pub struct GammaRenewalNeuron {
74 pub rate_hz: f64,
75 pub shape_k: u32,
76 pub dt_ms: f64,
77 time_since_spike: f64,
78 rng: Xoshiro256PlusPlus,
79}
80
81impl GammaRenewalNeuron {
82 pub fn new(rate_hz: f64, shape_k: u32, seed: u64) -> Self {
83 Self {
84 rate_hz,
85 shape_k,
86 dt_ms: 1.0,
87 time_since_spike: 0.0,
88 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
89 }
90 }
91 fn log_gamma_int(k: u32) -> f64 {
92 (1..k).map(|i| (i as f64).ln()).sum()
94 }
95 pub fn step(&mut self, rate_override: f64) -> i32 {
96 self.time_since_spike += self.dt_ms;
97 let r = if rate_override < 0.0 {
98 self.rate_hz
99 } else {
100 rate_override
101 };
102 let lam = r / 1000.0;
103 let k = self.shape_k;
104 let t = self.time_since_spike;
105 let mu = lam * (k as f64);
108 let log_pdf = (k as f64 - 1.0) * (mu * t).max(1e-30).ln() + mu.max(1e-30).ln()
109 - mu * t
110 - Self::log_gamma_int(k);
111 let surv = 1.0 - self.incomplete_gamma_ratio(k, mu * t);
112 let hazard = if surv > 1e-15 {
113 log_pdf.exp() / surv
114 } else {
115 mu
116 };
117 let p = hazard * self.dt_ms;
118 if self.rng.random::<f64>() < p {
119 self.time_since_spike = 0.0;
120 1
121 } else {
122 0
123 }
124 }
125 fn incomplete_gamma_ratio(&self, k: u32, x: f64) -> f64 {
126 if x <= 0.0 {
128 return 0.0;
129 }
130 let mut sum = 0.0_f64;
131 let mut term = 1.0_f64;
132 for n in 0..k {
133 if n > 0 {
134 term *= x / n as f64;
135 }
136 sum += term;
137 }
138 1.0 - (-x).exp() * sum
139 }
140 pub fn reset(&mut self) {
141 self.time_since_spike = 0.0;
142 }
143}
144
145#[derive(Clone, Debug)]
147pub struct StochasticIFNeuron {
148 pub v: f64,
149 pub v_rest: f64,
150 pub v_reset: f64,
151 pub v_threshold: f64,
152 pub tau_m: f64,
153 pub sigma: f64,
154 pub dt: f64,
155 rng: Xoshiro256PlusPlus,
156}
157
158impl StochasticIFNeuron {
159 pub fn new(seed: u64) -> Self {
160 Self {
161 v: -70.0,
162 v_rest: -70.0,
163 v_reset: -70.0,
164 v_threshold: -50.0,
165 tau_m: 20.0,
166 sigma: 3.0,
167 dt: 1.0,
168 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
169 }
170 }
171 fn randn(&mut self) -> f64 {
172 let u1: f64 = self.rng.random::<f64>().max(1e-30);
174 let u2: f64 = self.rng.random::<f64>();
175 (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
176 }
177 pub fn step(&mut self, current: f64) -> i32 {
178 let noise = self.sigma * (self.dt / self.tau_m).sqrt() * self.randn();
179 self.v += (-(self.v - self.v_rest) + current) / self.tau_m * self.dt + noise;
180 if self.v >= self.v_threshold {
181 self.v = self.v_reset;
182 1
183 } else {
184 0
185 }
186 }
187 pub fn reset(&mut self) {
188 self.v = self.v_rest;
189 }
190}
191
192#[derive(Clone, Debug)]
194pub struct GalvesLocherbachNeuron {
195 pub v: f64,
196 pub v_rest: f64,
197 pub decay: f64,
198 pub threshold_rate: f64,
199 pub steepness: f64,
200 pub dt: f64,
201 rng: Xoshiro256PlusPlus,
202}
203
204impl GalvesLocherbachNeuron {
205 pub fn new(seed: u64) -> Self {
206 Self {
207 v: 0.0,
208 v_rest: 0.0,
209 decay: 0.95,
210 threshold_rate: 0.5,
211 steepness: 5.0,
212 dt: 1.0,
213 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
214 }
215 }
216 pub fn step(&mut self, weighted_input: f64) -> i32 {
217 self.v = self.decay * self.v + weighted_input;
218 let p = 1.0 / (1.0 + (-(self.steepness * (self.v - self.threshold_rate))).exp());
219 if self.rng.random::<f64>() < p * self.dt {
220 self.v = self.v_rest;
221 1
222 } else {
223 0
224 }
225 }
226 pub fn reset(&mut self) {
227 self.v = self.v_rest;
228 }
229}
230
231#[derive(Clone, Debug)]
233pub struct SpikeResponseNeuron {
234 pub v: f64,
235 pub v_threshold: f64,
236 pub tau_eta: f64,
237 pub tau_kappa: f64,
238 pub eta_reset: f64,
239 pub time_since_spike: f64,
240 pub dt: f64,
241}
242
243impl SpikeResponseNeuron {
244 pub fn new() -> Self {
245 Self {
246 v: 0.0,
247 v_threshold: 1.0,
248 tau_eta: 10.0,
249 tau_kappa: 5.0,
250 eta_reset: -5.0,
251 time_since_spike: 1000.0,
252 dt: 1.0,
253 }
254 }
255 pub fn step(&mut self, weighted_input: f64) -> i32 {
256 self.time_since_spike += self.dt;
257 let eta = self.eta_reset * (-self.time_since_spike / self.tau_eta).exp();
258 let kappa = weighted_input * (1.0 - (-self.dt / self.tau_kappa).exp());
259 self.v = eta + kappa;
260 if self.v >= self.v_threshold {
261 self.time_since_spike = 0.0;
262 1
263 } else {
264 0
265 }
266 }
267 pub fn reset(&mut self) {
268 self.v = 0.0;
269 self.time_since_spike = 1000.0;
270 }
271}
272impl Default for SpikeResponseNeuron {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278#[derive(Clone, Debug)]
280pub struct GLMNeuron {
281 pub mu: f64,
282 pub dt_ms: f64,
283 pub k: Vec<f64>,
284 pub h: Vec<f64>,
285 stim_buf: Vec<f64>,
286 spike_buf: Vec<f64>,
287 rng: Xoshiro256PlusPlus,
288}
289
290impl GLMNeuron {
291 pub fn new(n_k: usize, n_h: usize, seed: u64) -> Self {
292 Self {
293 mu: -3.0,
294 dt_ms: 1.0,
295 k: vec![0.1; n_k],
296 h: vec![-0.5; n_h],
297 stim_buf: vec![0.0; n_k],
298 spike_buf: vec![0.0; n_h],
299 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
300 }
301 }
302 pub fn step(&mut self, stimulus: f64) -> i32 {
303 let nk = self.stim_buf.len();
305 let nh = self.spike_buf.len();
306 for i in (1..nk).rev() {
307 self.stim_buf[i] = self.stim_buf[i - 1];
308 }
309 if nk > 0 {
310 self.stim_buf[0] = stimulus;
311 }
312 let dot_k: f64 = self
313 .k
314 .iter()
315 .zip(self.stim_buf.iter())
316 .map(|(a, b)| a * b)
317 .sum();
318 let dot_h: f64 = self
319 .h
320 .iter()
321 .zip(self.spike_buf.iter())
322 .map(|(a, b)| a * b)
323 .sum();
324 let lam = (dot_k + dot_h + self.mu).exp();
325 let p = lam * self.dt_ms / 1000.0;
326 let spike = if self.rng.random::<f64>() < p { 1 } else { 0 };
327 for i in (1..nh).rev() {
328 self.spike_buf[i] = self.spike_buf[i - 1];
329 }
330 if nh > 0 {
331 self.spike_buf[0] = spike as f64;
332 }
333 spike
334 }
335 pub fn reset(&mut self) {
336 self.stim_buf.fill(0.0);
337 self.spike_buf.fill(0.0);
338 }
339}
340
341#[derive(Clone, Debug)]
343pub struct WilsonCowanUnit {
344 pub e: f64,
345 pub i: f64,
346 pub w_ee: f64,
347 pub w_ei: f64,
348 pub w_ie: f64,
349 pub w_ii: f64,
350 pub tau_e: f64,
351 pub tau_i: f64,
352 pub a: f64,
353 pub theta: f64,
354 pub dt: f64,
355}
356
357impl WilsonCowanUnit {
358 pub fn new() -> Self {
359 Self {
360 e: 0.1,
361 i: 0.05,
362 w_ee: 10.0,
363 w_ei: 6.0,
364 w_ie: 10.0,
365 w_ii: 1.0,
366 tau_e: 1.0,
367 tau_i: 2.0,
368 a: 1.2,
369 theta: 4.0,
370 dt: 0.1,
371 }
372 }
373 fn logistic(&self, z: f64) -> f64 {
374 if z >= 0.0 {
375 1.0 / (1.0 + (-z).exp())
376 } else {
377 let exp_z = z.exp();
378 exp_z / (1.0 + exp_z)
379 }
380 }
381 fn sigmoid(&self, x: f64) -> f64 {
382 self.logistic(self.a * (x - self.theta)) - self.logistic(-self.a * self.theta)
383 }
384 fn derivatives(&self, e: f64, i: f64, ext_input: f64) -> (f64, f64) {
385 let se = self.sigmoid(self.w_ee * e - self.w_ei * i + ext_input);
386 let si = self.sigmoid(self.w_ie * e - self.w_ii * i);
387 ((-e + se) / self.tau_e, (-i + si) / self.tau_i)
388 }
389 pub fn step(&mut self, ext_input: f64) -> f64 {
390 let e = self.e;
391 let i = self.i;
392 let (k1_e, k1_i) = self.derivatives(e, i, ext_input);
393 let (k2_e, k2_i) = self.derivatives(
394 e + 0.5 * self.dt * k1_e,
395 i + 0.5 * self.dt * k1_i,
396 ext_input,
397 );
398 let (k3_e, k3_i) = self.derivatives(
399 e + 0.5 * self.dt * k2_e,
400 i + 0.5 * self.dt * k2_i,
401 ext_input,
402 );
403 let (k4_e, k4_i) = self.derivatives(e + self.dt * k3_e, i + self.dt * k3_i, ext_input);
404 self.e = e + self.dt * (k1_e + 2.0 * k2_e + 2.0 * k3_e + k4_e) / 6.0;
405 self.i = i + self.dt * (k1_i + 2.0 * k2_i + 2.0 * k3_i + k4_i) / 6.0;
406 self.e
407 }
408 pub fn reset(&mut self) {
409 self.e = 0.1;
410 self.i = 0.05;
411 }
412}
413impl Default for WilsonCowanUnit {
414 fn default() -> Self {
415 Self::new()
416 }
417}
418
419#[derive(Clone, Debug)]
421pub struct JansenRitUnit {
422 pub y: [f64; 6],
423 pub a_exc: f64,
424 pub b_exc: f64,
425 pub a_rate: f64,
426 pub b_rate: f64,
427 pub c: f64,
428 pub e0: f64,
429 pub v0: f64,
430 pub r: f64,
431 pub dt: f64,
432}
433
434impl JansenRitUnit {
435 pub fn new() -> Self {
436 Self {
437 y: [0.0; 6],
438 a_exc: 3.25,
439 b_exc: 22.0,
440 a_rate: 100.0,
441 b_rate: 50.0,
442 c: 135.0,
443 e0: 2.5,
444 v0: 6.0,
445 r: 0.56,
446 dt: 0.001,
447 }
448 }
449 fn sigmoid(&self, x: f64) -> f64 {
450 2.0 * self.e0 / (1.0 + (self.r * (self.v0 - x)).exp())
451 }
452 pub fn step(&mut self, p_ext: f64) -> f64 {
453 let s1 = self.sigmoid(self.y[1] - self.y[2]);
454 let s0 = self.sigmoid(self.c * 0.8 * self.y[0]);
455 let s2 = self.sigmoid(self.c * 0.25 * self.y[0]);
456 let dy0 = self.y[3];
457 let dy3 = self.a_exc * self.a_rate * s1
458 - 2.0 * self.a_rate * self.y[3]
459 - self.a_rate.powi(2) * self.y[0];
460 let dy1 = self.y[4];
461 let dy4 = self.a_exc * self.a_rate * (p_ext + self.c * 0.8 * s0)
462 - 2.0 * self.a_rate * self.y[4]
463 - self.a_rate.powi(2) * self.y[1];
464 let dy2 = self.y[5];
465 let dy5 = self.b_exc * self.b_rate * self.c * 0.25 * s2
466 - 2.0 * self.b_rate * self.y[5]
467 - self.b_rate.powi(2) * self.y[2];
468 self.y[0] += dy0 * self.dt;
469 self.y[3] += dy3 * self.dt;
470 self.y[1] += dy1 * self.dt;
471 self.y[4] += dy4 * self.dt;
472 self.y[2] += dy2 * self.dt;
473 self.y[5] += dy5 * self.dt;
474 self.y[1] - self.y[2]
475 }
476 pub fn reset(&mut self) {
477 self.y = [0.0; 6];
478 }
479}
480impl Default for JansenRitUnit {
481 fn default() -> Self {
482 Self::new()
483 }
484}
485
486#[derive(Clone, Debug)]
488pub struct WongWangUnit {
489 pub s1: f64,
490 pub s2: f64,
491 pub tau_s: f64,
492 pub gamma: f64,
493 pub j_n: f64,
494 pub j_cross: f64,
495 pub i_0: f64,
496 pub sigma: f64,
497 pub dt: f64,
498 rng: Xoshiro256PlusPlus,
499}
500
501impl WongWangUnit {
502 pub fn new(seed: u64) -> Self {
503 Self {
504 s1: 0.1,
505 s2: 0.1,
506 tau_s: 0.1,
507 gamma: 0.641,
508 j_n: 0.2609,
509 j_cross: 0.0497,
510 i_0: 0.3255,
511 sigma: 0.02,
512 dt: 0.001,
513 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
514 }
515 }
516 fn phi(&self, i_total: f64) -> f64 {
517 let a = 270.0;
518 let b = 108.0;
519 let d = 0.154;
520 let x = a * i_total - b;
521 let denom = 1.0 - (-d * x).exp();
522 if denom.abs() < 1e-10 {
523 1.0 / d
524 } else {
525 x / denom
526 }
527 }
528 fn derivatives(
529 &self,
530 s1: f64,
531 s2: f64,
532 stim1: f64,
533 stim2: f64,
534 noise1: f64,
535 noise2: f64,
536 ) -> (f64, f64, f64, f64) {
537 let i1 = self.j_n * s1 - self.j_cross * s2 + self.i_0 + stim1 + noise1;
538 let i2 = self.j_n * s2 - self.j_cross * s1 + self.i_0 + stim2 + noise2;
539 let r1 = self.phi(i1);
540 let r2 = self.phi(i2);
541 (
542 -s1 / self.tau_s + self.gamma * (1.0 - s1) * r1,
543 -s2 / self.tau_s + self.gamma * (1.0 - s2) * r2,
544 r1,
545 r2,
546 )
547 }
548 fn randn(&mut self) -> f64 {
549 let u1 = self.rng.random::<f64>().max(1e-30);
550 let u2 = self.rng.random::<f64>();
551 (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
552 }
553 pub fn step(&mut self, stim1: f64, stim2: f64) -> (f64, f64) {
554 let noise1 = self.sigma * self.randn();
555 let noise2 = self.sigma * self.randn();
556 let (k1_s1, k1_s2, r1, r2) =
557 self.derivatives(self.s1, self.s2, stim1, stim2, noise1, noise2);
558 let (k2_s1, k2_s2, _, _) = self.derivatives(
559 self.s1 + 0.5 * self.dt * k1_s1,
560 self.s2 + 0.5 * self.dt * k1_s2,
561 stim1,
562 stim2,
563 noise1,
564 noise2,
565 );
566 let (k3_s1, k3_s2, _, _) = self.derivatives(
567 self.s1 + 0.5 * self.dt * k2_s1,
568 self.s2 + 0.5 * self.dt * k2_s2,
569 stim1,
570 stim2,
571 noise1,
572 noise2,
573 );
574 let (k4_s1, k4_s2, _, _) = self.derivatives(
575 self.s1 + self.dt * k3_s1,
576 self.s2 + self.dt * k3_s2,
577 stim1,
578 stim2,
579 noise1,
580 noise2,
581 );
582 self.s1 =
583 (self.s1 + self.dt * (k1_s1 + 2.0 * k2_s1 + 2.0 * k3_s1 + k4_s1) / 6.0).clamp(0.0, 1.0);
584 self.s2 =
585 (self.s2 + self.dt * (k1_s2 + 2.0 * k2_s2 + 2.0 * k3_s2 + k4_s2) / 6.0).clamp(0.0, 1.0);
586 (r1, r2)
587 }
588 pub fn reset(&mut self) {
589 self.s1 = 0.1;
590 self.s2 = 0.1;
591 }
592}
593
594#[derive(Clone, Debug)]
596pub struct ErmentroutKopellPopulation {
597 pub r: f64,
598 pub v: f64,
599 pub tau: f64,
600 pub delta: f64,
601 pub eta_bar: f64,
602 pub j: f64,
603 pub dt: f64,
604}
605
606impl ErmentroutKopellPopulation {
607 pub fn new() -> Self {
608 Self {
609 r: 0.1,
610 v: -2.0,
611 tau: 1.0,
612 delta: 1.0,
613 eta_bar: -5.0,
614 j: 15.0,
615 dt: 0.01,
616 }
617 }
618 pub fn step(&mut self, ext_input: f64) -> f64 {
619 let dr =
620 (self.delta / (std::f64::consts::PI * self.tau) + 2.0 * self.r * self.v) / self.tau;
621 let dv = (self.v * self.v + self.eta_bar + ext_input + self.j * self.tau * self.r
622 - std::f64::consts::PI.powi(2) * self.tau.powi(2) * self.r.powi(2))
623 / self.tau;
624 self.r += dr * self.dt;
625 self.v += dv * self.dt;
626 self.r = self.r.max(0.0);
627 self.r
628 }
629 pub fn reset(&mut self) {
630 self.r = 0.1;
631 self.v = -2.0;
632 }
633}
634impl Default for ErmentroutKopellPopulation {
635 fn default() -> Self {
636 Self::new()
637 }
638}
639
640#[derive(Clone, Debug)]
642pub struct WendlingNeuron {
643 pub y: [f64; 10],
644 pub a_exc: f64,
645 pub b_fast: f64,
646 pub g_slow: f64,
647 pub a_rate: f64,
648 pub b_rate: f64,
649 pub g_rate: f64,
650 pub c: f64,
651 pub e0: f64,
652 pub v0: f64,
653 pub r: f64,
654 pub dt: f64,
655}
656
657impl WendlingNeuron {
658 pub fn new() -> Self {
659 Self {
660 y: [0.0; 10],
661 a_exc: 3.25,
662 b_fast: 22.0,
663 g_slow: 10.0,
664 a_rate: 100.0,
665 b_rate: 500.0,
666 g_rate: 20.0,
667 c: 135.0,
668 e0: 2.5,
669 v0: 6.0,
670 r: 0.56,
671 dt: 0.001,
672 }
673 }
674 fn sigmoid(&self, x: f64) -> f64 {
675 2.0 * self.e0 / (1.0 + (self.r * (self.v0 - x)).exp())
676 }
677 pub fn step(&mut self, p_ext: f64) -> f64 {
678 let sig_main = self.sigmoid(self.y[1] - self.y[2] - self.y[3]);
679 let sig_0 = self.sigmoid(self.c * 0.8 * self.y[0]);
680 let sig_fast = self.sigmoid(self.c * 0.25 * self.y[0]);
681 let sig_slow = self.sigmoid(self.c * 0.1 * self.y[0]);
682 let a = self.a_rate;
683 let b = self.b_rate;
684 let g = self.g_rate;
685 let dy0 = self.y[5];
686 let dy5 = self.a_exc * a * sig_main - 2.0 * a * self.y[5] - a * a * self.y[0];
687 let dy1 = self.y[6];
688 let dy6 = self.a_exc * a * (p_ext + self.c * 0.8 * sig_0)
689 - 2.0 * a * self.y[6]
690 - a * a * self.y[1];
691 let dy2 = self.y[7];
692 let dy7 =
693 self.b_fast * b * self.c * 0.25 * sig_fast - 2.0 * b * self.y[7] - b * b * self.y[2];
694 let dy3 = self.y[8];
695 let dy8 =
696 self.g_slow * g * self.c * 0.1 * sig_slow - 2.0 * g * self.y[8] - g * g * self.y[3];
697 self.y[0] += dy0 * self.dt;
698 self.y[5] += dy5 * self.dt;
699 self.y[1] += dy1 * self.dt;
700 self.y[6] += dy6 * self.dt;
701 self.y[2] += dy2 * self.dt;
702 self.y[7] += dy7 * self.dt;
703 self.y[3] += dy3 * self.dt;
704 self.y[8] += dy8 * self.dt;
705 self.y[1] - self.y[2] - self.y[3]
706 }
707 pub fn reset(&mut self) {
708 self.y = [0.0; 10];
709 }
710}
711impl Default for WendlingNeuron {
712 fn default() -> Self {
713 Self::new()
714 }
715}
716
717#[derive(Clone, Debug)]
719pub struct LarterBreakspearNeuron {
720 pub v: f64,
721 pub w: f64,
722 pub z: f64,
723 pub g_ca: f64,
724 pub g_na: f64,
725 pub g_k: f64,
726 pub g_l: f64,
727 pub v_ca: f64,
728 pub v_na: f64,
729 pub v_k: f64,
730 pub v_l: f64,
731 pub phi: f64,
732 pub tau_k: f64,
733 pub b: f64,
734 pub a_ee: f64,
735 pub i_ext: f64,
736 pub dt: f64,
737}
738
739impl LarterBreakspearNeuron {
740 pub fn new() -> Self {
741 Self {
742 v: -0.5,
743 w: 0.0,
744 z: 0.0,
745 g_ca: 1.1,
746 g_na: 6.7,
747 g_k: 2.0,
748 g_l: 0.5,
749 v_ca: 1.0,
750 v_na: 0.53,
751 v_k: -0.7,
752 v_l: -0.5,
753 phi: 0.7,
754 tau_k: 1.0,
755 b: 0.1,
756 a_ee: 0.36,
757 i_ext: 0.3,
758 dt: 0.01,
759 }
760 }
761 pub fn step(&mut self, coupling: f64) -> f64 {
762 let m_ca = 0.5 * (1.0 + ((self.v + 0.01) / 0.15).tanh());
763 let m_na = 0.5 * (1.0 + ((self.v - 0.12) / 0.15).tanh());
764 let m_k = 0.5 * (1.0 + (self.v / 0.3).tanh());
765 let i_ca = self.g_ca * m_ca * (self.v - self.v_ca);
766 let i_na = self.g_na * m_na * (self.v - self.v_na);
767 let i_k = self.g_k * self.w * (self.v - self.v_k);
768 let i_l = self.g_l * (self.v - self.v_l);
769 let dv = -i_ca - i_na - i_k - i_l + self.i_ext + coupling + self.a_ee * self.v;
770 let dw = self.phi * (m_k - self.w) / self.tau_k;
771 let dz = self.b * (self.v + 0.5 - self.z);
772 self.v += dv * self.dt;
773 self.w += dw * self.dt;
774 self.z += dz * self.dt;
775 self.v
776 }
777 pub fn reset(&mut self) {
778 self.v = -0.5;
779 self.w = 0.0;
780 self.z = 0.0;
781 }
782}
783impl Default for LarterBreakspearNeuron {
784 fn default() -> Self {
785 Self::new()
786 }
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792
793 #[test]
794 fn poisson_fires() {
795 let mut n = PoissonNeuron::new(200.0, 1.0, 42);
796 let t: i32 = (0..1000).map(|_| n.step(-1.0)).sum();
797 assert!(t > 10);
798 }
799 #[test]
800 fn inhom_poisson_fires() {
801 let mut n = InhomogeneousPoissonNeuron::new(1.0, 42);
802 let t: i32 = (0..1000).map(|_| n.step(200.0)).sum();
803 assert!(t > 10);
804 }
805 #[test]
806 fn gamma_renewal_fires() {
807 let mut n = GammaRenewalNeuron::new(100.0, 3, 42);
808 let t: i32 = (0..2000).map(|_| n.step(-1.0)).sum();
809 assert!(t > 0);
810 }
811 #[test]
812 fn stochastic_if_fires() {
813 let mut n = StochasticIFNeuron::new(42);
814 let t: i32 = (0..500).map(|_| n.step(30.0)).sum();
815 assert!(t > 0);
816 }
817 #[test]
818 fn gl_fires() {
819 let mut n = GalvesLocherbachNeuron::new(42);
820 let t: i32 = (0..200).map(|_| n.step(2.0)).sum();
821 assert!(t > 0);
822 }
823 #[test]
824 fn srm_fires() {
825 let mut n = SpikeResponseNeuron::new();
826 let t: i32 = (0..200).map(|_| n.step(10.0)).sum();
827 assert!(t > 0);
828 }
829 #[test]
830 fn glm_fires() {
831 let mut n = GLMNeuron::new(5, 10, 42);
832 let t: i32 = (0..5000).map(|_| n.step(20.0)).sum();
833 assert!(t > 0);
834 }
835 #[test]
836 fn wc_oscillates() {
837 let mut n = WilsonCowanUnit::new();
838 let mut last = n.e;
839 let mut changes = 0;
840 for _ in 0..500 {
841 let e = n.step(5.0);
842 if (e - last).abs() > 0.001 {
843 changes += 1;
844 }
845 last = e;
846 }
847 assert!(changes > 0);
848 }
849 #[test]
850 fn jr_produces_eeg() {
851 let mut n = JansenRitUnit::new();
852 let mut nz = 0;
853 for _ in 0..5000 {
854 let v = n.step(220.0);
855 if v.abs() > 0.001 {
856 nz += 1;
857 }
858 }
859 assert!(nz > 0);
860 }
861 #[test]
862 fn ww_diverges() {
863 let mut n = WongWangUnit::new(42);
864 for _ in 0..5000 {
865 n.step(0.02, 0.0);
866 }
867 assert!((n.s1 - n.s2).abs() > 0.001);
868 }
869 #[test]
870 fn ek_pop_fires() {
871 let mut n = ErmentroutKopellPopulation::new();
872 for _ in 0..1000 {
873 n.step(0.0);
874 }
875 assert!(n.r > 0.0);
876 }
877 #[test]
878 fn wendling_produces() {
879 let mut n = WendlingNeuron::new();
880 let mut nz = 0;
881 for _ in 0..5000 {
882 let v = n.step(220.0);
883 if v.abs() > 0.001 {
884 nz += 1;
885 }
886 }
887 assert!(nz > 0);
888 }
889 #[test]
890 fn lb_evolves() {
891 let mut n = LarterBreakspearNeuron::new();
892 let v0 = n.v;
893 for _ in 0..1000 {
894 n.step(0.0);
895 }
896 assert!((n.v - v0).abs() > 0.001);
897 }
898
899 #[test]
903 fn poisson_reset_no_panic() {
904 let mut n = PoissonNeuron::new(200.0, 1.0, 42);
905 for _ in 0..100 {
906 n.step(-1.0);
907 }
908 n.reset();
909 }
910 #[test]
911 fn poisson_nan_no_panic() {
912 let mut n = PoissonNeuron::new(200.0, 1.0, 42);
913 n.step(f64::NAN);
914 }
915 #[test]
916 fn poisson_seed_varies() {
917 let mut n1 = PoissonNeuron::new(200.0, 1.0, 1);
918 let mut n2 = PoissonNeuron::new(200.0, 1.0, 999);
919 let t1: i32 = (0..1000).map(|_| n1.step(-1.0)).sum();
920 let t2: i32 = (0..1000).map(|_| n2.step(-1.0)).sum();
921 assert!(t1 > 0 && t2 > 0);
922 }
923
924 #[test]
926 fn inhom_poisson_zero_rate() {
927 let mut n = InhomogeneousPoissonNeuron::new(1.0, 42);
928 let t: i32 = (0..1000).map(|_| n.step(0.0)).sum();
929 assert_eq!(t, 0);
930 }
931 #[test]
932 fn inhom_poisson_nan_no_panic() {
933 let mut n = InhomogeneousPoissonNeuron::new(1.0, 42);
934 n.step(f64::NAN);
935 }
936
937 #[test]
939 fn gamma_renewal_reset_clears() {
940 let mut n = GammaRenewalNeuron::new(100.0, 3, 42);
941 for _ in 0..100 {
942 n.step(-1.0);
943 }
944 n.reset();
945 assert!((n.time_since_spike - 0.0).abs() < 1e-10);
946 }
947 #[test]
948 fn gamma_renewal_nan_no_panic() {
949 let mut n = GammaRenewalNeuron::new(100.0, 3, 42);
950 n.step(f64::NAN);
951 }
952
953 #[test]
955 fn stochastic_if_reset_clears() {
956 let mut n = StochasticIFNeuron::new(42);
957 for _ in 0..100 {
958 n.step(30.0);
959 }
960 n.reset();
961 assert!((n.v - n.v_rest).abs() < 1e-10);
962 }
963 #[test]
964 fn stochastic_if_bounded() {
965 let mut n = StochasticIFNeuron::new(42);
966 for _ in 0..1000 {
967 n.step(1e4);
968 }
969 assert!(n.v.is_finite());
970 }
971 #[test]
972 fn stochastic_if_nan_no_panic() {
973 StochasticIFNeuron::new(42).step(f64::NAN);
974 }
975 #[test]
976 fn stochastic_if_negative_no_crash() {
977 let mut n = StochasticIFNeuron::new(42);
978 for _ in 0..500 {
979 n.step(-10.0);
980 }
981 assert!(n.v.is_finite());
982 }
983
984 #[test]
986 fn gl_reset_clears() {
987 let mut n = GalvesLocherbachNeuron::new(42);
988 for _ in 0..100 {
989 n.step(2.0);
990 }
991 n.reset();
992 assert!((n.v - n.v_rest).abs() < 1e-10);
993 }
994 #[test]
995 fn gl_bounded() {
996 let mut n = GalvesLocherbachNeuron::new(42);
997 for _ in 0..1000 {
998 n.step(1e4);
999 }
1000 assert!(n.v.is_finite());
1001 }
1002 #[test]
1003 fn gl_nan_no_panic() {
1004 GalvesLocherbachNeuron::new(42).step(f64::NAN);
1005 }
1006
1007 #[test]
1009 fn srm_reset_clears() {
1010 let mut n = SpikeResponseNeuron::new();
1011 for _ in 0..100 {
1012 n.step(10.0);
1013 }
1014 n.reset();
1015 assert!((n.v - 0.0).abs() < 1e-10);
1016 }
1017 #[test]
1018 fn srm_bounded() {
1019 let mut n = SpikeResponseNeuron::new();
1020 for _ in 0..1000 {
1021 n.step(1e4);
1022 }
1023 assert!(n.v.is_finite());
1024 }
1025 #[test]
1026 fn srm_nan_no_panic() {
1027 SpikeResponseNeuron::new().step(f64::NAN);
1028 }
1029 #[test]
1030 fn srm_silent_without_input() {
1031 let mut n = SpikeResponseNeuron::new();
1032 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
1033 assert_eq!(t, 0);
1034 }
1035
1036 #[test]
1038 fn glm_reset_clears() {
1039 let mut n = GLMNeuron::new(5, 10, 42);
1040 for _ in 0..100 {
1041 n.step(20.0);
1042 }
1043 n.reset();
1044 }
1045 #[test]
1046 fn glm_nan_no_panic() {
1047 GLMNeuron::new(5, 10, 42).step(f64::NAN);
1048 }
1049
1050 #[test]
1052 fn wc_reset_clears() {
1053 let mut n = WilsonCowanUnit::new();
1054 for _ in 0..200 {
1055 n.step(5.0);
1056 }
1057 n.reset();
1058 assert!((n.e - 0.1).abs() < 1e-10);
1059 assert!((n.i - 0.05).abs() < 1e-10);
1060 }
1061 #[test]
1062 fn wc_bounded() {
1063 let mut n = WilsonCowanUnit::new();
1064 for _ in 0..5000 {
1065 n.step(1e3);
1066 }
1067 assert!(n.e.is_finite());
1068 assert!(n.i.is_finite());
1069 }
1070 #[test]
1071 fn wc_nan_no_panic() {
1072 WilsonCowanUnit::new().step(f64::NAN);
1073 }
1074
1075 #[test]
1077 fn jr_reset_clears() {
1078 let mut n = JansenRitUnit::new();
1079 for _ in 0..1000 {
1080 n.step(220.0);
1081 }
1082 n.reset();
1083 assert!(n.y.iter().all(|&x| x == 0.0));
1084 }
1085 #[test]
1086 fn jr_bounded() {
1087 let mut n = JansenRitUnit::new();
1088 for _ in 0..5000 {
1089 n.step(1e3);
1090 }
1091 assert!(n.y.iter().all(|x| x.is_finite()));
1092 }
1093 #[test]
1094 fn jr_nan_no_panic() {
1095 JansenRitUnit::new().step(f64::NAN);
1096 }
1097
1098 #[test]
1100 fn ww_reset_clears() {
1101 let mut n = WongWangUnit::new(42);
1102 for _ in 0..1000 {
1103 n.step(0.02, 0.0);
1104 }
1105 n.reset();
1106 assert!((n.s1 - 0.1).abs() < 1e-10);
1107 assert!((n.s2 - 0.1).abs() < 1e-10);
1108 }
1109 #[test]
1110 fn ww_bounded() {
1111 let mut n = WongWangUnit::new(42);
1112 for _ in 0..5000 {
1113 n.step(1.0, 0.0);
1114 }
1115 assert!(n.s1.is_finite());
1116 assert!(n.s2.is_finite());
1117 }
1118 #[test]
1119 fn ww_nan_no_panic() {
1120 WongWangUnit::new(42).step(f64::NAN, 0.0);
1121 }
1122
1123 #[test]
1125 fn ek_pop_reset_clears() {
1126 let mut n = ErmentroutKopellPopulation::new();
1127 for _ in 0..500 {
1128 n.step(0.0);
1129 }
1130 n.reset();
1131 assert!((n.r - 0.1).abs() < 1e-10);
1132 assert!((n.v - (-2.0)).abs() < 1e-10);
1133 }
1134 #[test]
1135 fn ek_pop_moderate_stable() {
1136 let mut n = ErmentroutKopellPopulation::new();
1137 for _ in 0..5000 {
1138 n.step(1.0);
1139 }
1140 assert!(n.r.is_finite());
1141 assert!(n.v.is_finite());
1142 }
1143 #[test]
1144 fn ek_pop_nan_no_panic() {
1145 ErmentroutKopellPopulation::new().step(f64::NAN);
1146 }
1147
1148 #[test]
1150 fn wendling_reset_clears() {
1151 let mut n = WendlingNeuron::new();
1152 for _ in 0..1000 {
1153 n.step(220.0);
1154 }
1155 n.reset();
1156 assert!(n.y.iter().all(|&x| x == 0.0));
1157 }
1158 #[test]
1159 fn wendling_bounded() {
1160 let mut n = WendlingNeuron::new();
1161 for _ in 0..5000 {
1162 n.step(1e3);
1163 }
1164 assert!(n.y.iter().all(|x| x.is_finite()));
1165 }
1166 #[test]
1167 fn wendling_nan_no_panic() {
1168 WendlingNeuron::new().step(f64::NAN);
1169 }
1170
1171 #[test]
1173 fn lb_reset_clears() {
1174 let mut n = LarterBreakspearNeuron::new();
1175 for _ in 0..500 {
1176 n.step(0.0);
1177 }
1178 n.reset();
1179 assert!((n.v - (-0.5)).abs() < 1e-10);
1180 assert!((n.w - 0.0).abs() < 1e-10);
1181 assert!((n.z - 0.0).abs() < 1e-10);
1182 }
1183 #[test]
1184 fn lb_bounded() {
1185 let mut n = LarterBreakspearNeuron::new();
1186 for _ in 0..5000 {
1187 n.step(10.0);
1188 }
1189 assert!(n.v.is_finite());
1190 }
1191 #[test]
1192 fn lb_nan_no_panic() {
1193 LarterBreakspearNeuron::new().step(f64::NAN);
1194 }
1195}