1#![allow(dead_code)]
13use crate::error::Result;
17#[cfg(test)]
18use crate::streaming::FrameMetadata;
19use crate::streaming::{Frame, ProcessingStage};
20use scirs2_core::ndarray::ArrayStatCompat;
21use scirs2_core::ndarray::{Array1, Array2, ArrayView2};
22use scirs2_core::random::prelude::*;
23use statrs::statistics::Statistics;
24use std::collections::{HashMap, VecDeque};
25use std::time::{Duration, Instant};
26
27#[derive(Debug, Clone)]
29pub struct SpikingNeuron {
30 pub membrane_potential: f64,
32 pub resting_potential: f64,
34 pub spike_threshold: f64,
36 pub reset_potential: f64,
38 pub tau_membrane: f64,
40 pub refractory_period: f64,
42 pub time_since_spike: f64,
44 pub input_current: f64,
46 pub spike_times: VecDeque<f64>,
48 pub is_refractory: bool,
50}
51
52impl SpikingNeuron {
53 pub fn new() -> Self {
55 Self {
56 membrane_potential: -70.0, resting_potential: -70.0,
58 spike_threshold: -50.0,
59 reset_potential: -80.0,
60 tau_membrane: 10.0, refractory_period: 2.0,
62 time_since_spike: 0.0,
63 input_current: 0.0,
64 spike_times: VecDeque::with_capacity(100),
65 is_refractory: false,
66 }
67 }
68
69 pub fn update(&mut self, dt: f64, inputcurrent: f64) -> bool {
71 self.input_current = inputcurrent;
72 self.time_since_spike += dt;
73
74 if self.is_refractory {
76 if self.time_since_spike >= self.refractory_period {
77 self.is_refractory = false;
78 self.membrane_potential = self.resting_potential;
79 }
80 return false;
81 }
82
83 let leak_current = (self.resting_potential - self.membrane_potential) / self.tau_membrane;
85 let dvdt = leak_current + inputcurrent;
86 self.membrane_potential += dvdt * dt;
87
88 if self.membrane_potential >= self.spike_threshold {
90 self.spike();
91 return true;
92 }
93
94 false
95 }
96
97 fn spike(&mut self) {
99 self.membrane_potential = self.reset_potential;
100 self.is_refractory = true;
101 self.time_since_spike = 0.0;
102
103 self.spike_times.push_back(self.get_current_time());
105
106 if self.spike_times.len() > 100 {
108 self.spike_times.pop_front();
109 }
110 }
111
112 fn get_current_time(&self) -> f64 {
114 scirs2_core::random::random::<f64>() * 1000.0
116 }
117
118 pub fn spike_rate(&self, timewindow: f64) -> f64 {
120 let current_time = self.get_current_time();
121 let cutoff_time = current_time - timewindow;
122
123 let recent_spikes = self
124 .spike_times
125 .iter()
126 .filter(|&&spike_time| spike_time >= cutoff_time)
127 .count();
128
129 recent_spikes as f64 / timewindow
130 }
131}
132
133impl Default for SpikingNeuron {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct PlasticSynapse {
142 pub weight: f64,
144 pub pre_neuron_id: usize,
146 pub post_neuron_id: usize,
148 pub last_pre_spike: Option<f64>,
150 pub last_post_spike: Option<f64>,
152 pub stdp_params: STDPParameters,
154 pub delay: f64,
156}
157
158#[derive(Debug, Clone)]
160pub struct STDPParameters {
161 pub a_plus: f64,
163 pub a_minus: f64,
165 pub tau_plus: f64,
167 pub tau_minus: f64,
169 pub w_max: f64,
171 pub w_min: f64,
173}
174
175impl Default for STDPParameters {
176 fn default() -> Self {
177 Self {
178 a_plus: 0.01,
179 a_minus: 0.012,
180 tau_plus: 20.0,
181 tau_minus: 20.0,
182 w_max: 1.0,
183 w_min: 0.0,
184 }
185 }
186}
187
188impl PlasticSynapse {
189 pub fn new(pre_id: usize, post_id: usize, initialweight: f64) -> Self {
191 Self {
192 weight: initialweight,
193 pre_neuron_id: pre_id,
194 post_neuron_id: post_id,
195 last_pre_spike: None,
196 last_post_spike: None,
197 stdp_params: STDPParameters::default(),
198 delay: 1.0, }
200 }
201
202 pub fn update_weight(&mut self, pre_spike_time: Option<f64>, post_spiketime: Option<f64>) {
204 if let Some(pre_time) = pre_spike_time {
206 self.last_pre_spike = Some(pre_time);
207 }
208 if let Some(post_time) = post_spiketime {
209 self.last_post_spike = Some(post_time);
210 }
211
212 if let (Some(t_pre), Some(t_post)) = (self.last_pre_spike, self.last_post_spike) {
214 let dt = t_post - t_pre - self.delay;
215
216 let weight_change = if dt > 0.0 {
217 self.stdp_params.a_plus * (-dt / self.stdp_params.tau_plus).exp()
219 } else {
220 -self.stdp_params.a_minus * (dt / self.stdp_params.tau_minus).exp()
222 };
223
224 self.weight += weight_change;
225 self.weight = self
226 .weight
227 .clamp(self.stdp_params.w_min, self.stdp_params.w_max);
228 }
229 }
230
231 pub fn calculate_current(&self, prespike: bool) -> f64 {
233 if prespike {
234 self.weight * 10.0 } else {
236 0.0
237 }
238 }
239}
240
241#[derive(Debug)]
243pub struct SpikingNeuralNetwork {
244 neurons: Vec<SpikingNeuron>,
246 synapses: Vec<PlasticSynapse>,
248 connectivity: HashMap<usize, Vec<usize>>,
250 dt: f64,
252 current_time: f64,
254 spike_events: VecDeque<SpikeEvent>,
256}
257
258#[derive(Debug, Clone)]
260pub struct SpikeEvent {
261 pub neuron_id: usize,
263 pub spike_time: f64,
265 pub amplitude: f64,
267}
268
269impl SpikingNeuralNetwork {
270 pub fn new(num_neurons: usize, connectivityprobability: f64) -> Self {
272 let mut neurons = Vec::with_capacity(num_neurons);
273 let mut synapses = Vec::new();
274 let mut connectivity = HashMap::new();
275 let mut rng = thread_rng();
276
277 for _ in 0..num_neurons {
279 neurons.push(SpikingNeuron::new());
280 }
281
282 for i in 0..num_neurons {
284 let mut connections = Vec::new();
285 for j in 0..num_neurons {
286 if i != j && rng.random::<f64>() < connectivityprobability {
287 connections.push(j);
288
289 let weight = rng.random_range(0.1..0.8);
291 synapses.push(PlasticSynapse::new(i, j, weight));
292 }
293 }
294 connectivity.insert(i, connections);
295 }
296
297 Self {
298 neurons,
299 synapses,
300 connectivity,
301 dt: 0.1, current_time: 0.0,
303 spike_events: VecDeque::with_capacity(1000),
304 }
305 }
306
307 pub fn process_input(&mut self, input: &Array1<f64>) -> Array1<f64> {
309 let num_neurons = self.neurons.len();
310 let input_size = input.len();
311
312 self.spike_events.clear();
314
315 for (i, &input_val) in input.iter().enumerate() {
317 if i < num_neurons {
318 self.neurons[i].input_current = input_val * 50.0; }
320 }
321
322 let mut spikes = vec![false; num_neurons];
324
325 let neuron_spike_states: Vec<bool> = self
327 .neurons
328 .iter()
329 .map(|neuron| neuron.time_since_spike < self.dt)
330 .collect();
331
332 for (i, neuron) in self.neurons.iter_mut().enumerate() {
333 let mut synaptic_input = 0.0;
335
336 for synapse in &self.synapses {
337 if synapse.post_neuron_id == i {
338 let prespike = neuron_spike_states[synapse.pre_neuron_id];
339 synaptic_input += synapse.calculate_current(prespike);
340 }
341 }
342
343 if i < input_size {
345 synaptic_input += neuron.input_current;
346 }
347
348 let spiked = neuron.update(self.dt, synaptic_input);
350 spikes[i] = spiked;
351
352 if spiked {
353 self.spike_events.push_back(SpikeEvent {
354 neuron_id: i,
355 spike_time: self.current_time,
356 amplitude: 1.0,
357 });
358 }
359 }
360
361 for synapse in &mut self.synapses {
363 let pre_spike_time = if spikes[synapse.pre_neuron_id] {
364 Some(self.current_time)
365 } else {
366 None
367 };
368
369 let post_spiketime = if spikes[synapse.post_neuron_id] {
370 Some(self.current_time)
371 } else {
372 None
373 };
374
375 synapse.update_weight(pre_spike_time, post_spiketime);
376 }
377
378 self.current_time += self.dt;
379
380 let timewindow = 10.0; let mut output = Array1::zeros(num_neurons);
383 for (i, neuron) in self.neurons.iter().enumerate() {
384 output[i] = neuron.spike_rate(timewindow);
385 }
386
387 output
388 }
389
390 pub fn get_activity_stats(&self) -> NetworkActivityStats {
392 let total_spikes = self.spike_events.len();
393 let active_neurons = self
394 .neurons
395 .iter()
396 .filter(|neuron| neuron.spike_rate(10.0) > 0.0)
397 .count();
398
399 let avg_membrane_potential = self
400 .neurons
401 .iter()
402 .map(|neuron| neuron.membrane_potential)
403 .sum::<f64>()
404 / self.neurons.len() as f64;
405
406 let avg_weight = self
407 .synapses
408 .iter()
409 .map(|synapse| synapse.weight)
410 .sum::<f64>()
411 / self.synapses.len() as f64;
412
413 NetworkActivityStats {
414 total_spikes,
415 active_neurons,
416 avg_membrane_potential,
417 avg_synaptic_weight: avg_weight,
418 network_sparsity: active_neurons as f64 / self.neurons.len() as f64,
419 }
420 }
421}
422
423#[derive(Debug, Clone)]
425pub struct NetworkActivityStats {
426 pub total_spikes: usize,
428 pub active_neurons: usize,
430 pub avg_membrane_potential: f64,
432 pub avg_synaptic_weight: f64,
434 pub network_sparsity: f64,
436}
437
438#[derive(Debug)]
440pub struct NeuromorphicEdgeDetector {
441 snn: SpikingNeuralNetwork,
443 preprocessing_params: EdgePreprocessingParams,
445 adaptation_rate: f64,
447 processing_history: VecDeque<f64>,
449}
450
451#[derive(Debug, Clone)]
453pub struct EdgePreprocessingParams {
454 pub contrast_threshold: f64,
456 pub temporal_threshold: f64,
458 pub spatial_kernel_size: usize,
460 pub adaptation_speed: f64,
462}
463
464impl Default for EdgePreprocessingParams {
465 fn default() -> Self {
466 Self {
467 contrast_threshold: 0.1,
468 temporal_threshold: 0.05,
469 spatial_kernel_size: 3,
470 adaptation_speed: 0.01,
471 }
472 }
473}
474
475impl NeuromorphicEdgeDetector {
476 pub fn new(_inputsize: usize) -> Self {
478 let network_size = _inputsize * 2; let snn = SpikingNeuralNetwork::new(network_size, 0.3);
480
481 Self {
482 snn,
483 preprocessing_params: EdgePreprocessingParams::default(),
484 adaptation_rate: 0.001,
485 processing_history: VecDeque::with_capacity(100),
486 }
487 }
488
489 fn image_to_spikes(&self, imagepatch: &ArrayView2<f32>) -> Array1<f64> {
491 let (height, width) = imagepatch.dim();
492 let mut spike_input = Array1::zeros(height * width);
493
494 for (i, &pixel) in imagepatch.iter().enumerate() {
496 let spike_rate = (pixel as f64 * 100.0).max(0.0); spike_input[i] = spike_rate;
499 }
500
501 spike_input
502 }
503
504 fn detect_edges_neuromorphic(&mut self, frame: &Frame) -> Result<Array2<f32>> {
506 let (height, width) = frame.data.dim();
507 let mut edge_map = Array2::zeros((height, width));
508
509 let kernel_size = self.preprocessing_params.spatial_kernel_size;
510 let half_kernel = kernel_size / 2;
511
512 for y in half_kernel..height.saturating_sub(half_kernel) {
514 for x in half_kernel..width.saturating_sub(half_kernel) {
515 let patch = frame.data.slice(scirs2_core::ndarray::s![
517 y.saturating_sub(half_kernel)..=(y + half_kernel).min(height - 1),
518 x.saturating_sub(half_kernel)..=(x + half_kernel).min(width - 1)
519 ]);
520
521 let spike_input = self.image_to_spikes(&patch);
523
524 let network_output = self.snn.process_input(&spike_input);
526
527 let edge_strength = network_output.mean() as f32;
529 edge_map[[y, x]] = edge_strength;
530 }
531 }
532
533 let max_edge = edge_map.iter().fold(0.0f32, |a, &b| a.max(b));
535 if max_edge > 0.0 {
536 edge_map.mapv_inplace(|x| x / max_edge);
537 }
538
539 Ok(edge_map)
540 }
541
542 fn adapt_parameters(&mut self, performancemetric: f64) {
544 self.processing_history.push_back(performancemetric);
545
546 if self.processing_history.len() > 10 {
547 self.processing_history.pop_front();
548 }
549
550 if self.processing_history.len() >= 2 {
552 let recent_avg = self.processing_history.iter().rev().take(5).sum::<f64>()
553 / 5.0_f64.min(self.processing_history.len() as f64);
554
555 let older_avg = self.processing_history.iter().take(5).sum::<f64>()
556 / 5.0_f64.min(self.processing_history.len() as f64);
557
558 let trend = recent_avg - older_avg;
559
560 if trend < 0.0 {
562 self.preprocessing_params.contrast_threshold *=
564 1.0 - self.preprocessing_params.adaptation_speed;
565 self.preprocessing_params.temporal_threshold *=
566 1.0 + self.preprocessing_params.adaptation_speed;
567 } else if trend > 0.0 {
568 self.preprocessing_params.contrast_threshold *=
570 1.0 + self.preprocessing_params.adaptation_speed * 0.5;
571 self.preprocessing_params.temporal_threshold *=
572 1.0 - self.preprocessing_params.adaptation_speed * 0.5;
573 }
574
575 self.preprocessing_params.contrast_threshold = self
577 .preprocessing_params
578 .contrast_threshold
579 .clamp(0.01, 0.5);
580 self.preprocessing_params.temporal_threshold = self
581 .preprocessing_params
582 .temporal_threshold
583 .clamp(0.01, 0.2);
584 }
585 }
586}
587
588impl ProcessingStage for NeuromorphicEdgeDetector {
589 fn process(&mut self, frame: Frame) -> Result<Frame> {
590 let edge_map = self.detect_edges_neuromorphic(&frame)?;
592
593 let edge_density =
595 edge_map.iter().filter(|&&x| x > 0.1).count() as f64 / edge_map.len() as f64;
596
597 self.adapt_parameters(edge_density);
599
600 Ok(Frame {
601 data: edge_map,
602 timestamp: frame.timestamp,
603 index: frame.index,
604 metadata: frame.metadata,
605 })
606 }
607
608 fn name(&self) -> &str {
609 "NeuromorphicEdgeDetector"
610 }
611}
612
613#[derive(Debug)]
615pub struct EventDrivenProcessor {
616 event_buffer: VecDeque<PixelEvent>,
618 _eventthreshold: f32,
620 previous_frame: Option<Array2<f32>>,
622 spatial_clusters: HashMap<(usize, usize), EventCluster>,
624 temporal_window: Duration,
626 efficiency_metrics: EfficiencyMetrics,
628}
629
630#[derive(Debug, Clone)]
632pub struct PixelEvent {
633 pub x: usize,
635 pub y: usize,
637 pub magnitude: f32,
639 pub timestamp: Instant,
641 pub polarity: EventPolarity,
643}
644
645#[derive(Debug, Clone, PartialEq)]
647pub enum EventPolarity {
648 Positive,
650 Negative,
652}
653
654#[derive(Debug, Clone)]
656pub struct EventCluster {
657 pub center: (f32, f32),
659 pub events: Vec<PixelEvent>,
661 pub activity: f32,
663 pub last_update: Instant,
665}
666
667#[derive(Debug, Clone)]
669pub struct EfficiencyMetrics {
670 pub sparsity: f32,
672 pub energy_consumption: f32,
674 pub speedup_factor: f32,
676 pub compression_ratio: f32,
678}
679
680impl EventDrivenProcessor {
681 pub fn new(_eventthreshold: f32) -> Self {
683 Self {
684 event_buffer: VecDeque::with_capacity(10000),
685 _eventthreshold,
686 previous_frame: None,
687 spatial_clusters: HashMap::new(),
688 temporal_window: Duration::from_millis(50),
689 efficiency_metrics: EfficiencyMetrics {
690 sparsity: 0.0,
691 energy_consumption: 0.0,
692 speedup_factor: 1.0,
693 compression_ratio: 1.0,
694 },
695 }
696 }
697
698 fn generate_events(&mut self, currentframe: &Array2<f32>) -> Vec<PixelEvent> {
700 let mut events = Vec::new();
701 let current_time = Instant::now();
702
703 if let Some(ref prev_frame) = self.previous_frame {
704 let (height, width) = currentframe.dim();
705
706 for y in 0..height {
707 for x in 0..width {
708 let current_val = currentframe[[y, x]];
709 let prev_val = prev_frame[[y, x]];
710 let diff = current_val - prev_val;
711
712 if diff.abs() > self._eventthreshold {
713 let polarity = if diff > 0.0 {
714 EventPolarity::Positive
715 } else {
716 EventPolarity::Negative
717 };
718
719 events.push(PixelEvent {
720 x,
721 y,
722 magnitude: diff.abs(),
723 timestamp: current_time,
724 polarity,
725 });
726 }
727 }
728 }
729 }
730
731 self.previous_frame = Some(currentframe.clone());
732 events
733 }
734
735 fn cluster_events(&mut self, events: &[PixelEvent]) {
737 const CLUSTER_RADIUS: f32 = 5.0;
738
739 let current_time = Instant::now();
741 self.spatial_clusters.retain(|_, cluster| {
742 current_time.duration_since(cluster.last_update) < self.temporal_window
743 });
744
745 for event in events {
746 let mut assigned_to_cluster = false;
747
748 for cluster in self.spatial_clusters.values_mut() {
750 let distance = ((event.x as f32 - cluster.center.0).powi(2)
751 + (event.y as f32 - cluster.center.1).powi(2))
752 .sqrt();
753
754 if distance <= CLUSTER_RADIUS {
755 cluster.events.push(event.clone());
756 cluster.activity += event.magnitude;
757 cluster.last_update = current_time;
758
759 let total_events = cluster.events.len() as f32;
761 cluster.center = (
762 (cluster.center.0 * (total_events - 1.0) + event.x as f32) / total_events,
763 (cluster.center.1 * (total_events - 1.0) + event.y as f32) / total_events,
764 );
765
766 assigned_to_cluster = true;
767 break;
768 }
769 }
770
771 if !assigned_to_cluster {
773 let cluster = EventCluster {
774 center: (event.x as f32, event.y as f32),
775 events: vec![event.clone()],
776 activity: event.magnitude,
777 last_update: current_time,
778 };
779
780 self.spatial_clusters.insert((event.x, event.y), cluster);
781 }
782 }
783 }
784
785 fn process_events_sparse(&self, frameshape: (usize, usize)) -> Array2<f32> {
787 let (height, width) = frameshape;
788 let mut processed_frame = Array2::zeros((height, width));
789
790 for cluster in self.spatial_clusters.values() {
792 if cluster.activity > self._eventthreshold {
793 let cluster_x = cluster.center.0 as usize;
795 let cluster_y = cluster.center.1 as usize;
796
797 let enhancement_radius = 2;
799 for dy in -enhancement_radius..=enhancement_radius {
800 for dx in -enhancement_radius..=enhancement_radius {
801 let x = (cluster_x as i32 + dx).clamp(0, width as i32 - 1) as usize;
802 let y = (cluster_y as i32 + dy).clamp(0, height as i32 - 1) as usize;
803
804 let distance = ((dx as f32).powi(2) + (dy as f32).powi(2)).sqrt();
805 let weight = (1.0 - distance / enhancement_radius as f32).max(0.0);
806
807 processed_frame[[y, x]] += cluster.activity * weight;
808 }
809 }
810 }
811 }
812
813 let max_val = processed_frame.iter().fold(0.0f32, |a, &b| a.max(b));
815 if max_val > 0.0 {
816 processed_frame.mapv_inplace(|x| x / max_val);
817 }
818
819 processed_frame
820 }
821
822 fn update_efficiency_metrics(&mut self, events: &[PixelEvent], framesize: usize) {
824 let event_count = events.len();
825
826 self.efficiency_metrics.sparsity = event_count as f32 / framesize as f32;
828
829 self.efficiency_metrics.energy_consumption = self.efficiency_metrics.sparsity * 0.1;
831
832 self.efficiency_metrics.speedup_factor = 1.0 / self.efficiency_metrics.sparsity.max(0.01);
834
835 self.efficiency_metrics.compression_ratio = framesize as f32 / event_count.max(1) as f32;
837 }
838}
839
840impl ProcessingStage for EventDrivenProcessor {
841 fn process(&mut self, frame: Frame) -> Result<Frame> {
842 let framesize = frame.data.len();
843
844 let events = self.generate_events(&frame.data);
846
847 self.cluster_events(&events);
849
850 let processed_data = self.process_events_sparse(frame.data.dim());
852
853 self.update_efficiency_metrics(&events, framesize);
855
856 for event in events {
858 self.event_buffer.push_back(event);
859
860 if self.event_buffer.len() > 10000 {
862 self.event_buffer.pop_front();
863 }
864 }
865
866 Ok(Frame {
867 data: processed_data,
868 timestamp: frame.timestamp,
869 index: frame.index,
870 metadata: frame.metadata,
871 })
872 }
873
874 fn name(&self) -> &str {
875 "EventDrivenProcessor"
876 }
877}
878
879impl EventDrivenProcessor {
880 pub fn get_efficiency_metrics(&self) -> &EfficiencyMetrics {
882 &self.efficiency_metrics
883 }
884
885 pub fn get_event_stats(&self) -> EventStats {
887 let total_events = self.event_buffer.len();
888 let active_clusters = self.spatial_clusters.len();
889
890 let positive_events = self
891 .event_buffer
892 .iter()
893 .filter(|event| event.polarity == EventPolarity::Positive)
894 .count();
895
896 let negative_events = total_events - positive_events;
897
898 let avg_magnitude = if total_events > 0 {
899 self.event_buffer
900 .iter()
901 .map(|event| event.magnitude)
902 .sum::<f32>()
903 / total_events as f32
904 } else {
905 0.0
906 };
907
908 EventStats {
909 total_events,
910 positive_events,
911 negative_events,
912 active_clusters,
913 avg_event_magnitude: avg_magnitude,
914 sparsity: self.efficiency_metrics.sparsity,
915 }
916 }
917}
918
919#[derive(Debug, Clone)]
921pub struct EventStats {
922 pub total_events: usize,
924 pub positive_events: usize,
926 pub negative_events: usize,
928 pub active_clusters: usize,
930 pub avg_event_magnitude: f32,
932 pub sparsity: f32,
934}
935
936#[derive(Debug)]
938pub struct AdaptiveNeuromorphicPipeline {
939 edge_detector: NeuromorphicEdgeDetector,
941 event_processor: EventDrivenProcessor,
943 processing_mode: NeuromorphicMode,
945 adaptation_params: AdaptationParams,
947 performance_history: VecDeque<PerformanceSnapshot>,
949}
950
951#[derive(Debug, Clone, PartialEq)]
953pub enum NeuromorphicMode {
954 HighAccuracy,
956 Balanced,
958 AdvancedEfficient,
960}
961
962#[derive(Debug, Clone)]
964pub struct AdaptationParams {
965 pub performance_threshold: f32,
967 pub energy_budget: f32,
969 pub learning_rate: f32,
971 pub min_accuracy: f32,
973}
974
975#[derive(Debug, Clone)]
977pub struct PerformanceSnapshot {
978 pub accuracy: f32,
980 pub energy: f32,
982 pub speed: f32,
984 pub sparsity: f32,
986 pub timestamp: Instant,
988}
989
990impl AdaptiveNeuromorphicPipeline {
991 pub fn new(_inputsize: usize) -> Self {
993 let edge_detector = NeuromorphicEdgeDetector::new(_inputsize);
994 let event_processor = EventDrivenProcessor::new(0.05);
995
996 Self {
997 edge_detector,
998 event_processor,
999 processing_mode: NeuromorphicMode::Balanced,
1000 adaptation_params: AdaptationParams {
1001 performance_threshold: 0.8,
1002 energy_budget: 1.0,
1003 learning_rate: 0.01,
1004 min_accuracy: 0.6,
1005 },
1006 performance_history: VecDeque::with_capacity(100),
1007 }
1008 }
1009
1010 pub fn process_adaptive(&mut self, frame: Frame) -> Result<Frame> {
1012 let start_time = Instant::now();
1013
1014 let processed_frame = match self.processing_mode {
1016 NeuromorphicMode::HighAccuracy => {
1017 let edge_frame = self.edge_detector.process(frame)?;
1019 self.event_processor.process(edge_frame)?
1020 }
1021 NeuromorphicMode::Balanced => {
1022 let event_stats = self.event_processor.get_event_stats();
1024
1025 if event_stats.sparsity > 0.1 {
1026 let edge_frame = self.edge_detector.process(frame)?;
1028 self.event_processor.process(edge_frame)?
1029 } else {
1030 self.event_processor.process(frame)?
1032 }
1033 }
1034 NeuromorphicMode::AdvancedEfficient => {
1035 self.event_processor.process(frame)?
1037 }
1038 };
1039
1040 let processing_time = start_time.elapsed();
1041
1042 let efficiency_metrics = self.event_processor.get_efficiency_metrics();
1044 let snapshot = PerformanceSnapshot {
1045 accuracy: self.estimate_accuracy(&processed_frame),
1046 energy: efficiency_metrics.energy_consumption,
1047 speed: 1.0 / processing_time.as_secs_f32(),
1048 sparsity: efficiency_metrics.sparsity,
1049 timestamp: Instant::now(),
1050 };
1051
1052 self.performance_history.push_back(snapshot);
1053 if self.performance_history.len() > 100 {
1054 self.performance_history.pop_front();
1055 }
1056
1057 self.adapt_processing_mode();
1059
1060 Ok(processed_frame)
1061 }
1062
1063 fn estimate_accuracy(&self, frame: &Frame) -> f32 {
1065 let mean = frame.data.mean_or(0.0);
1067 let variance =
1068 frame.data.iter().map(|&x| (x - mean).powi(2)).sum::<f32>() / frame.data.len() as f32;
1069 let edge_density =
1070 frame.data.iter().filter(|&&x| x > 0.1).count() as f32 / frame.data.len() as f32;
1071
1072 (variance.sqrt() + edge_density).min(1.0)
1073 }
1074
1075 fn adapt_processing_mode(&mut self) {
1077 if self.performance_history.len() < 10 {
1078 return;
1079 }
1080
1081 let recent_performance = &self
1082 .performance_history
1083 .iter()
1084 .rev()
1085 .take(10)
1086 .cloned()
1087 .collect::<Vec<_>>();
1088
1089 let avg_accuracy = recent_performance.iter().map(|p| p.accuracy).sum::<f32>()
1090 / recent_performance.len() as f32;
1091
1092 let avg_energy = recent_performance.iter().map(|p| p.energy).sum::<f32>()
1093 / recent_performance.len() as f32;
1094
1095 let avg_speed = recent_performance.iter().map(|p| p.speed).sum::<f32>()
1096 / recent_performance.len() as f32;
1097
1098 match self.processing_mode {
1100 NeuromorphicMode::HighAccuracy => {
1101 if avg_energy > self.adaptation_params.energy_budget && avg_speed < 30.0 {
1102 self.processing_mode = NeuromorphicMode::Balanced;
1103 }
1104 }
1105 NeuromorphicMode::Balanced => {
1106 if avg_accuracy < self.adaptation_params.min_accuracy {
1107 self.processing_mode = NeuromorphicMode::HighAccuracy;
1108 } else if avg_energy < self.adaptation_params.energy_budget * 0.5
1109 && avg_speed > 60.0
1110 {
1111 self.processing_mode = NeuromorphicMode::AdvancedEfficient;
1112 }
1113 }
1114 NeuromorphicMode::AdvancedEfficient => {
1115 if avg_accuracy < self.adaptation_params.min_accuracy * 0.8 {
1116 self.processing_mode = NeuromorphicMode::Balanced;
1117 }
1118 }
1119 }
1120 }
1121
1122 pub fn get_processing_stats(&self) -> NeuromorphicProcessingStats {
1124 let efficiency_metrics = self.event_processor.get_efficiency_metrics();
1125 let event_stats = self.event_processor.get_event_stats();
1126
1127 let recent_performance = if !self.performance_history.is_empty() {
1128 self.performance_history
1129 .back()
1130 .expect("Performance history should not be empty after check")
1131 .clone()
1132 } else {
1133 PerformanceSnapshot {
1134 accuracy: 0.0,
1135 energy: 0.0,
1136 speed: 0.0,
1137 sparsity: 0.0,
1138 timestamp: Instant::now(),
1139 }
1140 };
1141
1142 NeuromorphicProcessingStats {
1143 current_mode: self.processing_mode.clone(),
1144 accuracy: recent_performance.accuracy,
1145 energy_consumption: efficiency_metrics.energy_consumption,
1146 processing_speed: recent_performance.speed,
1147 sparsity: efficiency_metrics.sparsity,
1148 speedup_factor: efficiency_metrics.speedup_factor,
1149 total_events: event_stats.total_events,
1150 active_clusters: event_stats.active_clusters,
1151 }
1152 }
1153
1154 pub async fn initialize_adaptive_learning(&mut self) -> Result<()> {
1156 self.performance_history.clear();
1158
1159 self.processing_mode = NeuromorphicMode::Balanced;
1161
1162 self.adaptation_params = AdaptationParams {
1164 performance_threshold: 0.8,
1165 energy_budget: 1.0,
1166 learning_rate: 0.01,
1167 min_accuracy: 0.6,
1168 };
1169
1170 Ok(())
1173 }
1174}
1175
1176#[derive(Debug, Clone)]
1178pub struct NeuromorphicProcessingStats {
1179 pub current_mode: NeuromorphicMode,
1181 pub accuracy: f32,
1183 pub energy_consumption: f32,
1185 pub processing_speed: f32,
1187 pub sparsity: f32,
1189 pub speedup_factor: f32,
1191 pub total_events: usize,
1193 pub active_clusters: usize,
1195}
1196
1197#[cfg(test)]
1198mod tests {
1199 use super::*;
1200
1201 #[test]
1202 fn test_spiking_neuron() {
1203 let mut neuron = SpikingNeuron::new();
1204
1205 let spiked = neuron.update(1.0, 10.0);
1207 assert!(!spiked);
1208 assert!(neuron.membrane_potential > neuron.resting_potential);
1209
1210 let mut spike_occurred = false;
1212 for _ in 0..100 {
1213 if neuron.update(1.0, 100.0) {
1214 spike_occurred = true;
1215 break;
1216 }
1217 }
1218 assert!(spike_occurred);
1219 }
1220
1221 #[test]
1222 fn test_plastic_synapse() {
1223 let mut synapse = PlasticSynapse::new(0, 1, 0.5);
1224
1225 synapse.update_weight(Some(10.0), Some(15.0));
1227 assert!(synapse.weight >= 0.5); synapse.update_weight(Some(20.0), Some(18.0));
1231 }
1233
1234 #[test]
1235 fn test_spiking_neural_network() {
1236 let mut snn = SpikingNeuralNetwork::new(10, 0.2);
1237 let input = Array1::from_vec(vec![1.0, 0.5, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
1238
1239 let output = snn.process_input(&input);
1240 assert_eq!(output.len(), 10);
1241
1242 let stats = snn.get_activity_stats();
1243 assert!(stats.avg_membrane_potential < 0.0); }
1245
1246 #[test]
1247 fn test_neuromorphic_edge_detector() {
1248 let mut detector = NeuromorphicEdgeDetector::new(64);
1249
1250 let frame = Frame {
1251 data: Array2::from_shape_fn((8, 8), |(_y, x)| if x > 4 { 1.0 } else { 0.0 }),
1252 timestamp: Instant::now(),
1253 index: 0,
1254 metadata: Some(FrameMetadata {
1255 width: 8,
1256 height: 8,
1257 fps: 30.0,
1258 channels: 1,
1259 }),
1260 };
1261
1262 let result = detector.process(frame);
1263 assert!(result.is_ok());
1264
1265 let processed = result.expect("Result should be Ok after assertion");
1266 assert_eq!(processed.data.dim(), (8, 8));
1267 }
1268
1269 #[test]
1270 fn test_event_driven_processor() {
1271 let mut processor = EventDrivenProcessor::new(0.1);
1272
1273 let frame1 = Frame {
1275 data: Array2::zeros((10, 10)),
1276 timestamp: Instant::now(),
1277 index: 0,
1278 metadata: None,
1279 };
1280
1281 let frame2 = Frame {
1282 data: Array2::from_shape_fn((10, 10), |(_x, y)| if y == 5 { 1.0 } else { 0.0 }),
1283 timestamp: Instant::now(),
1284 index: 1,
1285 metadata: None,
1286 };
1287
1288 let result1 = processor.process(frame1);
1290 assert!(result1.is_ok());
1291
1292 let result2 = processor.process(frame2);
1294 assert!(result2.is_ok());
1295
1296 let stats = processor.get_event_stats();
1297 println!("Event stats: {stats:?}");
1298 }
1299
1300 #[test]
1301 fn test_adaptive_neuromorphic_pipeline() {
1302 let mut pipeline = AdaptiveNeuromorphicPipeline::new(64);
1303
1304 let frame = Frame {
1305 data: Array2::from_shape_fn((8, 8), |(y, x)| (x + y) as f32 / 16.0),
1306 timestamp: Instant::now(),
1307 index: 0,
1308 metadata: None,
1309 };
1310
1311 let result = pipeline.process_adaptive(frame);
1312 assert!(result.is_ok());
1313
1314 let stats = pipeline.get_processing_stats();
1315 assert!(stats.sparsity >= 0.0 && stats.sparsity <= 1.0);
1316 }
1317}