1use rand::{RngExt, SeedableRng};
11use rand_xoshiro::Xoshiro256PlusPlus;
12
13#[derive(Clone, Debug)]
16pub struct QuadraticIFNeuron {
17 pub v: f64,
18 pub v_reset: f64,
19 pub v_peak: f64,
20 pub dt: f64,
21}
22
23impl QuadraticIFNeuron {
24 pub fn new(v_reset: f64, v_peak: f64, dt: f64) -> Self {
25 Self {
26 v: v_reset,
27 v_reset,
28 v_peak,
29 dt,
30 }
31 }
32
33 pub fn step(&mut self, current: f64) -> i32 {
34 self.v += (self.v * self.v + current) * self.dt;
35 if self.v >= self.v_peak {
36 self.v = self.v_reset;
37 1
38 } else {
39 0
40 }
41 }
42
43 pub fn reset(&mut self) {
44 self.v = self.v_reset;
45 }
46}
47
48impl Default for QuadraticIFNeuron {
49 fn default() -> Self {
50 Self::new(-1.0, 1.0, 0.01)
51 }
52}
53
54#[derive(Clone, Debug)]
57pub struct ThetaNeuron {
58 pub theta: f64,
59 pub dt: f64,
60}
61
62impl ThetaNeuron {
63 pub fn new(dt: f64) -> Self {
64 Self { theta: 0.0, dt }
65 }
66
67 pub fn step(&mut self, current: f64) -> i32 {
68 let prev = self.theta;
69 self.theta += ((1.0 - self.theta.cos()) + (1.0 + self.theta.cos()) * current) * self.dt;
70 if self.theta >= std::f64::consts::PI && prev < std::f64::consts::PI {
71 self.theta -= 2.0 * std::f64::consts::PI;
72 1
73 } else {
74 0
75 }
76 }
77
78 pub fn reset(&mut self) {
79 self.theta = 0.0;
80 }
81}
82
83impl Default for ThetaNeuron {
84 fn default() -> Self {
85 Self::new(0.01)
86 }
87}
88
89#[derive(Clone, Debug)]
91pub struct PerfectIntegratorNeuron {
92 pub v: f64,
93 pub c_m: f64,
94 pub v_threshold: f64,
95 pub v_reset: f64,
96 pub dt: f64,
97}
98
99impl PerfectIntegratorNeuron {
100 pub fn new(c_m: f64, v_threshold: f64, dt: f64) -> Self {
101 Self {
102 v: 0.0,
103 c_m,
104 v_threshold,
105 v_reset: 0.0,
106 dt,
107 }
108 }
109
110 pub fn step(&mut self, current: f64) -> i32 {
111 self.v += (current / self.c_m) * self.dt;
112 if self.v >= self.v_threshold {
113 self.v = self.v_reset;
114 1
115 } else {
116 0
117 }
118 }
119
120 pub fn reset(&mut self) {
121 self.v = self.v_reset;
122 }
123}
124
125impl Default for PerfectIntegratorNeuron {
126 fn default() -> Self {
127 Self::new(1.0, 1.0, 0.1)
128 }
129}
130
131#[derive(Clone, Debug)]
133pub struct GatedLIFNeuron {
134 pub v: f64,
135 pub gate_v: f64,
136 pub gate_i: f64,
137 pub v_threshold: f64,
138 pub dt: f64,
139}
140
141impl GatedLIFNeuron {
142 pub fn new(gate_v: f64, gate_i: f64, v_threshold: f64) -> Self {
143 Self {
144 v: 0.0,
145 gate_v,
146 gate_i,
147 v_threshold,
148 dt: 1.0,
149 }
150 }
151
152 pub fn step(&mut self, current: f64) -> i32 {
153 self.v = self.gate_v * self.v + self.gate_i * current;
154 if self.v >= self.v_threshold {
155 self.v -= self.v_threshold;
156 1
157 } else {
158 0
159 }
160 }
161
162 pub fn reset(&mut self) {
163 self.v = 0.0;
164 }
165}
166
167impl Default for GatedLIFNeuron {
168 fn default() -> Self {
169 Self::new(0.9, 1.0, 1.0)
170 }
171}
172
173#[derive(Clone, Debug)]
175pub struct NonlinearLIFNeuron {
176 pub v: f64,
177 pub w: f64,
178 pub v_rest: f64,
179 pub v_crit: f64,
180 pub v_threshold: f64,
181 pub v_reset: f64,
182 pub a: f64,
183 pub b: f64,
184 pub tau_w: f64,
185 pub c_m: f64,
186 pub dt: f64,
187}
188
189impl NonlinearLIFNeuron {
190 pub fn new() -> Self {
191 Self {
192 v: -65.0,
193 w: 0.0,
194 v_rest: -65.0,
195 v_crit: -40.0,
196 v_threshold: -20.0,
197 v_reset: -65.0,
198 a: 0.04,
199 b: 0.5,
200 tau_w: 100.0,
201 c_m: 1.0,
202 dt: 0.1,
203 }
204 }
205
206 pub fn step(&mut self, current: f64) -> i32 {
207 let v_prev = self.v;
208 let cubic = self.a * (self.v - self.v_rest) * (self.v - self.v_crit);
209 self.v += (cubic - self.w + current) / self.c_m * self.dt;
210 self.w += (self.b * (self.v - self.v_rest) - self.w) / self.tau_w * self.dt;
211 if self.v >= self.v_threshold && v_prev < self.v_threshold {
212 self.v = self.v_reset;
213 1
214 } else {
215 0
216 }
217 }
218
219 pub fn reset(&mut self) {
220 self.v = self.v_rest;
221 self.w = 0.0;
222 }
223}
224
225impl Default for NonlinearLIFNeuron {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231#[derive(Clone, Debug)]
233pub struct SFANeuron {
234 pub v: f64,
235 pub g_sfa: f64,
236 pub v_rest: f64,
237 pub v_reset: f64,
238 pub v_threshold: f64,
239 pub tau_m: f64,
240 pub tau_sfa: f64,
241 pub delta_g: f64,
242 pub e_k: f64,
243 pub resistance: f64,
244 pub dt: f64,
245}
246
247impl SFANeuron {
248 pub fn new() -> Self {
249 Self {
250 v: -70.0,
251 g_sfa: 0.0,
252 v_rest: -70.0,
253 v_reset: -70.0,
254 v_threshold: -50.0,
255 tau_m: 10.0,
256 tau_sfa: 200.0,
257 delta_g: 0.5,
258 e_k: -80.0,
259 resistance: 1.0,
260 dt: 1.0,
261 }
262 }
263
264 pub fn step(&mut self, current: f64) -> i32 {
265 self.v += (-(self.v - self.v_rest) - self.g_sfa * (self.v - self.e_k)
266 + self.resistance * current)
267 / self.tau_m
268 * self.dt;
269 self.g_sfa *= (-self.dt / self.tau_sfa).exp();
270 if self.v >= self.v_threshold {
271 self.v = self.v_reset;
272 self.g_sfa += self.delta_g;
273 1
274 } else {
275 0
276 }
277 }
278
279 pub fn reset(&mut self) {
280 self.v = self.v_rest;
281 self.g_sfa = 0.0;
282 }
283}
284
285impl Default for SFANeuron {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291#[derive(Clone, Debug)]
293pub struct MATNeuron {
294 pub v: f64,
295 pub theta1: f64,
296 pub theta2: f64,
297 pub v_rest: f64,
298 pub v_reset: f64,
299 pub v_threshold_base: f64,
300 pub tau_m: f64,
301 pub tau_1: f64,
302 pub tau_2: f64,
303 pub h1: f64,
304 pub h2: f64,
305 pub resistance: f64,
306 pub dt: f64,
307}
308
309impl MATNeuron {
310 pub fn new() -> Self {
311 Self {
312 v: -70.0,
313 theta1: 0.0,
314 theta2: 0.0,
315 v_rest: -70.0,
316 v_reset: -70.0,
317 v_threshold_base: -50.0,
318 tau_m: 10.0,
319 tau_1: 10.0,
320 tau_2: 200.0,
321 h1: 5.0,
322 h2: 3.0,
323 resistance: 1.0,
324 dt: 1.0,
325 }
326 }
327
328 pub fn step(&mut self, current: f64) -> i32 {
329 self.v += (-(self.v - self.v_rest) + self.resistance * current) / self.tau_m * self.dt;
330 self.theta1 *= (-self.dt / self.tau_1).exp();
331 self.theta2 *= (-self.dt / self.tau_2).exp();
332 let threshold = self.v_threshold_base + self.theta1 + self.theta2;
333 if self.v >= threshold {
334 self.v = self.v_reset;
335 self.theta1 += self.h1;
336 self.theta2 += self.h2;
337 1
338 } else {
339 0
340 }
341 }
342
343 pub fn reset(&mut self) {
344 self.v = self.v_rest;
345 self.theta1 = 0.0;
346 self.theta2 = 0.0;
347 }
348}
349
350impl Default for MATNeuron {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356#[derive(Clone, Debug)]
358pub struct EscapeRateNeuron {
359 pub v: f64,
360 pub v_rest: f64,
361 pub v_reset: f64,
362 pub v_threshold: f64,
363 pub tau_m: f64,
364 pub rho_0: f64,
365 pub delta_u: f64,
366 pub resistance: f64,
367 pub dt: f64,
368 rng: Xoshiro256PlusPlus,
369}
370
371impl EscapeRateNeuron {
372 pub fn new(seed: u64) -> Self {
373 Self {
374 v: -70.0,
375 v_rest: -70.0,
376 v_reset: -70.0,
377 v_threshold: -50.0,
378 tau_m: 10.0,
379 rho_0: 0.001,
380 delta_u: 3.0,
381 resistance: 1.0,
382 dt: 1.0,
383 rng: Xoshiro256PlusPlus::seed_from_u64(seed),
384 }
385 }
386
387 pub fn step(&mut self, current: f64) -> i32 {
388 self.v += (-(self.v - self.v_rest) + self.resistance * current) / self.tau_m * self.dt;
389 let rate = self.rho_0 * ((self.v - self.v_threshold) / self.delta_u).exp();
390 if self.rng.random::<f64>() < rate * self.dt {
391 self.v = self.v_reset;
392 1
393 } else {
394 0
395 }
396 }
397
398 pub fn reset(&mut self) {
399 self.v = self.v_rest;
400 }
401}
402
403#[derive(Clone, Debug)]
405pub struct KLIFNeuron {
406 pub v: f64,
407 pub k: f64,
408 pub alpha: f64,
409 pub v_threshold: f64,
410 pub v_reset: f64,
411}
412
413impl KLIFNeuron {
414 pub fn new(tau: f64, k: f64, dt: f64) -> Self {
415 Self {
416 v: 0.0,
417 k,
418 alpha: (-dt / tau).exp(),
419 v_threshold: 1.0,
420 v_reset: 0.0,
421 }
422 }
423
424 pub fn step(&mut self, current: f64) -> i32 {
425 self.v = self.alpha * self.v + self.k * current;
426 if self.v >= self.v_threshold {
427 self.v = self.v_reset;
428 1
429 } else {
430 0
431 }
432 }
433
434 pub fn reset(&mut self) {
435 self.v = 0.0;
436 }
437}
438
439impl Default for KLIFNeuron {
440 fn default() -> Self {
441 Self::new(10.0, 1.0, 1.0)
442 }
443}
444
445#[derive(Clone, Debug)]
447pub struct InhibitoryLIFNeuron {
448 pub v: f64,
449 pub inh_trace: f64,
450 pub alpha_m: f64,
451 pub alpha_inh: f64,
452 pub v_threshold: f64,
453 pub inh_strength: f64,
454}
455
456impl InhibitoryLIFNeuron {
457 pub fn new(tau_m: f64, tau_inh: f64, dt: f64) -> Self {
458 Self {
459 v: 0.0,
460 inh_trace: 0.0,
461 alpha_m: (-dt / tau_m).exp(),
462 alpha_inh: (-dt / tau_inh).exp(),
463 v_threshold: 1.0,
464 inh_strength: 0.5,
465 }
466 }
467
468 pub fn step(&mut self, current: f64) -> i32 {
469 self.inh_trace *= self.alpha_inh;
470 self.v = self.alpha_m * self.v + current - self.inh_strength * self.inh_trace;
471 if self.v >= self.v_threshold {
472 self.v = 0.0;
473 self.inh_trace += 1.0;
474 1
475 } else {
476 0
477 }
478 }
479
480 pub fn reset(&mut self) {
481 self.v = 0.0;
482 self.inh_trace = 0.0;
483 }
484}
485
486impl Default for InhibitoryLIFNeuron {
487 fn default() -> Self {
488 Self::new(10.0, 5.0, 1.0)
489 }
490}
491
492#[derive(Clone, Debug)]
494pub struct ComplementaryLIFNeuron {
495 pub v_pos: f64,
496 pub v_neg: f64,
497 pub alpha: f64,
498 pub v_threshold: f64,
499}
500
501impl ComplementaryLIFNeuron {
502 pub fn new(tau: f64, dt: f64, v_threshold: f64) -> Self {
503 Self {
504 v_pos: 0.0,
505 v_neg: 0.0,
506 alpha: (-dt / tau).exp(),
507 v_threshold,
508 }
509 }
510
511 pub fn step(&mut self, current: f64) -> i32 {
512 let inp_pos = current.max(0.0);
513 let inp_neg = (-current).max(0.0);
514 self.v_pos = self.alpha * self.v_pos + inp_pos;
515 self.v_neg = self.alpha * self.v_neg + inp_neg;
516 let diff = self.v_pos - self.v_neg;
517 if diff >= self.v_threshold {
518 self.v_pos = 0.0;
519 self.v_neg = 0.0;
520 1
521 } else if diff <= -self.v_threshold {
522 self.v_pos = 0.0;
523 self.v_neg = 0.0;
524 -1
525 } else {
526 0
527 }
528 }
529
530 pub fn reset(&mut self) {
531 self.v_pos = 0.0;
532 self.v_neg = 0.0;
533 }
534}
535
536impl Default for ComplementaryLIFNeuron {
537 fn default() -> Self {
538 Self::new(10.0, 1.0, 1.0)
539 }
540}
541
542#[derive(Clone, Debug)]
544pub struct ParametricLIFNeuron {
545 pub v: f64,
546 pub a: f64,
547 pub threshold: f64,
548}
549
550impl ParametricLIFNeuron {
551 pub fn new(a: f64, threshold: f64) -> Self {
552 Self {
553 v: 0.0,
554 a,
555 threshold,
556 }
557 }
558
559 pub fn step(&mut self, current: f64) -> i32 {
560 let alpha = 1.0 / (1.0 + (-self.a).exp());
561 let spike = if self.v >= self.threshold { 1 } else { 0 };
562 self.v = alpha * self.v * (1.0 - spike as f64) + current;
563 spike
564 }
565
566 pub fn reset(&mut self) {
567 self.v = 0.0;
568 }
569}
570
571impl Default for ParametricLIFNeuron {
572 fn default() -> Self {
573 Self::new(0.0, 1.0)
574 }
575}
576
577#[derive(Clone, Debug)]
579pub struct NonResettingLIFNeuron {
580 pub v: f64,
581 pub theta: f64,
582 pub v_rest: f64,
583 pub theta_rest: f64,
584 pub delta_theta: f64,
585 pub tau_m: f64,
586 pub tau_theta: f64,
587 pub r_m: f64,
588 pub dt: f64,
589}
590
591impl NonResettingLIFNeuron {
592 pub fn new() -> Self {
593 Self {
594 v: -65.0,
595 theta: -50.0,
596 v_rest: -65.0,
597 theta_rest: -50.0,
598 delta_theta: 5.0,
599 tau_m: 10.0,
600 tau_theta: 50.0,
601 r_m: 1.0,
602 dt: 0.1,
603 }
604 }
605
606 pub fn step(&mut self, current: f64) -> i32 {
607 self.v += (-(self.v - self.v_rest) + self.r_m * current) / self.tau_m * self.dt;
608 self.theta += (-(self.theta - self.theta_rest)) / self.tau_theta * self.dt;
609 if self.v >= self.theta {
610 self.theta += self.delta_theta;
611 1
612 } else {
613 0
614 }
615 }
616
617 pub fn reset(&mut self) {
618 self.v = self.v_rest;
619 self.theta = self.theta_rest;
620 }
621}
622
623impl Default for NonResettingLIFNeuron {
624 fn default() -> Self {
625 Self::new()
626 }
627}
628
629#[derive(Clone, Debug)]
631pub struct AdaptiveThresholdIFNeuron {
632 pub v: f64,
633 pub theta: f64,
634 pub v_rest: f64,
635 pub v_reset: f64,
636 pub theta_rest: f64,
637 pub delta_theta: f64,
638 pub tau_m: f64,
639 pub tau_theta: f64,
640 pub dt: f64,
641}
642
643impl AdaptiveThresholdIFNeuron {
644 pub fn new() -> Self {
645 Self {
646 v: -65.0,
647 theta: -50.0,
648 v_rest: -65.0,
649 v_reset: -65.0,
650 theta_rest: -50.0,
651 delta_theta: 5.0,
652 tau_m: 10.0,
653 tau_theta: 50.0,
654 dt: 0.1,
655 }
656 }
657
658 pub fn step(&mut self, current: f64) -> i32 {
659 self.v += (-(self.v - self.v_rest) + current) / self.tau_m * self.dt;
660 self.theta += (-(self.theta - self.theta_rest)) / self.tau_theta * self.dt;
661 if self.v >= self.theta {
662 self.v = self.v_reset;
663 self.theta += self.delta_theta;
664 1
665 } else {
666 0
667 }
668 }
669
670 pub fn reset(&mut self) {
671 self.v = self.v_rest;
672 self.theta = self.theta_rest;
673 }
674}
675
676impl Default for AdaptiveThresholdIFNeuron {
677 fn default() -> Self {
678 Self::new()
679 }
680}
681
682#[derive(Clone, Debug)]
684pub struct SigmaDeltaNeuron {
685 pub sigma: f64,
686 pub v_threshold: f64,
687}
688
689impl SigmaDeltaNeuron {
690 pub fn new(v_threshold: f64) -> Self {
691 Self {
692 sigma: 0.0,
693 v_threshold,
694 }
695 }
696
697 pub fn step(&mut self, current: f64) -> i32 {
698 self.sigma += current;
699 if self.sigma >= self.v_threshold {
700 self.sigma -= self.v_threshold;
701 1
702 } else if self.sigma <= -self.v_threshold {
703 self.sigma += self.v_threshold;
704 -1
705 } else {
706 0
707 }
708 }
709
710 pub fn reset(&mut self) {
711 self.sigma = 0.0;
712 }
713}
714
715impl Default for SigmaDeltaNeuron {
716 fn default() -> Self {
717 Self::new(1.0)
718 }
719}
720
721#[derive(Clone, Debug)]
723pub struct EnergyLIFNeuron {
724 pub v: f64,
725 pub epsilon: f64,
726 pub v_rest: f64,
727 pub v_reset: f64,
728 pub v_threshold: f64,
729 pub tau_m: f64,
730 pub tau_e: f64,
731 pub alpha: f64,
732 pub epsilon_0: f64,
733 pub resistance: f64,
734 pub dt: f64,
735}
736
737impl EnergyLIFNeuron {
738 pub fn new() -> Self {
739 Self {
740 v: -70.0,
741 epsilon: 1.0,
742 v_rest: -70.0,
743 v_reset: -70.0,
744 v_threshold: -50.0,
745 tau_m: 10.0,
746 tau_e: 500.0,
747 alpha: 0.1,
748 epsilon_0: 1.0,
749 resistance: 1.0,
750 dt: 1.0,
751 }
752 }
753
754 pub fn step(&mut self, current: f64) -> i32 {
755 let effective_r = self.resistance * self.epsilon;
756 self.v += (-(self.v - self.v_rest) + effective_r * current) / self.tau_m * self.dt;
757 self.epsilon += (self.epsilon_0 - self.epsilon) / self.tau_e * self.dt;
758 if self.v >= self.v_threshold && self.epsilon > 0.1 {
759 self.v = self.v_reset;
760 self.epsilon -= self.alpha;
761 1
762 } else if self.v >= self.v_threshold {
763 self.v = self.v_threshold;
764 0
765 } else {
766 0
767 }
768 }
769
770 pub fn reset(&mut self) {
771 self.v = self.v_rest;
772 self.epsilon = self.epsilon_0;
773 }
774}
775
776impl Default for EnergyLIFNeuron {
777 fn default() -> Self {
778 Self::new()
779 }
780}
781
782#[derive(Clone, Debug)]
784pub struct IntegerQIFNeuron {
785 pub v: i32,
786 pub k: i32,
787 pub v_threshold: i32,
788 pub v_reset: i32,
789 pub v_min: i32,
790}
791
792impl IntegerQIFNeuron {
793 pub fn new(k: i32, v_threshold: i32) -> Self {
794 Self {
795 v: 0,
796 k,
797 v_threshold,
798 v_reset: -v_threshold,
799 v_min: -2 * v_threshold,
800 }
801 }
802
803 pub fn step(&mut self, current: i32) -> i32 {
804 self.v = (self.v + (self.v.wrapping_mul(self.v) >> self.k) + current).max(self.v_min);
805 if self.v >= self.v_threshold {
806 self.v = self.v_reset;
807 1
808 } else {
809 0
810 }
811 }
812
813 pub fn reset(&mut self) {
814 self.v = 0;
815 }
816}
817
818impl Default for IntegerQIFNeuron {
819 fn default() -> Self {
820 Self::new(6, 1024)
821 }
822}
823
824#[derive(Clone, Debug)]
826pub struct ClosedFormContinuousNeuron {
827 pub x: f64,
828 pub w_tau: f64,
829 pub w_x: f64,
830 pub w_in: f64,
831 pub tau_base: f64,
832 pub bias: f64,
833 pub v_threshold: f64,
834 pub dt: f64,
835}
836
837impl ClosedFormContinuousNeuron {
838 pub fn new() -> Self {
839 Self {
840 x: 0.0,
841 w_tau: -0.5,
842 w_x: 0.8,
843 w_in: 1.0,
844 tau_base: 10.0,
845 bias: 0.0,
846 v_threshold: 1.0,
847 dt: 1.0,
848 }
849 }
850
851 pub fn step(&mut self, current: f64) -> i32 {
852 let sigma_tau = 1.0 / (1.0 + (-(self.w_tau * current + self.bias)).exp());
853 let tau_eff = (self.tau_base * sigma_tau).max(0.1);
854 let f_target = (self.w_x * self.x + self.w_in * current).tanh();
855 let decay = (-self.dt / tau_eff).exp();
856 self.x = self.x * decay + f_target * (1.0 - decay);
857 if self.x >= self.v_threshold {
858 self.x -= self.v_threshold;
859 1
860 } else {
861 0
862 }
863 }
864
865 pub fn reset(&mut self) {
866 self.x = 0.0;
867 }
868}
869
870impl Default for ClosedFormContinuousNeuron {
871 fn default() -> Self {
872 Self::new()
873 }
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879
880 #[test]
881 fn qif_fires_with_positive_input() {
882 let mut n = QuadraticIFNeuron::default();
883 let total: i32 = (0..1000).map(|_| n.step(0.5)).sum();
884 assert!(total > 0);
885 }
886
887 #[test]
888 fn theta_fires() {
889 let mut n = ThetaNeuron::default();
890 let total: i32 = (0..1000).map(|_| n.step(0.5)).sum();
891 assert!(total > 0);
892 }
893
894 #[test]
895 fn perfect_integrator_fires() {
896 let mut n = PerfectIntegratorNeuron::default();
897 let total: i32 = (0..100).map(|_| n.step(0.5)).sum();
898 assert!(total > 0);
899 }
900
901 #[test]
902 fn gated_lif_fires() {
903 let mut n = GatedLIFNeuron::default();
904 let total: i32 = (0..20).map(|_| n.step(0.5)).sum();
905 assert!(total > 0);
906 }
907
908 #[test]
909 fn nlif_fires() {
910 let mut n = NonlinearLIFNeuron::new();
911 let total: i32 = (0..2000).map(|_| n.step(500.0)).sum();
912 assert!(total > 0);
913 }
914
915 #[test]
916 fn sfa_fires_then_adapts() {
917 let mut n = SFANeuron::new();
918 let first: i32 = (0..100).map(|_| n.step(30.0)).sum();
919 let second: i32 = (0..100).map(|_| n.step(30.0)).sum();
920 assert!(first > 0);
921 assert!(second <= first + 2);
922 }
923
924 #[test]
925 fn mat_dual_threshold_adapts() {
926 let mut n = MATNeuron::new();
927 let total: i32 = (0..200).map(|_| n.step(30.0)).sum();
928 assert!(total > 0);
929 assert!(n.theta1 > 0.0 || n.theta2 > 0.0);
930 }
931
932 #[test]
933 fn escape_rate_stochastic() {
934 let mut n = EscapeRateNeuron::new(42);
935 let total: i32 = (0..1000).map(|_| n.step(30.0)).sum();
936 assert!(total > 0);
937 }
938
939 #[test]
940 fn klif_fires() {
941 let mut n = KLIFNeuron::default();
942 let total: i32 = (0..50).map(|_| n.step(0.5)).sum();
943 assert!(total > 0);
944 }
945
946 #[test]
947 fn ilif_self_inhibits() {
948 let mut n = InhibitoryLIFNeuron::default();
949 let total: i32 = (0..100).map(|_| n.step(0.8)).sum();
950 assert!(total > 0);
951 }
952
953 #[test]
954 fn clif_positive_spike() {
955 let mut n = ComplementaryLIFNeuron::default();
956 let total: i32 = (0..20).map(|_| n.step(0.5)).sum();
957 assert!(total > 0);
958 }
959
960 #[test]
961 fn plif_fires() {
962 let mut n = ParametricLIFNeuron::default();
963 let total: i32 = (0..20).map(|_| n.step(1.5)).sum();
964 assert!(total > 0);
965 }
966
967 #[test]
968 fn non_resetting_threshold_increases() {
969 let mut n = NonResettingLIFNeuron::new();
970 let initial = n.theta;
971 for _ in 0..5000 {
972 n.step(30.0);
973 }
974 assert!(n.theta > initial);
975 }
976
977 #[test]
978 fn adaptive_threshold_fires() {
979 let mut n = AdaptiveThresholdIFNeuron::new();
980 let total: i32 = (0..500).map(|_| n.step(30.0)).sum();
981 assert!(total > 0);
982 }
983
984 #[test]
985 fn sigma_delta_encodes() {
986 let mut n = SigmaDeltaNeuron::default();
987 let total: i32 = (0..10).map(|_| n.step(0.3)).sum();
988 assert!(total > 0);
989 }
990
991 #[test]
992 fn energy_lif_fires() {
993 let mut n = EnergyLIFNeuron::new();
994 let total: i32 = (0..200).map(|_| n.step(30.0)).sum();
995 assert!(total > 0);
996 }
997
998 #[test]
999 fn iqif_fires() {
1000 let mut n = IntegerQIFNeuron::default();
1001 let total: i32 = (0..200).map(|_| n.step(50)).sum();
1002 assert!(total > 0);
1003 }
1004
1005 #[test]
1006 fn cfc_activates() {
1007 let mut n = ClosedFormContinuousNeuron {
1008 v_threshold: 0.9,
1009 ..ClosedFormContinuousNeuron::new()
1010 };
1011 let total: i32 = (0..100).map(|_| n.step(5.0)).sum();
1012 assert!(total > 0);
1013 }
1014}