1use core::cmp::Ordering;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct SpikeEvent {
47 neuron_id: usize,
49 timestamp_us: u64,
51}
52
53impl SpikeEvent {
54 pub fn new(neuron_id: usize, timestamp_us: u64) -> Self {
56 Self {
57 neuron_id,
58 timestamp_us,
59 }
60 }
61
62 pub fn neuron_id(&self) -> usize {
64 self.neuron_id
65 }
66
67 pub fn timestamp_us(&self) -> u64 {
69 self.timestamp_us
70 }
71
72 pub fn timestamp_ms(&self) -> f64 {
74 self.timestamp_us as f64 / 1000.0
75 }
76
77 pub fn timestamp_s(&self) -> f64 {
79 self.timestamp_us as f64 / 1_000_000.0
80 }
81}
82
83impl PartialOrd for SpikeEvent {
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
85 Some(self.cmp(other))
86 }
87}
88
89impl Ord for SpikeEvent {
90 fn cmp(&self, other: &Self) -> Ordering {
91 self.timestamp_us.cmp(&other.timestamp_us)
92 }
93}
94
95#[derive(Debug, Clone)]
99pub struct SpikeTrain {
100 neuron_id: usize,
101 spikes: Vec<u64>, }
103
104impl SpikeTrain {
105 pub fn new(neuron_id: usize) -> Self {
107 Self {
108 neuron_id,
109 spikes: Vec::new(),
110 }
111 }
112
113 pub fn add_spike(&mut self, timestamp_us: u64) {
115 self.spikes.push(timestamp_us);
116 }
117
118 pub fn neuron_id(&self) -> usize {
120 self.neuron_id
121 }
122
123 pub fn spike_count(&self) -> usize {
125 self.spikes.len()
126 }
127
128 pub fn spikes(&self) -> &[u64] {
130 &self.spikes
131 }
132
133 pub fn firing_rate(&self, duration_us: u64) -> f64 {
135 if duration_us == 0 {
136 return 0.0;
137 }
138 let duration_s = duration_us as f64 / 1_000_000.0;
139 self.spikes.len() as f64 / duration_s
140 }
141
142 pub fn inter_spike_intervals(&self) -> Vec<u64> {
144 if self.spikes.len() < 2 {
145 return Vec::new();
146 }
147 self.spikes
148 .windows(2)
149 .map(|w| w[1].saturating_sub(w[0]))
150 .collect()
151 }
152}
153
154#[derive(Debug, Clone)]
158pub struct LIFNeuron {
159 id: usize,
160 tau: f64,
162 resistance: f64,
164 v_rest: f64,
166 v_thresh: f64,
168 v_mem: f64,
170 refrac_period_us: u64,
172 last_spike_us: Option<u64>,
174}
175
176impl LIFNeuron {
177 pub fn new(id: usize, tau: f64, resistance: f64, v_rest: f64, v_thresh: f64) -> Self {
179 Self {
180 id,
181 tau,
182 resistance,
183 v_rest,
184 v_thresh,
185 v_mem: v_rest,
186 refrac_period_us: 2000, last_spike_us: None,
188 }
189 }
190
191 pub fn id(&self) -> usize {
193 self.id
194 }
195
196 pub fn membrane_potential(&self) -> f64 {
198 self.v_mem
199 }
200
201 pub fn update(&mut self, current: f64, dt_us: u64, current_time_us: u64) -> Option<u64> {
205 if let Some(last_spike) = self.last_spike_us {
207 if current_time_us < last_spike + self.refrac_period_us {
208 return None;
209 }
210 }
211
212 let dt = dt_us as f64 / 1_000_000.0; let dv = ((-(self.v_mem - self.v_rest) + self.resistance * current) / self.tau) * dt;
216 self.v_mem += dv;
217
218 if self.v_mem >= self.v_thresh {
220 self.v_mem = self.v_rest; self.last_spike_us = Some(current_time_us);
222 Some(current_time_us)
223 } else {
224 None
225 }
226 }
227
228 pub fn reset(&mut self) {
230 self.v_mem = self.v_rest;
231 self.last_spike_us = None;
232 }
233
234 pub fn set_refractory_period(&mut self, period_us: u64) {
236 self.refrac_period_us = period_us;
237 }
238}
239
240#[derive(Debug, Clone)]
245pub struct IzhikevichNeuron {
246 id: usize,
247 a: f64,
249 b: f64,
251 c: f64,
253 d: f64,
255 v: f64,
257 u: f64,
259}
260
261impl IzhikevichNeuron {
262 pub fn new(id: usize, a: f64, b: f64, c: f64, d: f64) -> Self {
264 Self {
265 id,
266 a,
267 b,
268 c,
269 d,
270 v: c, u: b * c,
272 }
273 }
274
275 pub fn regular_spiking(id: usize) -> Self {
277 Self::new(id, 0.02, 0.2, -65.0, 8.0)
278 }
279
280 pub fn fast_spiking(id: usize) -> Self {
282 Self::new(id, 0.1, 0.2, -65.0, 2.0)
283 }
284
285 pub fn bursting(id: usize) -> Self {
287 Self::new(id, 0.02, 0.2, -50.0, 2.0)
288 }
289
290 pub fn id(&self) -> usize {
292 self.id
293 }
294
295 pub fn membrane_potential(&self) -> f64 {
297 self.v
298 }
299
300 pub fn update(&mut self, current: f64, dt_us: u64, current_time_us: u64) -> Option<u64> {
304 let dt = dt_us as f64 / 1000.0; let v_squared = self.v * self.v;
311 let dv = (0.04 * v_squared + 5.0 * self.v + 140.0 - self.u + current) * dt;
312 let du = (self.a * (self.b * self.v - self.u)) * dt;
313
314 self.v += dv;
315 self.u += du;
316
317 if self.v >= 30.0 {
319 self.v = self.c;
321 self.u += self.d;
322 Some(current_time_us)
323 } else {
324 None
325 }
326 }
327
328 pub fn reset(&mut self) {
330 self.v = self.c;
331 self.u = self.b * self.c;
332 }
333}
334
335#[derive(Debug, Clone)]
340pub struct STDPSynapse {
341 pre_id: usize,
343 post_id: usize,
345 weight: f64,
347 a_plus: f64,
349 a_minus: f64,
351 tau_plus: f64,
353 tau_minus: f64,
355 last_pre_spike_us: Option<u64>,
357 last_post_spike_us: Option<u64>,
359}
360
361impl STDPSynapse {
362 pub fn new(
364 pre_id: usize,
365 post_id: usize,
366 initial_weight: f64,
367 a_plus: f64,
368 a_minus: f64,
369 ) -> Self {
370 Self {
371 pre_id,
372 post_id,
373 weight: initial_weight,
374 a_plus,
375 a_minus,
376 tau_plus: 20.0, tau_minus: 20.0, last_pre_spike_us: None,
379 last_post_spike_us: None,
380 }
381 }
382
383 pub fn weight(&self) -> f64 {
385 self.weight
386 }
387
388 pub fn pre_id(&self) -> usize {
390 self.pre_id
391 }
392
393 pub fn post_id(&self) -> usize {
395 self.post_id
396 }
397
398 pub fn on_pre_spike(&mut self, spike_time_us: u64) {
400 self.last_pre_spike_us = Some(spike_time_us);
401
402 if let Some(last_post) = self.last_post_spike_us {
404 if last_post < spike_time_us {
405 let dt = ((spike_time_us - last_post) as f64) / 1000.0; let delta_w = -self.a_minus * (-dt / self.tau_minus).exp();
407 self.weight += delta_w;
408 self.weight = self.weight.max(0.0); }
410 }
411 }
412
413 pub fn on_post_spike(&mut self, spike_time_us: u64) {
415 self.last_post_spike_us = Some(spike_time_us);
416
417 if let Some(last_pre) = self.last_pre_spike_us {
419 if last_pre < spike_time_us {
420 let dt = ((spike_time_us - last_pre) as f64) / 1000.0; let delta_w = self.a_plus * (-dt / self.tau_plus).exp();
422 self.weight += delta_w;
423 }
424 }
425 }
426
427 pub fn set_time_constants(&mut self, tau_plus: f64, tau_minus: f64) {
429 self.tau_plus = tau_plus;
430 self.tau_minus = tau_minus;
431 }
432
433 pub fn reset(&mut self) {
435 self.last_pre_spike_us = None;
436 self.last_post_spike_us = None;
437 }
438}
439
440#[derive(Debug, Clone)]
444pub struct NeuromorphicCore {
445 id: usize,
447 max_neurons: usize,
449 max_synapses: usize,
451 neuron_count: usize,
453 synapse_count: usize,
455}
456
457impl NeuromorphicCore {
458 pub fn new(id: usize, max_neurons: usize, max_synapses: usize) -> Self {
460 Self {
461 id,
462 max_neurons,
463 max_synapses,
464 neuron_count: 0,
465 synapse_count: 0,
466 }
467 }
468
469 pub fn loihi_core(id: usize) -> Self {
471 Self::new(id, 128, 128 * 1024)
472 }
473
474 pub fn truenorth_core(id: usize) -> Self {
476 Self::new(id, 256, 64 * 1024)
477 }
478
479 pub fn id(&self) -> usize {
481 self.id
482 }
483
484 pub fn can_add_neuron(&self) -> bool {
486 self.neuron_count < self.max_neurons
487 }
488
489 pub fn can_add_synapse(&self) -> bool {
491 self.synapse_count < self.max_synapses
492 }
493
494 pub fn add_neuron(&mut self) -> Result<usize, &'static str> {
496 if !self.can_add_neuron() {
497 return Err("Core neuron capacity exceeded");
498 }
499 let neuron_id = self.neuron_count;
500 self.neuron_count += 1;
501 Ok(neuron_id)
502 }
503
504 pub fn add_synapse(&mut self) -> Result<usize, &'static str> {
506 if !self.can_add_synapse() {
507 return Err("Core synapse capacity exceeded");
508 }
509 let synapse_id = self.synapse_count;
510 self.synapse_count += 1;
511 Ok(synapse_id)
512 }
513
514 pub fn utilization(&self) -> CoreUtilization {
516 CoreUtilization {
517 neuron_util: self.neuron_count as f64 / self.max_neurons as f64,
518 synapse_util: self.synapse_count as f64 / self.max_synapses as f64,
519 }
520 }
521}
522
523#[derive(Debug, Clone, Copy)]
525pub struct CoreUtilization {
526 pub neuron_util: f64,
528 pub synapse_util: f64,
530}
531
532#[derive(Debug, Clone)]
536pub struct EventDrivenSimulation {
537 current_time_us: u64,
539 event_queue: Vec<SpikeEvent>,
541}
542
543impl EventDrivenSimulation {
544 pub fn new() -> Self {
546 Self {
547 current_time_us: 0,
548 event_queue: Vec::new(),
549 }
550 }
551
552 pub fn current_time_us(&self) -> u64 {
554 self.current_time_us
555 }
556
557 pub fn schedule_event(&mut self, event: SpikeEvent) {
559 self.event_queue.push(event);
560 self.event_queue.sort(); }
562
563 pub fn next_event(&mut self) -> Option<SpikeEvent> {
565 if self.event_queue.is_empty() {
566 return None;
567 }
568 let event = self.event_queue.remove(0);
569 self.current_time_us = event.timestamp_us();
570 Some(event)
571 }
572
573 pub fn has_events(&self) -> bool {
575 !self.event_queue.is_empty()
576 }
577
578 pub fn event_count(&self) -> usize {
580 self.event_queue.len()
581 }
582
583 pub fn advance_to(&mut self, time_us: u64) {
585 self.current_time_us = time_us;
586 }
587
588 pub fn reset(&mut self) {
590 self.current_time_us = 0;
591 self.event_queue.clear();
592 }
593}
594
595impl Default for EventDrivenSimulation {
596 fn default() -> Self {
597 Self::new()
598 }
599}
600
601#[derive(Debug, Clone, Copy)]
603pub enum SpikeEncoding {
604 Rate,
606 Temporal,
608 Population,
610 Phase,
612}
613
614#[derive(Debug, Clone)]
616pub struct RateEncoder {
617 max_rate: f64,
619 window_us: u64,
621}
622
623impl RateEncoder {
624 pub fn new(max_rate: f64, window_us: u64) -> Self {
626 Self {
627 max_rate,
628 window_us,
629 }
630 }
631
632 pub fn encode(&self, value: f64) -> usize {
634 let rate = value.max(0.0).min(1.0) * self.max_rate;
635 let window_s = self.window_us as f64 / 1_000_000.0;
636 (rate * window_s).round() as usize
637 }
638
639 pub fn max_rate(&self) -> f64 {
641 self.max_rate
642 }
643
644 pub fn window_us(&self) -> u64 {
646 self.window_us
647 }
648}
649
650#[derive(Debug, Clone)]
652pub struct RateDecoder {
653 window_us: u64,
655}
656
657impl RateDecoder {
658 pub fn new(window_us: u64) -> Self {
660 Self { window_us }
661 }
662
663 pub fn decode(&self, spike_train: &SpikeTrain) -> f64 {
665 spike_train.firing_rate(self.window_us)
666 }
667
668 pub fn window_us(&self) -> u64 {
670 self.window_us
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677
678 #[test]
679 fn test_spike_event_creation() {
680 let event = SpikeEvent::new(0, 1000);
681 assert_eq!(event.neuron_id(), 0);
682 assert_eq!(event.timestamp_us(), 1000);
683 assert_eq!(event.timestamp_ms(), 1.0);
684 assert_eq!(event.timestamp_s(), 0.001);
685 }
686
687 #[test]
688 fn test_spike_event_ordering() {
689 let event1 = SpikeEvent::new(0, 1000);
690 let event2 = SpikeEvent::new(1, 2000);
691 assert!(event1 < event2);
692 }
693
694 #[test]
695 fn test_spike_train() {
696 let mut train = SpikeTrain::new(0);
697 train.add_spike(1000);
698 train.add_spike(2000);
699 train.add_spike(3000);
700
701 assert_eq!(train.spike_count(), 3);
702 assert_eq!(train.neuron_id(), 0);
703
704 let rate = train.firing_rate(10_000); assert!((rate - 300.0).abs() < 1.0); let isis = train.inter_spike_intervals();
708 assert_eq!(isis.len(), 2);
709 assert_eq!(isis[0], 1000);
710 assert_eq!(isis[1], 1000);
711 }
712
713 #[test]
714 fn test_lif_neuron() {
715 let mut neuron = LIFNeuron::new(0, 0.02, 0.2, -65.0, -50.0);
716 assert_eq!(neuron.id(), 0);
717 assert_eq!(neuron.membrane_potential(), -65.0);
718
719 let spike = neuron.update(100.0, 1000, 1000);
721 assert!(spike.is_some() || spike.is_none()); neuron.reset();
724 assert_eq!(neuron.membrane_potential(), -65.0);
725 }
726
727 #[test]
728 fn test_izhikevich_neuron() {
729 let mut neuron = IzhikevichNeuron::regular_spiking(0);
730 assert_eq!(neuron.id(), 0);
731
732 let spike = neuron.update(10.0, 1000, 1000);
733 assert!(spike.is_some() || spike.is_none());
734
735 neuron.reset();
736 }
737
738 #[test]
739 fn test_izhikevich_neuron_types() {
740 let _rs = IzhikevichNeuron::regular_spiking(0);
741 let _fs = IzhikevichNeuron::fast_spiking(1);
742 let _burst = IzhikevichNeuron::bursting(2);
743 }
744
745 #[test]
746 fn test_stdp_synapse() {
747 let mut synapse = STDPSynapse::new(0, 1, 0.5, 0.02, 0.02);
748 assert_eq!(synapse.pre_id(), 0);
749 assert_eq!(synapse.post_id(), 1);
750 assert_eq!(synapse.weight(), 0.5);
751
752 synapse.on_pre_spike(1000);
753 synapse.on_post_spike(2000); assert!(synapse.weight() > 0.5); synapse.reset();
758 }
759
760 #[test]
761 fn test_neuromorphic_core() {
762 let mut core = NeuromorphicCore::loihi_core(0);
763 assert_eq!(core.id(), 0);
764 assert!(core.can_add_neuron());
765 assert!(core.can_add_synapse());
766
767 let neuron_id = core.add_neuron().expect("add_neuron should succeed");
768 assert_eq!(neuron_id, 0);
769
770 let synapse_id = core.add_synapse().expect("add_synapse should succeed");
771 assert_eq!(synapse_id, 0);
772
773 let util = core.utilization();
774 assert!(util.neuron_util > 0.0);
775 assert!(util.synapse_util > 0.0);
776 }
777
778 #[test]
779 fn test_truenorth_core() {
780 let core = NeuromorphicCore::truenorth_core(0);
781 assert_eq!(core.id(), 0);
782 }
783
784 #[test]
785 fn test_event_driven_simulation() {
786 let mut sim = EventDrivenSimulation::new();
787 assert_eq!(sim.current_time_us(), 0);
788 assert!(!sim.has_events());
789
790 sim.schedule_event(SpikeEvent::new(0, 1000));
791 sim.schedule_event(SpikeEvent::new(1, 500));
792
793 assert!(sim.has_events());
794 assert_eq!(sim.event_count(), 2);
795
796 let event1 = sim.next_event().expect("next_event should return event");
797 assert_eq!(event1.timestamp_us(), 500); let event2 = sim.next_event().expect("next_event should return event");
800 assert_eq!(event2.timestamp_us(), 1000);
801
802 assert!(!sim.has_events());
803
804 sim.reset();
805 assert_eq!(sim.current_time_us(), 0);
806 }
807
808 #[test]
809 fn test_rate_encoder() {
810 let encoder = RateEncoder::new(100.0, 10_000); let spike_count = encoder.encode(0.5); assert!(spike_count > 0);
813 assert_eq!(encoder.max_rate(), 100.0);
814 assert_eq!(encoder.window_us(), 10_000);
815 }
816
817 #[test]
818 fn test_rate_decoder() {
819 let decoder = RateDecoder::new(10_000);
820 let mut train = SpikeTrain::new(0);
821 train.add_spike(1000);
822 train.add_spike(2000);
823 train.add_spike(3000);
824
825 let rate = decoder.decode(&train);
826 assert!(rate > 0.0);
827 assert_eq!(decoder.window_us(), 10_000);
828 }
829
830 #[test]
831 fn test_spike_encoding_variants() {
832 let _rate = SpikeEncoding::Rate;
833 let _temporal = SpikeEncoding::Temporal;
834 let _population = SpikeEncoding::Population;
835 let _phase = SpikeEncoding::Phase;
836 }
837
838 #[test]
839 fn test_default_simulation() {
840 let sim = EventDrivenSimulation::default();
841 assert_eq!(sim.current_time_us(), 0);
842 }
843
844 #[test]
845 fn test_simulation_advance() {
846 let mut sim = EventDrivenSimulation::new();
847 sim.advance_to(5000);
848 assert_eq!(sim.current_time_us(), 5000);
849 }
850
851 #[test]
852 fn test_lif_refractory_period() {
853 let mut neuron = LIFNeuron::new(0, 0.02, 0.2, -65.0, -50.0);
854 neuron.set_refractory_period(3000); }
856
857 #[test]
858 fn test_stdp_time_constants() {
859 let mut synapse = STDPSynapse::new(0, 1, 0.5, 0.02, 0.02);
860 synapse.set_time_constants(15.0, 25.0);
861 }
862
863 #[test]
864 fn test_core_capacity() {
865 let mut core = NeuromorphicCore::new(0, 2, 2);
866
867 core.add_neuron().expect("first add_neuron should succeed");
868 core.add_neuron().expect("second add_neuron should succeed");
869 assert!(core.add_neuron().is_err()); core.add_synapse()
872 .expect("first add_synapse should succeed");
873 core.add_synapse()
874 .expect("second add_synapse should succeed");
875 assert!(core.add_synapse().is_err()); }
877}