1#[derive(Clone, Debug)]
13pub struct LoihiCUBANeuron {
14 pub v: i32,
15 pub u: i32,
16 pub tau_v: i32,
17 pub tau_u: i32,
18 pub v_threshold: i32,
19 pub v_reset: i32,
20}
21
22impl LoihiCUBANeuron {
23 pub fn new() -> Self {
24 Self {
25 v: 0,
26 u: 0,
27 tau_v: 10,
28 tau_u: 5,
29 v_threshold: 1000,
30 v_reset: 0,
31 }
32 }
33 pub fn step(&mut self, weighted_input: i32) -> i32 {
34 self.u = self.u - self.u / self.tau_u + weighted_input;
35 self.v = self.v - self.v / self.tau_v + self.u;
36 if self.v >= self.v_threshold {
37 self.v = self.v_reset;
38 1
39 } else {
40 0
41 }
42 }
43 pub fn reset(&mut self) {
44 self.v = 0;
45 self.u = 0;
46 }
47}
48impl Default for LoihiCUBANeuron {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54#[derive(Clone, Debug)]
56pub struct Loihi2Neuron {
57 pub s1: i32,
58 pub s2: i32,
59 pub s3: i32,
60 pub tau1: i32,
61 pub tau2: i32,
62 pub tau3: i32,
63 pub w12: i32,
64 pub w13: i32,
65 pub w23: i32,
66 pub s1_threshold: i32,
67 pub s1_reset: i32,
68 pub s3_incr: i32,
69}
70
71impl Loihi2Neuron {
72 pub fn new() -> Self {
73 Self {
74 s1: 0,
75 s2: 0,
76 s3: 0,
77 tau1: 10,
78 tau2: 5,
79 tau3: 50,
80 w12: 1,
81 w13: 0,
82 w23: 0,
83 s1_threshold: 1000,
84 s1_reset: 0,
85 s3_incr: 10,
86 }
87 }
88 pub fn step(&mut self, weighted_input: i32) -> i32 {
89 self.s3 -= self.s3 / self.tau3;
90 self.s2 = self.s2 - self.s2 / self.tau2 + weighted_input + self.w23 * self.s3;
91 self.s1 = self.s1 - self.s1 / self.tau1 + self.w12 * self.s2 + self.w13 * self.s3;
92 if self.s1 >= self.s1_threshold {
93 self.s1 = self.s1_reset;
94 self.s3 += self.s3_incr;
95 1
96 } else {
97 0
98 }
99 }
100 pub fn reset(&mut self) {
101 self.s1 = 0;
102 self.s2 = 0;
103 self.s3 = 0;
104 }
105}
106impl Default for Loihi2Neuron {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112#[derive(Clone, Debug)]
114pub struct TrueNorthNeuron {
115 pub v: i32,
116 pub leak: i32,
117 pub threshold: i32,
118 pub v_reset: i32,
119}
120
121impl TrueNorthNeuron {
122 pub fn new(threshold: i32) -> Self {
123 Self {
124 v: 0,
125 leak: 0,
126 threshold,
127 v_reset: 0,
128 }
129 }
130 pub fn step(&mut self, weighted_input: i32) -> i32 {
131 self.v += weighted_input - self.leak;
132 if self.v >= self.threshold {
133 self.v = self.v_reset;
134 1
135 } else {
136 0
137 }
138 }
139 pub fn reset(&mut self) {
140 self.v = 0;
141 }
142}
143impl Default for TrueNorthNeuron {
144 fn default() -> Self {
145 Self::new(100)
146 }
147}
148
149#[derive(Clone, Debug)]
151pub struct BrainScaleSAdExNeuron {
152 pub v: f64,
153 pub w: f64,
154 pub v_rest: f64,
155 pub v_reset: f64,
156 pub v_threshold: f64,
157 pub delta_t: f64,
158 pub v_rh: f64,
159 pub tau: f64,
160 pub tau_w: f64,
161 pub a: f64,
162 pub b: f64,
163 pub hw_speedup: f64,
164 pub dt: f64,
165}
166
167impl BrainScaleSAdExNeuron {
168 pub fn new() -> Self {
169 Self {
170 v: -65.0,
171 w: 0.0,
172 v_rest: -65.0,
173 v_reset: -68.0,
174 v_threshold: -50.0,
175 delta_t: 2.0,
176 v_rh: -55.0,
177 tau: 20.0,
178 tau_w: 100.0,
179 a: 0.5,
180 b: 7.0,
181 hw_speedup: 1000.0,
182 dt: 0.1,
183 }
184 }
185 pub fn step(&mut self, current: f64) -> i32 {
186 let exp_arg = ((self.v - self.v_rh) / self.delta_t).clamp(-20.0, 20.0);
187 let exp_term = self.delta_t * exp_arg.exp();
188 let dv = (-(self.v - self.v_rest) + exp_term - self.w + current) / self.tau * self.dt;
189 let dw = (self.a * (self.v - self.v_rest) - self.w) / self.tau_w * self.dt;
190 self.v += dv;
191 self.w += dw;
192 if self.v >= self.v_threshold {
193 self.v = self.v_reset;
194 self.w += self.b;
195 1
196 } else {
197 0
198 }
199 }
200 pub fn reset(&mut self) {
201 self.v = self.v_rest;
202 self.w = 0.0;
203 }
204}
205impl Default for BrainScaleSAdExNeuron {
206 fn default() -> Self {
207 Self::new()
208 }
209}
210
211#[derive(Clone, Debug)]
213pub struct SpiNNakerLIFNeuron {
214 pub v: f64,
215 pub v_rest: f64,
216 pub v_reset: f64,
217 pub v_threshold: f64,
218 pub tau_m: f64,
219 pub i_offset: f64,
220 pub tau_refrac: f64,
221 pub refrac_count: f64,
222 pub dt: f64,
223}
224
225impl SpiNNakerLIFNeuron {
226 pub fn new() -> Self {
227 Self {
228 v: -70.0,
229 v_rest: -70.0,
230 v_reset: -70.0,
231 v_threshold: -50.0,
232 tau_m: 20.0,
233 i_offset: 0.0,
234 tau_refrac: 2.0,
235 refrac_count: 0.0,
236 dt: 1.0,
237 }
238 }
239 pub fn step(&mut self, current: f64) -> i32 {
240 if self.refrac_count > 0.0 {
241 self.refrac_count -= self.dt;
242 return 0;
243 }
244 self.v += (-(self.v - self.v_rest) + current + self.i_offset) / self.tau_m * self.dt;
245 if self.v >= self.v_threshold {
246 self.v = self.v_reset;
247 self.refrac_count = self.tau_refrac;
248 1
249 } else {
250 0
251 }
252 }
253 pub fn reset(&mut self) {
254 self.v = self.v_rest;
255 self.refrac_count = 0.0;
256 }
257}
258impl Default for SpiNNakerLIFNeuron {
259 fn default() -> Self {
260 Self::new()
261 }
262}
263
264#[derive(Clone, Debug)]
266pub struct SpiNNaker2Neuron {
267 pub v: i32,
268 pub v_rest: i32,
269 pub v_reset: i32,
270 pub v_threshold: i32,
271 pub decay_mult: i32,
272 pub decay_shift: i32,
273 pub refrac_steps: i32,
274 pub refrac_count: i32,
275}
276
277impl SpiNNaker2Neuron {
278 pub fn new() -> Self {
279 Self {
280 v: 0,
281 v_rest: 0,
282 v_reset: 0,
283 v_threshold: 1024,
284 decay_mult: 243,
285 decay_shift: 8,
286 refrac_steps: 2,
287 refrac_count: 0,
288 }
289 }
290 pub fn step(&mut self, current: i32) -> i32 {
291 if self.refrac_count > 0 {
292 self.refrac_count -= 1;
293 return 0;
294 }
295 self.v = ((self.v - self.v_rest).wrapping_mul(self.decay_mult) >> self.decay_shift)
296 + self.v_rest
297 + current;
298 if self.v >= self.v_threshold {
299 self.v = self.v_reset;
300 self.refrac_count = self.refrac_steps;
301 1
302 } else {
303 0
304 }
305 }
306 pub fn reset(&mut self) {
307 self.v = self.v_rest;
308 self.refrac_count = 0;
309 }
310}
311impl Default for SpiNNaker2Neuron {
312 fn default() -> Self {
313 Self::new()
314 }
315}
316
317#[derive(Clone, Debug)]
319pub struct DPINeuron {
320 pub i_mem: f64,
321 pub i_threshold: f64,
322 pub i_reset: f64,
323 pub i_leak: f64,
324 pub tau: f64,
325 pub gain: f64,
326 pub dt: f64,
327}
328
329impl DPINeuron {
330 pub fn new() -> Self {
331 Self {
332 i_mem: 0.0,
333 i_threshold: 1.0,
334 i_reset: 0.0,
335 i_leak: 0.01,
336 tau: 20.0,
337 gain: 1.0,
338 dt: 1.0,
339 }
340 }
341 pub fn step(&mut self, i_syn: f64) -> i32 {
342 self.i_mem += (-self.i_mem + self.gain * i_syn + self.i_leak) / self.tau * self.dt;
343 if self.i_mem >= self.i_threshold {
344 self.i_mem = self.i_reset;
345 1
346 } else {
347 0
348 }
349 }
350 pub fn reset(&mut self) {
351 self.i_mem = 0.0;
352 }
353}
354impl Default for DPINeuron {
355 fn default() -> Self {
356 Self::new()
357 }
358}
359
360#[derive(Clone, Debug)]
362pub struct AkidaNeuron {
363 pub v: i32,
364 pub threshold: i32,
365 pub modulation: f64,
366 pub rank: i32,
367 pub spiked: bool,
368}
369
370impl AkidaNeuron {
371 pub fn new(threshold: i32) -> Self {
372 Self {
373 v: 0,
374 threshold,
375 modulation: 0.75,
376 rank: 0,
377 spiked: false,
378 }
379 }
380 pub fn step(&mut self, weight: f64) -> i32 {
381 if self.spiked {
382 return 0;
383 }
384 self.v += (weight * self.modulation.powi(self.rank)) as i32;
385 self.rank += 1;
386 if self.v >= self.threshold {
387 self.spiked = true;
388 1
389 } else {
390 0
391 }
392 }
393 pub fn reset(&mut self) {
394 self.v = 0;
395 self.rank = 0;
396 self.spiked = false;
397 }
398}
399impl Default for AkidaNeuron {
400 fn default() -> Self {
401 Self::new(100)
402 }
403}
404
405#[derive(Clone, Debug)]
407pub struct NeuroGridNeuron {
408 pub v_s: f64,
409 pub v_d: f64,
410 pub tau_s: f64,
411 pub tau_d: f64,
412 pub g_c: f64,
413 pub delta_t: f64,
414 pub v_rest: f64,
415 pub v_threshold: f64,
416 pub v_peak: f64,
417 pub v_reset: f64,
418 pub dt: f64,
419}
420
421impl NeuroGridNeuron {
422 pub fn new() -> Self {
423 Self {
424 v_s: -65.0,
425 v_d: -65.0,
426 tau_s: 20.0,
427 tau_d: 50.0,
428 g_c: 0.5,
429 delta_t: 2.0,
430 v_rest: -65.0,
431 v_threshold: -50.0,
432 v_peak: 20.0,
433 v_reset: -65.0,
434 dt: 0.1,
435 }
436 }
437 fn valid(&self) -> bool {
438 self.v_s.is_finite()
439 && self.v_d.is_finite()
440 && self.tau_s.is_finite()
441 && self.tau_s > 0.0
442 && self.tau_d.is_finite()
443 && self.tau_d > 0.0
444 && self.g_c.is_finite()
445 && self.g_c >= 0.0
446 && self.delta_t.is_finite()
447 && self.delta_t > 0.0
448 && self.v_rest.is_finite()
449 && self.v_threshold.is_finite()
450 && self.v_peak.is_finite()
451 && self.v_reset.is_finite()
452 && self.dt.is_finite()
453 && self.dt > 0.0
454 }
455
456 fn derivatives(&self, v_s: f64, v_d: f64, current: f64) -> (f64, f64) {
457 let v_s_eff = v_s.min(self.v_peak);
458 let dv_d = (-(v_d - self.v_rest) + current - self.g_c * (v_d - v_s_eff)) / self.tau_d;
459 let exp_arg = ((v_s_eff - self.v_threshold) / self.delta_t).min(20.0);
460 let exp_term = self.delta_t * exp_arg.exp();
461 let dv_s = (-(v_s_eff - self.v_rest) + exp_term + self.g_c * (v_d - v_s_eff)) / self.tau_s;
462 (dv_s, dv_d)
463 }
464
465 fn rk4_substep(&self, v_s: f64, v_d: f64, current: f64) -> (f64, f64) {
466 let dt = self.dt;
467 let (k1s, k1d) = self.derivatives(v_s, v_d, current);
468 let (k2s, k2d) = self.derivatives(v_s + 0.5 * dt * k1s, v_d + 0.5 * dt * k1d, current);
469 let (k3s, k3d) = self.derivatives(v_s + 0.5 * dt * k2s, v_d + 0.5 * dt * k2d, current);
470 let (k4s, k4d) = self.derivatives(v_s + dt * k3s, v_d + dt * k3d, current);
471 (
472 v_s + dt * (k1s + 2.0 * k2s + 2.0 * k3s + k4s) / 6.0,
473 v_d + dt * (k1d + 2.0 * k2d + 2.0 * k3d + k4d) / 6.0,
474 )
475 }
476
477 pub fn step(&mut self, current: f64) -> i32 {
478 if !current.is_finite() || !self.valid() {
479 return 0;
480 }
481 let (next_v_s, next_v_d) = self.rk4_substep(self.v_s, self.v_d, current);
482 if !next_v_s.is_finite() || !next_v_d.is_finite() {
483 return 0;
484 }
485 self.v_d = next_v_d;
486 if next_v_s >= self.v_peak {
487 self.v_s = self.v_reset;
488 1
489 } else {
490 self.v_s = next_v_s;
491 0
492 }
493 }
494 pub fn reset(&mut self) {
495 self.v_s = -65.0;
496 self.v_d = -65.0;
497 }
498}
499impl Default for NeuroGridNeuron {
500 fn default() -> Self {
501 Self::new()
502 }
503}
504
505#[cfg(test)]
506mod tests {
507 use super::*;
508
509 #[test]
510 fn loihi_cuba_fires() {
511 let mut n = LoihiCUBANeuron::new();
512 let t: i32 = (0..200).map(|_| n.step(100)).sum();
513 assert!(t > 0);
514 }
515 #[test]
516 fn loihi2_fires() {
517 let mut n = Loihi2Neuron {
518 tau3: 8,
519 ..Loihi2Neuron::new()
520 };
521 let t: i32 = (0..500).map(|_| n.step(200)).sum();
522 assert!(t > 0);
523 }
524 #[test]
525 fn truenorth_fires() {
526 let mut n = TrueNorthNeuron::default();
527 let t: i32 = (0..10).map(|_| n.step(50)).sum();
528 assert!(t > 0);
529 }
530 #[test]
531 fn brainscales_fires() {
532 let mut n = BrainScaleSAdExNeuron::new();
533 let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
534 assert!(t > 0);
535 }
536 #[test]
537 fn spinnaker_fires() {
538 let mut n = SpiNNakerLIFNeuron::new();
539 let t: i32 = (0..200).map(|_| n.step(30.0)).sum();
540 assert!(t > 0);
541 }
542 #[test]
543 fn spinnaker2_fires() {
544 let mut n = SpiNNaker2Neuron::new();
545 let t: i32 = (0..200).map(|_| n.step(100)).sum();
546 assert!(t > 0);
547 }
548 #[test]
549 fn dpi_fires() {
550 let mut n = DPINeuron::new();
551 let t: i32 = (0..100).map(|_| n.step(1.0)).sum();
552 assert!(t > 0);
553 }
554 #[test]
555 fn akida_fires() {
556 let mut n = AkidaNeuron::default();
557 let t: i32 = (0..10).map(|_| n.step(50.0)).sum();
558 assert!(t > 0);
559 }
560 #[test]
561 fn neurogrid_fires() {
562 let mut n = NeuroGridNeuron::new();
563 let t: i32 = (0..2000).map(|_| n.step(500.0)).sum();
564 assert!(t > 0);
565 }
566
567 #[test]
571 fn loihi_cuba_silent() {
572 let mut n = LoihiCUBANeuron::new();
573 let t: i32 = (0..200).map(|_| n.step(0)).sum();
574 assert_eq!(t, 0);
575 }
576 #[test]
577 fn loihi_cuba_reset() {
578 let mut n = LoihiCUBANeuron::new();
579 for _ in 0..50 {
580 n.step(100);
581 }
582 n.reset();
583 assert_eq!(n.v, 0);
584 assert_eq!(n.u, 0);
585 }
586 #[test]
587 fn loihi_cuba_bounded() {
588 let mut n = LoihiCUBANeuron::new();
589 for _ in 0..1000 {
590 n.step(10000);
591 }
592 }
593
594 #[test]
596 fn loihi2_silent() {
597 let mut n = Loihi2Neuron::new();
598 let t: i32 = (0..200).map(|_| n.step(0)).sum();
599 assert_eq!(t, 0);
600 }
601 #[test]
602 fn loihi2_reset() {
603 let mut n = Loihi2Neuron::new();
604 for _ in 0..50 {
605 n.step(200);
606 }
607 n.reset();
608 assert_eq!(n.s1, 0);
609 }
610 #[test]
611 fn loihi2_bounded() {
612 let mut n = Loihi2Neuron {
613 tau3: 8,
614 ..Loihi2Neuron::new()
615 };
616 for _ in 0..1000 {
617 n.step(10000);
618 }
619 }
620
621 #[test]
623 fn truenorth_silent() {
624 let mut n = TrueNorthNeuron::default();
625 let t: i32 = (0..100).map(|_| n.step(0)).sum();
626 assert_eq!(t, 0);
627 }
628 #[test]
629 fn truenorth_reset() {
630 let mut n = TrueNorthNeuron::default();
631 for _ in 0..10 {
632 n.step(50);
633 }
634 n.reset();
635 assert_eq!(n.v, 0);
636 }
637
638 #[test]
640 fn brainscales_silent() {
641 let mut n = BrainScaleSAdExNeuron::new();
642 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
643 assert_eq!(t, 0);
644 }
645 #[test]
646 fn brainscales_reset() {
647 let mut n = BrainScaleSAdExNeuron::new();
648 for _ in 0..100 {
649 n.step(500.0);
650 }
651 n.reset();
652 assert!((n.v - n.v_rest).abs() < 1e-10);
653 }
654 #[test]
655 fn brainscales_bounded() {
656 let mut n = BrainScaleSAdExNeuron::new();
657 for _ in 0..2000 {
658 n.step(1e4);
659 }
660 assert!(n.v.is_finite());
661 }
662 #[test]
663 fn brainscales_nan_no_panic() {
664 BrainScaleSAdExNeuron::new().step(f64::NAN);
665 }
666
667 #[test]
669 fn spinnaker_silent() {
670 let mut n = SpiNNakerLIFNeuron::new();
671 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
672 assert_eq!(t, 0);
673 }
674 #[test]
675 fn spinnaker_reset() {
676 let mut n = SpiNNakerLIFNeuron::new();
677 for _ in 0..50 {
678 n.step(30.0);
679 }
680 n.reset();
681 assert!((n.v - n.v_rest).abs() < 1e-10);
682 }
683 #[test]
684 fn spinnaker_bounded() {
685 let mut n = SpiNNakerLIFNeuron::new();
686 for _ in 0..1000 {
687 n.step(1e4);
688 }
689 assert!(n.v.is_finite());
690 }
691 #[test]
692 fn spinnaker_nan_no_panic() {
693 SpiNNakerLIFNeuron::new().step(f64::NAN);
694 }
695
696 #[test]
698 fn spinnaker2_silent() {
699 let mut n = SpiNNaker2Neuron::new();
700 let t: i32 = (0..200).map(|_| n.step(0)).sum();
701 assert_eq!(t, 0);
702 }
703 #[test]
704 fn spinnaker2_reset() {
705 let mut n = SpiNNaker2Neuron::new();
706 for _ in 0..50 {
707 n.step(100);
708 }
709 n.reset();
710 }
711 #[test]
712 fn spinnaker2_bounded() {
713 let mut n = SpiNNaker2Neuron::new();
714 for _ in 0..1000 {
715 n.step(10000);
716 }
717 }
718
719 #[test]
721 fn dpi_silent() {
722 let mut n = DPINeuron::new();
723 let t: i32 = (0..100).map(|_| n.step(0.0)).sum();
724 assert_eq!(t, 0);
725 }
726 #[test]
727 fn dpi_reset() {
728 let mut n = DPINeuron::new();
729 for _ in 0..50 {
730 n.step(1.0);
731 }
732 n.reset();
733 }
734 #[test]
735 fn dpi_nan_no_panic() {
736 DPINeuron::new().step(f64::NAN);
737 }
738
739 #[test]
741 fn akida_silent() {
742 let mut n = AkidaNeuron::default();
743 let t: i32 = (0..100).map(|_| n.step(0.0)).sum();
744 assert_eq!(t, 0);
745 }
746 #[test]
747 fn akida_reset() {
748 let mut n = AkidaNeuron::default();
749 for _ in 0..10 {
750 n.step(50.0);
751 }
752 n.reset();
753 }
754 #[test]
755 fn akida_nan_no_panic() {
756 AkidaNeuron::default().step(f64::NAN);
757 }
758
759 #[test]
761 fn neurogrid_silent() {
762 let mut n = NeuroGridNeuron::new();
763 let t: i32 = (0..200).map(|_| n.step(0.0)).sum();
764 assert_eq!(t, 0);
765 }
766 #[test]
767 fn neurogrid_reset() {
768 let mut n = NeuroGridNeuron::new();
769 for _ in 0..100 {
770 n.step(500.0);
771 }
772 n.reset();
773 assert!((n.v_s - (-65.0)).abs() < 1e-10);
774 }
775 #[test]
776 fn neurogrid_bounded() {
777 let mut n = NeuroGridNeuron::new();
778 for _ in 0..2000 {
779 n.step(1e4);
780 }
781 assert!(n.v_s.is_finite());
782 }
783 #[test]
784 fn neurogrid_nan_no_panic() {
785 NeuroGridNeuron::new().step(f64::NAN);
786 }
787 #[test]
788 fn neurogrid_rk4_anchor() {
789 let mut n = NeuroGridNeuron::new();
790 let spikes: i32 = (0..20_000).map(|_| n.step(100.0)).sum();
791 assert_eq!(spikes, 94);
792 assert!(n.v_s.is_finite());
793 assert!(n.v_d.is_finite());
794 }
795 #[test]
796 fn neurogrid_invalid_input_preserves_state() {
797 let mut n = NeuroGridNeuron::new();
798 for _ in 0..10 {
799 n.step(100.0);
800 }
801 let old = (n.v_s, n.v_d);
802 assert_eq!(n.step(f64::INFINITY), 0);
803 assert_eq!((n.v_s, n.v_d), old);
804 }
805}