1use crate::error::SpatialResult;
8use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2};
9use scirs2_core::random::Rng;
10use std::collections::VecDeque;
11
12#[derive(Debug, Clone)]
52pub struct AdvancedMemristiveLearning {
53 crossbar_array: MemristiveCrossbar,
55 plasticity_mechanisms: Vec<PlasticityMechanism>,
57 homeostatic_system: HomeostaticSystem,
59 metaplasticity: MetaplasticityRules,
61 neuromodulation: NeuromodulationSystem,
63 learning_history: LearningHistory,
65 #[allow(dead_code)]
67 online_learning: bool,
68 forgetting_protection: bool,
70}
71
72#[derive(Debug, Clone)]
78pub struct MemristiveCrossbar {
79 pub conductances: Array2<f64>,
81 pub resistances: Array2<f64>,
83 pub switching_thresholds: Array2<f64>,
85 pub retention_times: Array2<f64>,
87 pub endurance_cycles: Array2<usize>,
89 pub programming_voltages: Array2<f64>,
91 pub temperature_coefficients: Array2<f64>,
93 pub device_variability: Array2<f64>,
95 pub dimensions: (usize, usize),
97 pub device_type: MemristiveDeviceType,
99}
100
101#[derive(Debug, Clone)]
106pub enum MemristiveDeviceType {
107 TitaniumDioxide,
109 HafniumOxide,
111 TantalumOxide,
113 SilverSulfide,
115 Organic,
117 PhaseChange,
119 MagneticTunnelJunction,
121}
122
123#[derive(Debug, Clone)]
128pub struct PlasticityMechanism {
129 pub mechanism_type: PlasticityType,
131 pub time_constants: PlasticityTimeConstants,
133 pub learning_rates: PlasticityLearningRates,
135 pub thresholds: PlasticityThresholds,
137 pub enabled: bool,
139 pub weight_scaling: f64,
141}
142
143#[derive(Debug, Clone)]
148pub enum PlasticityType {
149 STDP,
151 HomeostaticScaling,
153 IntrinsicPlasticity,
155 HeterosynapticPlasticity,
157 Metaplasticity,
159 CalciumDependent,
161 VoltageDependent,
163 FrequencyDependent,
165}
166
167#[derive(Debug, Clone)]
169pub struct PlasticityTimeConstants {
170 pub tau_fast: f64,
172 pub tau_slow: f64,
174 pub stdp_window: f64,
176 pub tau_homeostatic: f64,
178 pub tau_calcium: f64,
180}
181
182#[derive(Debug, Clone)]
184pub struct PlasticityLearningRates {
185 pub potentiation_rate: f64,
187 pub depression_rate: f64,
189 pub homeostatic_rate: f64,
191 pub metaplastic_rate: f64,
193 pub intrinsic_rate: f64,
195}
196
197#[derive(Debug, Clone)]
199pub struct PlasticityThresholds {
200 pub ltp_threshold: f64,
202 pub ltd_threshold: f64,
204 pub target_activity: f64,
206 pub metaplasticity_threshold: f64,
208 pub saturation_threshold: f64,
210}
211
212#[derive(Debug, Clone)]
217pub struct HomeostaticSystem {
218 pub target_firing_rates: Array1<f64>,
220 pub current_firing_rates: Array1<f64>,
222 pub time_constants: Array1<f64>,
224 pub mechanisms: Vec<HomeostaticMechanism>,
226 pub adaptation_rates: Array1<f64>,
228 pub activity_history: VecDeque<Array1<f64>>,
230 pub history_window: usize,
232}
233
234#[derive(Debug, Clone)]
236pub enum HomeostaticMechanism {
237 SynapticScaling,
239 IntrinsicExcitability,
241 StructuralPlasticity,
243 InhibitoryPlasticity,
245 MetaplasticRegulation,
247}
248
249#[derive(Debug, Clone)]
254pub struct MetaplasticityRules {
255 pub learning_rate_adaptation: LearningRateAdaptation,
257 pub threshold_adaptation: ThresholdAdaptation,
259 pub consolidation_rules: ConsolidationRules,
261 pub forgetting_protection: ForgettingProtectionRules,
263}
264
265#[derive(Debug, Clone)]
267pub struct LearningRateAdaptation {
268 pub base_rate: f64,
270 pub adaptation_factor: f64,
272 pub performance_history: VecDeque<f64>,
274 pub adaptation_threshold: f64,
276 pub max_rate: f64,
278 pub min_rate: f64,
280}
281
282#[derive(Debug, Clone)]
284pub struct ThresholdAdaptation {
285 pub adaptive_thresholds: Array1<f64>,
287 pub update_rates: Array1<f64>,
289 pub target_activations: Array1<f64>,
291 pub threshold_bounds: Vec<(f64, f64)>,
293}
294
295#[derive(Debug, Clone)]
297pub struct ConsolidationRules {
298 pub time_windows: Vec<f64>,
300 pub consolidation_strengths: Array1<f64>,
302 pub replay_enabled: bool,
304 pub replay_patterns: Vec<Array1<f64>>,
306 pub systems_consolidation: bool,
308}
309
310#[derive(Debug, Clone)]
312pub struct ForgettingProtectionRules {
313 pub ewc_enabled: bool,
315 pub fisher_information: Array2<f64>,
317 pub synaptic_intelligence: bool,
319 pub importance_weights: Array1<f64>,
321 pub protection_strength: f64,
323}
324
325#[derive(Debug, Clone)]
330pub struct NeuromodulationSystem {
331 pub dopamine_levels: Array1<f64>,
333 pub serotonin_levels: Array1<f64>,
335 pub acetylcholine_levels: Array1<f64>,
337 pub noradrenaline_levels: Array1<f64>,
339 pub modulation_effects: NeuromodulationEffects,
341 pub release_patterns: NeuromodulatorReleasePatterns,
343}
344
345#[derive(Debug, Clone)]
347pub struct NeuromodulationEffects {
348 pub learning_rate_modulation: Array1<f64>,
350 pub threshold_modulation: Array1<f64>,
352 pub excitability_modulation: Array1<f64>,
354 pub attention_modulation: Array1<f64>,
356}
357
358#[derive(Debug, Clone)]
360pub struct NeuromodulatorReleasePatterns {
361 pub phasic_dopamine: Vec<(f64, f64)>, pub tonic_serotonin: f64,
365 pub cholinergic_attention: Array1<f64>,
367 pub stress_noradrenaline: f64,
369}
370
371#[derive(Debug, Clone)]
376pub struct LearningHistory {
377 pub weight_changes: VecDeque<Array2<f64>>,
379 pub performance_metrics: VecDeque<PerformanceMetrics>,
381 pub plasticity_events: VecDeque<PlasticityEvent>,
383 pub consolidation_events: VecDeque<ConsolidationEvent>,
385 pub max_history_length: usize,
387}
388
389#[derive(Debug, Clone)]
391pub struct PerformanceMetrics {
392 pub accuracy: f64,
394 pub learning_speed: f64,
396 pub stability: f64,
398 pub generalization: f64,
400 pub timestamp: f64,
402}
403
404#[derive(Debug, Clone)]
406pub struct PlasticityEvent {
407 pub event_type: PlasticityEventType,
409 pub synapses: Vec<(usize, usize)>,
411 pub magnitude: f64,
413 pub timestamp: f64,
415 pub context: String,
417}
418
419#[derive(Debug, Clone)]
421pub enum PlasticityEventType {
422 LongTermPotentiation,
423 LongTermDepression,
424 HomeostaticScaling,
425 StructuralPlasticity,
426 MetaplasticChange,
427}
428
429#[derive(Debug, Clone)]
431pub struct ConsolidationEvent {
432 pub consolidation_type: ConsolidationType,
434 pub patterns: Vec<Array1<f64>>,
436 pub strength: f64,
438 pub timestamp: f64,
440}
441
442#[derive(Debug, Clone)]
444pub enum ConsolidationType {
445 SynapticConsolidation,
446 SystemsConsolidation,
447 ReconsolidationUpdate,
448 OfflineReplay,
449}
450
451#[derive(Debug, Clone)]
453pub struct TrainingResult {
454 pub final_weights: Array2<f64>,
456 pub training_metrics: Vec<PerformanceMetrics>,
458 pub plasticity_events: VecDeque<PlasticityEvent>,
460 pub consolidation_events: VecDeque<ConsolidationEvent>,
462}
463
464impl AdvancedMemristiveLearning {
465 pub fn new(rows: usize, cols: usize, device_type: MemristiveDeviceType) -> Self {
475 let crossbar_array = MemristiveCrossbar::new(rows, cols, device_type);
476
477 let plasticity_mechanisms = vec![
478 PlasticityMechanism::new(PlasticityType::STDP),
479 PlasticityMechanism::new(PlasticityType::HomeostaticScaling),
480 PlasticityMechanism::new(PlasticityType::IntrinsicPlasticity),
481 ];
482
483 let homeostatic_system = HomeostaticSystem::new(rows);
484 let metaplasticity = MetaplasticityRules::new();
485 let neuromodulation = NeuromodulationSystem::new(rows);
486 let learning_history = LearningHistory::new();
487
488 Self {
489 crossbar_array,
490 plasticity_mechanisms,
491 homeostatic_system,
492 metaplasticity,
493 neuromodulation,
494 learning_history,
495 online_learning: true,
496 forgetting_protection: true,
497 }
498 }
499
500 pub fn enable_plasticity(mut self, plasticity_type: PlasticityType) -> Self {
505 for mechanism in &mut self.plasticity_mechanisms {
506 if std::mem::discriminant(&mechanism.mechanism_type)
507 == std::mem::discriminant(&plasticity_type)
508 {
509 mechanism.enabled = true;
510 }
511 }
512 self
513 }
514
515 pub fn with_homeostatic_regulation(mut self, target_rates: Array1<f64>) -> Self {
520 self.homeostatic_system.target_firing_rates = target_rates;
521 self
522 }
523
524 pub fn with_forgetting_protection(mut self, enabled: bool) -> Self {
529 self.forgetting_protection = enabled;
530 self.metaplasticity.forgetting_protection.ewc_enabled = enabled;
531 self
532 }
533
534 pub async fn train_spatial_data(
547 &mut self,
548 spatial_data: &ArrayView2<'_, f64>,
549 target_outputs: &ArrayView1<'_, f64>,
550 epochs: usize,
551 ) -> SpatialResult<TrainingResult> {
552 let mut training_metrics = Vec::new();
553
554 for epoch in 0..epochs {
555 let epoch_metrics = self.process_epoch(spatial_data, target_outputs).await?;
557
558 self.apply_homeostatic_regulation().await?;
560
561 self.apply_metaplasticity_updates(&epoch_metrics).await?;
563
564 self.update_neuromodulation(&epoch_metrics).await?;
566
567 self.record_learning_history(&epoch_metrics, epoch as f64)
569 .await?;
570
571 training_metrics.push(epoch_metrics);
572
573 if self.should_trigger_consolidation(epoch) {
575 self.trigger_memory_consolidation().await?;
576 }
577 }
578
579 let final_weights = self.crossbar_array.conductances.clone();
580
581 Ok(TrainingResult {
582 final_weights,
583 training_metrics,
584 plasticity_events: self.learning_history.plasticity_events.clone(),
585 consolidation_events: self.learning_history.consolidation_events.clone(),
586 })
587 }
588
589 async fn process_epoch(
591 &mut self,
592 spatial_data: &ArrayView2<'_, f64>,
593 target_outputs: &ArrayView1<'_, f64>,
594 ) -> SpatialResult<PerformanceMetrics> {
595 let n_samples = spatial_data.dim().0;
596 let mut total_error = 0.0;
597 let mut correct_predictions = 0;
598
599 for i in 0..n_samples {
600 let input = spatial_data.row(i);
601 let target = target_outputs[i];
602
603 let output = self.forward_pass(&input).await?;
605
606 let error = target - output;
608 total_error += error.abs();
609
610 if error.abs() < 0.1 {
611 correct_predictions += 1;
612 }
613
614 self.apply_plasticity_mechanisms(&input, output, target, error)
616 .await?;
617
618 self.update_memristive_devices(&input, error).await?;
620 }
621
622 let accuracy = correct_predictions as f64 / n_samples as f64;
623 let average_error = total_error / n_samples as f64;
624
625 Ok(PerformanceMetrics {
626 accuracy,
627 learning_speed: 1.0 / (average_error + 1e-8),
628 stability: self.compute_weight_stability(),
629 generalization: self.estimate_generalization(),
630 timestamp: std::time::SystemTime::now()
631 .duration_since(std::time::UNIX_EPOCH)
632 .unwrap()
633 .as_secs_f64(),
634 })
635 }
636
637 async fn forward_pass(&self, input: &ArrayView1<'_, f64>) -> SpatialResult<f64> {
639 let mut output = 0.0;
640
641 for (i, &input_val) in input.iter().enumerate() {
642 if i < self.crossbar_array.dimensions.0 {
643 for j in 0..self.crossbar_array.dimensions.1 {
644 let conductance = self.crossbar_array.conductances[[i, j]];
645 let current = input_val * conductance;
646
647 let nonlinear_current = self.apply_device_nonlinearity(current, i, j);
649
650 output += nonlinear_current;
651 }
652 }
653 }
654
655 Ok(Self::sigmoid(output))
657 }
658
659 fn apply_device_nonlinearity(&self, current: f64, row: usize, col: usize) -> f64 {
661 match self.crossbar_array.device_type {
662 MemristiveDeviceType::TitaniumDioxide => {
663 let threshold = self.crossbar_array.switching_thresholds[[row, col]];
665 if current.abs() > threshold {
666 current * (1.0 + 0.1 * (current / threshold).ln())
667 } else {
668 current
669 }
670 }
671 MemristiveDeviceType::HafniumOxide => {
672 let threshold = self.crossbar_array.switching_thresholds[[row, col]];
674 current * (1.0 + 0.2 * (current / threshold).tanh())
675 }
676 MemristiveDeviceType::PhaseChange => {
677 let threshold = self.crossbar_array.switching_thresholds[[row, col]];
679 if current.abs() > threshold {
680 current * 2.0
681 } else {
682 current * 0.1
683 }
684 }
685 _ => current, }
687 }
688
689 async fn apply_plasticity_mechanisms(
691 &mut self,
692 input: &ArrayView1<'_, f64>,
693 output: f64,
694 target: f64,
695 error: f64,
696 ) -> SpatialResult<()> {
697 let mechanisms = self.plasticity_mechanisms.clone();
698 for mechanism in &mechanisms {
699 if mechanism.enabled {
700 match mechanism.mechanism_type {
701 PlasticityType::STDP => {
702 self.apply_stdp_plasticity(input, output, mechanism).await?;
703 }
704 PlasticityType::HomeostaticScaling => {
705 self.apply_homeostatic_scaling(input, output, mechanism)
706 .await?;
707 }
708 PlasticityType::CalciumDependent => {
709 self.apply_calcium_dependent_plasticity(input, output, target, mechanism)
710 .await?;
711 }
712 PlasticityType::VoltageDependent => {
713 self.apply_voltage_dependent_plasticity(input, error, mechanism)
714 .await?;
715 }
716 _ => {
717 self.apply_error_based_plasticity(input, error, mechanism)
719 .await?;
720 }
721 }
722 }
723 }
724
725 Ok(())
726 }
727
728 async fn apply_stdp_plasticity(
730 &mut self,
731 input: &ArrayView1<'_, f64>,
732 output: f64,
733 mechanism: &PlasticityMechanism,
734 ) -> SpatialResult<()> {
735 let tau_plus = mechanism.time_constants.tau_fast;
736 let tau_minus = mechanism.time_constants.tau_slow;
737 let a_plus = mechanism.learning_rates.potentiation_rate;
738 let a_minus = mechanism.learning_rates.depression_rate;
739
740 for (i, &input_val) in input.iter().enumerate() {
742 if i < self.crossbar_array.dimensions.0 {
743 for j in 0..self.crossbar_array.dimensions.1 {
744 let dt = if input_val > 0.5 && output > 0.5 {
746 1.0 } else if input_val <= 0.5 && output > 0.5 {
748 -1.0 } else {
750 0.0 };
752
753 let weight_change = if dt > 0.0 {
754 a_plus * (-dt / tau_plus).exp()
755 } else if dt < 0.0 {
756 -a_minus * (dt / tau_minus).exp()
757 } else {
758 0.0
759 };
760
761 self.crossbar_array.conductances[[i, j]] +=
762 weight_change * mechanism.weight_scaling;
763 self.crossbar_array.conductances[[i, j]] =
764 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
765 }
766 }
767 }
768
769 Ok(())
770 }
771
772 async fn apply_homeostatic_scaling(
774 &mut self,
775 _input: &ArrayView1<'_, f64>,
776 output: f64,
777 mechanism: &PlasticityMechanism,
778 ) -> SpatialResult<()> {
779 let target_activity = mechanism.thresholds.target_activity;
780 let scaling_rate = mechanism.learning_rates.homeostatic_rate;
781
782 let activity_error = output - target_activity;
784 let scaling_factor = 1.0 - scaling_rate * activity_error;
785
786 for i in 0..self.crossbar_array.dimensions.0 {
788 for j in 0..self.crossbar_array.dimensions.1 {
789 self.crossbar_array.conductances[[i, j]] *= scaling_factor;
790 self.crossbar_array.conductances[[i, j]] =
791 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
792 }
793 }
794
795 Ok(())
796 }
797
798 async fn apply_calcium_dependent_plasticity(
800 &mut self,
801 input: &ArrayView1<'_, f64>,
802 output: f64,
803 target: f64,
804 mechanism: &PlasticityMechanism,
805 ) -> SpatialResult<()> {
806 let calcium_level = Self::compute_calcium_level(input, output, target);
808
809 let ltp_threshold = mechanism.thresholds.ltp_threshold;
810 let ltd_threshold = mechanism.thresholds.ltd_threshold;
811
812 for (i, &input_val) in input.iter().enumerate() {
813 if i < self.crossbar_array.dimensions.0 {
814 for j in 0..self.crossbar_array.dimensions.1 {
815 let local_calcium = calcium_level * input_val;
816
817 let weight_change = if local_calcium > ltp_threshold {
818 mechanism.learning_rates.potentiation_rate * (local_calcium - ltp_threshold)
819 } else if local_calcium < ltd_threshold {
820 -mechanism.learning_rates.depression_rate * (ltd_threshold - local_calcium)
821 } else {
822 0.0
823 };
824
825 self.crossbar_array.conductances[[i, j]] += weight_change;
826 self.crossbar_array.conductances[[i, j]] =
827 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
828 }
829 }
830 }
831
832 Ok(())
833 }
834
835 async fn apply_voltage_dependent_plasticity(
837 &mut self,
838 input: &ArrayView1<'_, f64>,
839 error: f64,
840 mechanism: &PlasticityMechanism,
841 ) -> SpatialResult<()> {
842 let voltage_threshold = mechanism.thresholds.ltd_threshold;
843
844 for (i, &input_val) in input.iter().enumerate() {
845 if i < self.crossbar_array.dimensions.0 {
846 for j in 0..self.crossbar_array.dimensions.1 {
847 let local_voltage = input_val * error.abs();
848
849 if local_voltage > voltage_threshold {
850 let weight_change = mechanism.learning_rates.potentiation_rate
851 * (local_voltage - voltage_threshold)
852 * error.signum();
853
854 self.crossbar_array.conductances[[i, j]] += weight_change;
855 self.crossbar_array.conductances[[i, j]] =
856 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
857 }
858 }
859 }
860 }
861
862 Ok(())
863 }
864
865 async fn apply_error_based_plasticity(
867 &mut self,
868 input: &ArrayView1<'_, f64>,
869 error: f64,
870 mechanism: &PlasticityMechanism,
871 ) -> SpatialResult<()> {
872 let learning_rate = mechanism.learning_rates.potentiation_rate;
873
874 for (i, &input_val) in input.iter().enumerate() {
875 if i < self.crossbar_array.dimensions.0 {
876 for j in 0..self.crossbar_array.dimensions.1 {
877 let weight_change = learning_rate * error * input_val;
878
879 self.crossbar_array.conductances[[i, j]] += weight_change;
880 self.crossbar_array.conductances[[i, j]] =
881 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
882 }
883 }
884 }
885
886 Ok(())
887 }
888
889 fn compute_calcium_level(input: &ArrayView1<'_, f64>, output: f64, target: f64) -> f64 {
891 let input_activity = input.iter().map(|&x| x.max(0.0)).sum::<f64>();
892 let output_activity = output.max(0.0);
893 let target_activity = target.max(0.0);
894
895 (input_activity * 0.3 + output_activity * 0.4 + target_activity * 0.3).min(1.0)
897 }
898
899 async fn update_memristive_devices(
901 &mut self,
902 input: &ArrayView1<'_, f64>,
903 _error: f64,
904 ) -> SpatialResult<()> {
905 for (i, &input_val) in input.iter().enumerate() {
906 if i < self.crossbar_array.dimensions.0 {
907 for j in 0..self.crossbar_array.dimensions.1 {
908 let conductance = self.crossbar_array.conductances[[i, j]];
910 self.crossbar_array.resistances[[i, j]] = if conductance > 1e-12 {
911 1.0 / conductance
912 } else {
913 1e12
914 };
915
916 if input_val > 0.1 {
918 self.crossbar_array.endurance_cycles[[i, j]] += 1;
919 }
920
921 self.apply_device_aging(i, j);
923
924 self.apply_device_variability(i, j);
926 }
927 }
928 }
929
930 Ok(())
931 }
932
933 fn apply_device_aging(&mut self, row: usize, col: usize) {
935 let cycles = self.crossbar_array.endurance_cycles[[row, col]];
936 let aging_factor = 1.0 - (cycles as f64) * 1e-8; self.crossbar_array.conductances[[row, col]] *= aging_factor.max(0.1);
939 }
940
941 fn apply_device_variability(&mut self, row: usize, col: usize) {
943 let variability = self.crossbar_array.device_variability[[row, col]];
944 let mut rng = scirs2_core::random::rng();
945 let noise = (rng.gen_range(0.0..1.0) - 0.5) * variability;
946
947 self.crossbar_array.conductances[[row, col]] += noise;
948 self.crossbar_array.conductances[[row, col]] =
949 self.crossbar_array.conductances[[row, col]].clamp(0.0, 1.0);
950 }
951
952 async fn apply_homeostatic_regulation(&mut self) -> SpatialResult<()> {
954 let current_rates = self.compute_current_firing_rates();
956 self.homeostatic_system
957 .activity_history
958 .push_back(current_rates);
959
960 if self.homeostatic_system.activity_history.len() > self.homeostatic_system.history_window {
962 self.homeostatic_system.activity_history.pop_front();
963 }
964
965 self.apply_synaptic_scaling().await?;
967 self.apply_intrinsic_excitability_adjustment().await?;
968
969 Ok(())
970 }
971
972 fn compute_current_firing_rates(&self) -> Array1<f64> {
974 let mut rates = Array1::zeros(self.crossbar_array.dimensions.1);
976
977 for j in 0..self.crossbar_array.dimensions.1 {
978 let total_conductance: f64 = (0..self.crossbar_array.dimensions.0)
979 .map(|i| self.crossbar_array.conductances[[i, j]])
980 .sum();
981 rates[j] = Self::sigmoid(total_conductance);
982 }
983
984 rates
985 }
986
987 async fn apply_synaptic_scaling(&mut self) -> SpatialResult<()> {
989 let current_rates = self.compute_current_firing_rates();
990
991 for j in 0..self.crossbar_array.dimensions.1 {
992 let target_rate = self.homeostatic_system.target_firing_rates[j];
993 let current_rate = current_rates[j];
994 let adaptation_rate = self.homeostatic_system.adaptation_rates[j];
995
996 let scaling_factor = 1.0 + adaptation_rate * (target_rate - current_rate);
997
998 for i in 0..self.crossbar_array.dimensions.0 {
1000 self.crossbar_array.conductances[[i, j]] *= scaling_factor;
1001 self.crossbar_array.conductances[[i, j]] =
1002 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
1003 }
1004 }
1005
1006 Ok(())
1007 }
1008
1009 async fn apply_intrinsic_excitability_adjustment(&mut self) -> SpatialResult<()> {
1011 let current_rates = self.compute_current_firing_rates();
1013
1014 for j in 0..self.crossbar_array.dimensions.1 {
1015 let target_rate = self.homeostatic_system.target_firing_rates[j];
1016 let current_rate = current_rates[j];
1017 let adaptation_rate = self.homeostatic_system.adaptation_rates[j];
1018
1019 let threshold_adjustment = adaptation_rate * (current_rate - target_rate);
1020
1021 for i in 0..self.crossbar_array.dimensions.0 {
1022 self.crossbar_array.switching_thresholds[[i, j]] += threshold_adjustment;
1023 self.crossbar_array.switching_thresholds[[i, j]] =
1024 self.crossbar_array.switching_thresholds[[i, j]].clamp(0.1, 2.0);
1025 }
1026 }
1027
1028 Ok(())
1029 }
1030
1031 async fn apply_metaplasticity_updates(
1033 &mut self,
1034 metrics: &PerformanceMetrics,
1035 ) -> SpatialResult<()> {
1036 self.metaplasticity
1038 .learning_rate_adaptation
1039 .performance_history
1040 .push_back(metrics.accuracy);
1041
1042 if self
1043 .metaplasticity
1044 .learning_rate_adaptation
1045 .performance_history
1046 .len()
1047 > 100
1048 {
1049 self.metaplasticity
1050 .learning_rate_adaptation
1051 .performance_history
1052 .pop_front();
1053 }
1054
1055 self.adapt_learning_rates(metrics).await?;
1057
1058 self.adapt_thresholds(metrics).await?;
1060
1061 if metrics.accuracy > 0.9 {
1063 self.trigger_memory_consolidation().await?;
1064 }
1065
1066 Ok(())
1067 }
1068
1069 async fn adapt_learning_rates(&mut self, _metrics: &PerformanceMetrics) -> SpatialResult<()> {
1071 let performance_trend = self.compute_performance_trend();
1072
1073 for mechanism in &mut self.plasticity_mechanisms {
1074 if performance_trend > 0.0 {
1075 mechanism.learning_rates.potentiation_rate *= 1.01;
1077 mechanism.learning_rates.depression_rate *= 1.01;
1078 } else {
1079 mechanism.learning_rates.potentiation_rate *= 0.99;
1081 mechanism.learning_rates.depression_rate *= 0.99;
1082 }
1083
1084 mechanism.learning_rates.potentiation_rate =
1086 mechanism.learning_rates.potentiation_rate.clamp(1e-6, 0.1);
1087 mechanism.learning_rates.depression_rate =
1088 mechanism.learning_rates.depression_rate.clamp(1e-6, 0.1);
1089 }
1090
1091 Ok(())
1092 }
1093
1094 fn compute_performance_trend(&self) -> f64 {
1096 let history = &self
1097 .metaplasticity
1098 .learning_rate_adaptation
1099 .performance_history;
1100
1101 if history.len() < 10 {
1102 return 0.0;
1103 }
1104
1105 let recent_performance: f64 = history.iter().rev().take(5).sum::<f64>() / 5.0;
1106 let older_performance: f64 = history.iter().rev().skip(5).take(5).sum::<f64>() / 5.0;
1107
1108 recent_performance - older_performance
1109 }
1110
1111 async fn adapt_thresholds(&mut self, metrics: &PerformanceMetrics) -> SpatialResult<()> {
1113 for mechanism in &mut self.plasticity_mechanisms {
1115 if metrics.learning_speed > 1.0 {
1116 mechanism.thresholds.ltp_threshold *= 1.001;
1118 mechanism.thresholds.ltd_threshold *= 1.001;
1119 } else {
1120 mechanism.thresholds.ltp_threshold *= 0.999;
1122 mechanism.thresholds.ltd_threshold *= 0.999;
1123 }
1124
1125 mechanism.thresholds.ltp_threshold = mechanism.thresholds.ltp_threshold.clamp(0.1, 2.0);
1127 mechanism.thresholds.ltd_threshold = mechanism.thresholds.ltd_threshold.clamp(0.1, 2.0);
1128 }
1129
1130 Ok(())
1131 }
1132
1133 async fn update_neuromodulation(&mut self, metrics: &PerformanceMetrics) -> SpatialResult<()> {
1135 let performance_change = metrics.accuracy - 0.5; self.neuromodulation
1138 .dopamine_levels
1139 .mapv_inplace(|x| x + 0.1 * performance_change);
1140
1141 let stability_change = metrics.stability - 0.5;
1143 self.neuromodulation
1144 .serotonin_levels
1145 .mapv_inplace(|x| x + 0.05 * stability_change);
1146
1147 self.neuromodulation
1149 .dopamine_levels
1150 .mapv_inplace(|x| x.clamp(0.0, 1.0));
1151 self.neuromodulation
1152 .serotonin_levels
1153 .mapv_inplace(|x| x.clamp(0.0, 1.0));
1154
1155 Ok(())
1156 }
1157
1158 async fn record_learning_history(
1160 &mut self,
1161 metrics: &PerformanceMetrics,
1162 _timestamp: f64,
1163 ) -> SpatialResult<()> {
1164 self.learning_history
1166 .performance_metrics
1167 .push_back(metrics.clone());
1168
1169 self.learning_history
1171 .weight_changes
1172 .push_back(self.crossbar_array.conductances.clone());
1173
1174 if self.learning_history.performance_metrics.len()
1176 > self.learning_history.max_history_length
1177 {
1178 self.learning_history.performance_metrics.pop_front();
1179 self.learning_history.weight_changes.pop_front();
1180 }
1181
1182 Ok(())
1183 }
1184
1185 fn should_trigger_consolidation(&self, epoch: usize) -> bool {
1187 epoch.is_multiple_of(100)
1189 || self
1190 .learning_history
1191 .performance_metrics
1192 .back()
1193 .map(|m| m.accuracy > 0.95)
1194 .unwrap_or(false)
1195 }
1196
1197 async fn trigger_memory_consolidation(&mut self) -> SpatialResult<()> {
1199 self.strengthen_important_connections().await?;
1201
1202 let consolidation_event = ConsolidationEvent {
1204 consolidation_type: ConsolidationType::SynapticConsolidation,
1205 patterns: vec![], strength: 1.0,
1207 timestamp: std::time::SystemTime::now()
1208 .duration_since(std::time::UNIX_EPOCH)
1209 .unwrap()
1210 .as_secs_f64(),
1211 };
1212
1213 self.learning_history
1214 .consolidation_events
1215 .push_back(consolidation_event);
1216
1217 Ok(())
1218 }
1219
1220 async fn strengthen_important_connections(&mut self) -> SpatialResult<()> {
1222 let mut importance_matrix = Array2::zeros(self.crossbar_array.dimensions);
1224
1225 for i in 0..self.crossbar_array.dimensions.0 {
1226 for j in 0..self.crossbar_array.dimensions.1 {
1227 let conductance = self.crossbar_array.conductances[[i, j]];
1228 let usage = self.crossbar_array.endurance_cycles[[i, j]] as f64;
1229
1230 importance_matrix[[i, j]] = conductance * (1.0 + 0.1 * usage.ln_1p());
1232 }
1233 }
1234
1235 let threshold = self.compute_importance_threshold(&importance_matrix, 0.8);
1237
1238 for i in 0..self.crossbar_array.dimensions.0 {
1239 for j in 0..self.crossbar_array.dimensions.1 {
1240 if importance_matrix[[i, j]] > threshold {
1241 self.crossbar_array.conductances[[i, j]] *= 1.05; self.crossbar_array.conductances[[i, j]] =
1243 self.crossbar_array.conductances[[i, j]].min(1.0);
1244 }
1245 }
1246 }
1247
1248 Ok(())
1249 }
1250
1251 fn compute_importance_threshold(
1253 &self,
1254 importance_matrix: &Array2<f64>,
1255 percentile: f64,
1256 ) -> f64 {
1257 let mut values: Vec<f64> = importance_matrix.iter().cloned().collect();
1258 values.sort_by(|a, b| a.partial_cmp(b).unwrap());
1259
1260 let index = (values.len() as f64 * percentile) as usize;
1261 values.get(index).cloned().unwrap_or(0.0)
1262 }
1263
1264 fn sigmoid(x: f64) -> f64 {
1266 1.0 / (1.0 + (-x).exp())
1267 }
1268
1269 fn compute_weight_stability(&self) -> f64 {
1270 let weight_variance = self.crossbar_array.conductances.var(0.0);
1272 1.0 / (1.0 + weight_variance)
1273 }
1274
1275 fn estimate_generalization(&self) -> f64 {
1276 0.8 }
1279
1280 pub fn crossbar_dimensions(&self) -> (usize, usize) {
1282 self.crossbar_array.dimensions
1283 }
1284
1285 pub fn device_type(&self) -> &MemristiveDeviceType {
1287 &self.crossbar_array.device_type
1288 }
1289
1290 pub fn conductances(&self) -> &Array2<f64> {
1292 &self.crossbar_array.conductances
1293 }
1294
1295 pub fn learning_history(&self) -> &LearningHistory {
1297 &self.learning_history
1298 }
1299}
1300
1301impl MemristiveCrossbar {
1302 pub fn new(rows: usize, cols: usize, device_type: MemristiveDeviceType) -> Self {
1304 let mut rng = scirs2_core::random::rng();
1305 let conductances = Array2::from_shape_fn((rows, cols), |_| rng.gen_range(0.0..0.1));
1306 let resistances = conductances.mapv(|g| if g > 1e-12 { 1.0 / g } else { 1e12 });
1307 let switching_thresholds = Array2::from_elem((rows, cols), 0.5);
1308 let retention_times = Array2::from_elem((rows, cols), 1e6);
1309 let endurance_cycles = Array2::zeros((rows, cols));
1310 let programming_voltages = Array2::from_elem((rows, cols), 1.0);
1311 let temperature_coefficients = Array2::from_elem((rows, cols), 0.01);
1312 let device_variability = Array2::from_shape_fn((rows, cols), |_| rng.gen_range(0.0..0.01));
1313
1314 Self {
1315 conductances,
1316 resistances,
1317 switching_thresholds,
1318 retention_times,
1319 endurance_cycles,
1320 programming_voltages,
1321 temperature_coefficients,
1322 device_variability,
1323 dimensions: (rows, cols),
1324 device_type,
1325 }
1326 }
1327}
1328
1329impl PlasticityMechanism {
1330 pub fn new(mechanism_type: PlasticityType) -> Self {
1332 let (time_constants, learning_rates, thresholds) = match mechanism_type {
1333 PlasticityType::STDP => (
1334 PlasticityTimeConstants {
1335 tau_fast: 20.0,
1336 tau_slow: 40.0,
1337 stdp_window: 100.0,
1338 tau_homeostatic: 1000.0,
1339 tau_calcium: 50.0,
1340 },
1341 PlasticityLearningRates {
1342 potentiation_rate: 0.01,
1343 depression_rate: 0.005,
1344 homeostatic_rate: 0.001,
1345 metaplastic_rate: 0.0001,
1346 intrinsic_rate: 0.001,
1347 },
1348 PlasticityThresholds {
1349 ltp_threshold: 0.6,
1350 ltd_threshold: 0.4,
1351 target_activity: 0.5,
1352 metaplasticity_threshold: 0.8,
1353 saturation_threshold: 0.95,
1354 },
1355 ),
1356 _ => (
1357 PlasticityTimeConstants {
1358 tau_fast: 10.0,
1359 tau_slow: 20.0,
1360 stdp_window: 50.0,
1361 tau_homeostatic: 500.0,
1362 tau_calcium: 25.0,
1363 },
1364 PlasticityLearningRates {
1365 potentiation_rate: 0.005,
1366 depression_rate: 0.0025,
1367 homeostatic_rate: 0.0005,
1368 metaplastic_rate: 0.00005,
1369 intrinsic_rate: 0.0005,
1370 },
1371 PlasticityThresholds {
1372 ltp_threshold: 0.5,
1373 ltd_threshold: 0.3,
1374 target_activity: 0.4,
1375 metaplasticity_threshold: 0.7,
1376 saturation_threshold: 0.9,
1377 },
1378 ),
1379 };
1380
1381 Self {
1382 mechanism_type,
1383 time_constants,
1384 learning_rates,
1385 thresholds,
1386 enabled: true,
1387 weight_scaling: 1.0,
1388 }
1389 }
1390}
1391
1392impl HomeostaticSystem {
1393 pub fn new(num_neurons: usize) -> Self {
1395 Self {
1396 target_firing_rates: Array1::from_elem(num_neurons, 0.5),
1397 current_firing_rates: Array1::zeros(num_neurons),
1398 time_constants: Array1::from_elem(num_neurons, 1000.0),
1399 mechanisms: vec![
1400 HomeostaticMechanism::SynapticScaling,
1401 HomeostaticMechanism::IntrinsicExcitability,
1402 ],
1403 adaptation_rates: Array1::from_elem(num_neurons, 0.001),
1404 activity_history: VecDeque::new(),
1405 history_window: 100,
1406 }
1407 }
1408}
1409
1410impl Default for MetaplasticityRules {
1411 fn default() -> Self {
1412 Self::new()
1413 }
1414}
1415
1416impl MetaplasticityRules {
1417 pub fn new() -> Self {
1419 Self {
1420 learning_rate_adaptation: LearningRateAdaptation {
1421 base_rate: 0.01,
1422 adaptation_factor: 0.1,
1423 performance_history: VecDeque::new(),
1424 adaptation_threshold: 0.1,
1425 max_rate: 0.1,
1426 min_rate: 1e-6,
1427 },
1428 threshold_adaptation: ThresholdAdaptation {
1429 adaptive_thresholds: Array1::from_elem(10, 0.5),
1430 update_rates: Array1::from_elem(10, 0.001),
1431 target_activations: Array1::from_elem(10, 0.5),
1432 threshold_bounds: vec![(0.1, 2.0); 10],
1433 },
1434 consolidation_rules: ConsolidationRules {
1435 time_windows: vec![100.0, 1000.0, 10000.0],
1436 consolidation_strengths: Array1::from_elem(3, 1.0),
1437 replay_enabled: true,
1438 replay_patterns: Vec::new(),
1439 systems_consolidation: true,
1440 },
1441 forgetting_protection: ForgettingProtectionRules {
1442 ewc_enabled: false,
1443 fisher_information: Array2::zeros((10, 10)),
1444 synaptic_intelligence: false,
1445 importance_weights: Array1::zeros(10),
1446 protection_strength: 1.0,
1447 },
1448 }
1449 }
1450}
1451
1452impl NeuromodulationSystem {
1453 pub fn new(num_neurons: usize) -> Self {
1455 Self {
1456 dopamine_levels: Array1::from_elem(num_neurons, 0.5),
1457 serotonin_levels: Array1::from_elem(num_neurons, 0.5),
1458 acetylcholine_levels: Array1::from_elem(num_neurons, 0.5),
1459 noradrenaline_levels: Array1::from_elem(num_neurons, 0.5),
1460 modulation_effects: NeuromodulationEffects {
1461 learning_rate_modulation: Array1::from_elem(num_neurons, 1.0),
1462 threshold_modulation: Array1::from_elem(num_neurons, 1.0),
1463 excitability_modulation: Array1::from_elem(num_neurons, 1.0),
1464 attention_modulation: Array1::from_elem(num_neurons, 1.0),
1465 },
1466 release_patterns: NeuromodulatorReleasePatterns {
1467 phasic_dopamine: Vec::new(),
1468 tonic_serotonin: 0.5,
1469 cholinergic_attention: Array1::from_elem(num_neurons, 0.5),
1470 stress_noradrenaline: 0.3,
1471 },
1472 }
1473 }
1474}
1475
1476impl Default for LearningHistory {
1477 fn default() -> Self {
1478 Self::new()
1479 }
1480}
1481
1482impl LearningHistory {
1483 pub fn new() -> Self {
1485 Self {
1486 weight_changes: VecDeque::new(),
1487 performance_metrics: VecDeque::new(),
1488 plasticity_events: VecDeque::new(),
1489 consolidation_events: VecDeque::new(),
1490 max_history_length: 1000,
1491 }
1492 }
1493}
1494
1495#[cfg(test)]
1496mod tests {
1497 use super::*;
1498 use scirs2_core::ndarray::array;
1499
1500 #[test]
1501 fn test_advanced_memristive_learning_creation() {
1502 let learning_system =
1503 AdvancedMemristiveLearning::new(8, 4, MemristiveDeviceType::TitaniumDioxide);
1504 assert_eq!(learning_system.crossbar_dimensions(), (8, 4));
1505 assert_eq!(learning_system.plasticity_mechanisms.len(), 3);
1506 assert!(learning_system.forgetting_protection);
1507 }
1508
1509 #[test]
1510 fn test_memristive_device_types() {
1511 let tio2_system =
1512 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::TitaniumDioxide);
1513 let hfo2_system = AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::HafniumOxide);
1514 let pcm_system = AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange);
1515
1516 assert!(matches!(
1517 tio2_system.device_type(),
1518 MemristiveDeviceType::TitaniumDioxide
1519 ));
1520 assert!(matches!(
1521 hfo2_system.device_type(),
1522 MemristiveDeviceType::HafniumOxide
1523 ));
1524 assert!(matches!(
1525 pcm_system.device_type(),
1526 MemristiveDeviceType::PhaseChange
1527 ));
1528 }
1529
1530 #[test]
1531 fn test_plasticity_mechanism_creation() {
1532 let stdp_mechanism = PlasticityMechanism::new(PlasticityType::STDP);
1533 assert!(stdp_mechanism.enabled);
1534 assert!(matches!(
1535 stdp_mechanism.mechanism_type,
1536 PlasticityType::STDP
1537 ));
1538 assert!(stdp_mechanism.learning_rates.potentiation_rate > 0.0);
1539 }
1540
1541 #[test]
1542 fn test_homeostatic_regulation() {
1543 let target_rates = Array1::from_vec(vec![0.3, 0.7, 0.5, 0.8]);
1544 let learning_system =
1545 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::HafniumOxide)
1546 .with_homeostatic_regulation(target_rates.clone());
1547 assert_eq!(
1548 learning_system.homeostatic_system.target_firing_rates,
1549 target_rates
1550 );
1551 }
1552
1553 #[test]
1554 fn test_forgetting_protection() {
1555 let learning_system =
1556 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange)
1557 .with_forgetting_protection(true);
1558 assert!(learning_system.forgetting_protection);
1559 assert!(
1560 learning_system
1561 .metaplasticity
1562 .forgetting_protection
1563 .ewc_enabled
1564 );
1565
1566 let no_protection_system =
1567 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange)
1568 .with_forgetting_protection(false);
1569 assert!(!no_protection_system.forgetting_protection);
1570 assert!(
1571 !no_protection_system
1572 .metaplasticity
1573 .forgetting_protection
1574 .ewc_enabled
1575 );
1576 }
1577
1578 #[tokio::test]
1579 async fn test_memristive_forward_pass() {
1580 let learning_system =
1581 AdvancedMemristiveLearning::new(3, 2, MemristiveDeviceType::TitaniumDioxide);
1582 let input = array![0.5, 0.8, 0.3];
1583 let result = learning_system.forward_pass(&input.view()).await;
1584 assert!(result.is_ok());
1585 let output = result.unwrap();
1586 assert!((0.0..=1.0).contains(&output)); }
1588
1589 #[test]
1590 fn test_device_nonlinearity() {
1591 let learning_system =
1592 AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::TitaniumDioxide);
1593
1594 let linear_current = 0.1;
1596 let nonlinear_current = learning_system.apply_device_nonlinearity(linear_current, 0, 0);
1597 assert!(nonlinear_current.is_finite());
1598
1599 let hfo2_system = AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::HafniumOxide);
1601 let hfo2_output = hfo2_system.apply_device_nonlinearity(linear_current, 0, 0);
1602 assert!(hfo2_output.is_finite());
1603
1604 let pcm_system = AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::PhaseChange);
1606 let pcm_output = pcm_system.apply_device_nonlinearity(linear_current, 0, 0);
1607 assert!(pcm_output.is_finite());
1608 }
1609
1610 #[tokio::test]
1611 async fn test_memristive_training() {
1612 let mut learning_system =
1613 AdvancedMemristiveLearning::new(2, 1, MemristiveDeviceType::TitaniumDioxide);
1614
1615 let spatial_data = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
1616 let target_outputs = array![0.0, 1.0, 1.0, 0.0]; let result = learning_system
1619 .train_spatial_data(&spatial_data.view(), &target_outputs.view(), 5)
1620 .await;
1621
1622 assert!(result.is_ok());
1623 let training_result = result.unwrap();
1624 assert_eq!(training_result.training_metrics.len(), 5);
1625 assert!(!training_result.final_weights.is_empty());
1626 }
1627
1628 #[test]
1629 fn test_memristive_crossbar_creation() {
1630 let crossbar = MemristiveCrossbar::new(4, 3, MemristiveDeviceType::SilverSulfide);
1631 assert_eq!(crossbar.dimensions, (4, 3));
1632 assert_eq!(crossbar.conductances.shape(), &[4, 3]);
1633 assert_eq!(crossbar.resistances.shape(), &[4, 3]);
1634 assert_eq!(crossbar.switching_thresholds.shape(), &[4, 3]);
1635 assert!(matches!(
1636 crossbar.device_type,
1637 MemristiveDeviceType::SilverSulfide
1638 ));
1639
1640 for i in 0..4 {
1642 for j in 0..3 {
1643 let conductance = crossbar.conductances[[i, j]];
1644 let resistance = crossbar.resistances[[i, j]];
1645 if conductance > 1e-12 {
1646 assert!((resistance * conductance - 1.0).abs() < 1e-6);
1647 }
1648 }
1649 }
1650 }
1651
1652 #[test]
1653 fn test_device_aging_and_variability() {
1654 let mut learning_system =
1655 AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::Organic);
1656
1657 let initial_conductance = learning_system.crossbar_array.conductances[[0, 0]];
1659
1660 learning_system.apply_device_aging(0, 0);
1662 let aged_conductance = learning_system.crossbar_array.conductances[[0, 0]];
1663
1664 assert!(aged_conductance <= initial_conductance);
1666
1667 let pre_variability = learning_system.crossbar_array.conductances[[0, 0]];
1669 learning_system.apply_device_variability(0, 0);
1670 let post_variability = learning_system.crossbar_array.conductances[[0, 0]];
1671
1672 assert!((0.0..=1.0).contains(&post_variability));
1674 }
1675
1676 #[test]
1677 fn test_plasticity_mechanisms_configuration() {
1678 let learning_system =
1679 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::TitaniumDioxide)
1680 .enable_plasticity(PlasticityType::CalciumDependent)
1681 .enable_plasticity(PlasticityType::VoltageDependent);
1682
1683 let enabled_mechanisms: Vec<_> = learning_system
1685 .plasticity_mechanisms
1686 .iter()
1687 .filter(|m| m.enabled)
1688 .map(|m| &m.mechanism_type)
1689 .collect();
1690
1691 assert!(!enabled_mechanisms.is_empty());
1692 }
1693
1694 #[test]
1695 fn test_learning_history_tracking() {
1696 let learning_system =
1697 AdvancedMemristiveLearning::new(3, 3, MemristiveDeviceType::MagneticTunnelJunction);
1698
1699 let history = learning_system.learning_history();
1700 assert_eq!(history.max_history_length, 1000);
1701 assert!(history.weight_changes.is_empty());
1702 assert!(history.performance_metrics.is_empty());
1703 }
1704}