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)
703 .await?;
704 }
705 PlasticityType::HomeostaticScaling => {
706 self.apply_homeostatic_scaling(input, output, &mechanism)
707 .await?;
708 }
709 PlasticityType::CalciumDependent => {
710 self.apply_calcium_dependent_plasticity(input, output, target, &mechanism)
711 .await?;
712 }
713 PlasticityType::VoltageDependent => {
714 self.apply_voltage_dependent_plasticity(input, error, &mechanism)
715 .await?;
716 }
717 _ => {
718 self.apply_error_based_plasticity(input, error, &mechanism)
720 .await?;
721 }
722 }
723 }
724 }
725
726 Ok(())
727 }
728
729 async fn apply_stdp_plasticity(
731 &mut self,
732 input: &ArrayView1<'_, f64>,
733 output: f64,
734 mechanism: &PlasticityMechanism,
735 ) -> SpatialResult<()> {
736 let tau_plus = mechanism.time_constants.tau_fast;
737 let tau_minus = mechanism.time_constants.tau_slow;
738 let a_plus = mechanism.learning_rates.potentiation_rate;
739 let a_minus = mechanism.learning_rates.depression_rate;
740
741 for (i, &input_val) in input.iter().enumerate() {
743 if i < self.crossbar_array.dimensions.0 {
744 for j in 0..self.crossbar_array.dimensions.1 {
745 let dt = if input_val > 0.5 && output > 0.5 {
747 1.0 } else if input_val <= 0.5 && output > 0.5 {
749 -1.0 } else {
751 0.0 };
753
754 let weight_change = if dt > 0.0 {
755 a_plus * (-dt / tau_plus).exp()
756 } else if dt < 0.0 {
757 -a_minus * (dt / tau_minus).exp()
758 } else {
759 0.0
760 };
761
762 self.crossbar_array.conductances[[i, j]] +=
763 weight_change * mechanism.weight_scaling;
764 self.crossbar_array.conductances[[i, j]] =
765 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
766 }
767 }
768 }
769
770 Ok(())
771 }
772
773 async fn apply_homeostatic_scaling(
775 &mut self,
776 _input: &ArrayView1<'_, f64>,
777 output: f64,
778 mechanism: &PlasticityMechanism,
779 ) -> SpatialResult<()> {
780 let target_activity = mechanism.thresholds.target_activity;
781 let scaling_rate = mechanism.learning_rates.homeostatic_rate;
782
783 let activity_error = output - target_activity;
785 let scaling_factor = 1.0 - scaling_rate * activity_error;
786
787 for i in 0..self.crossbar_array.dimensions.0 {
789 for j in 0..self.crossbar_array.dimensions.1 {
790 self.crossbar_array.conductances[[i, j]] *= scaling_factor;
791 self.crossbar_array.conductances[[i, j]] =
792 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
793 }
794 }
795
796 Ok(())
797 }
798
799 async fn apply_calcium_dependent_plasticity(
801 &mut self,
802 input: &ArrayView1<'_, f64>,
803 output: f64,
804 target: f64,
805 mechanism: &PlasticityMechanism,
806 ) -> SpatialResult<()> {
807 let calcium_level = Self::compute_calcium_level(input, output, target);
809
810 let ltp_threshold = mechanism.thresholds.ltp_threshold;
811 let ltd_threshold = mechanism.thresholds.ltd_threshold;
812
813 for (i, &input_val) in input.iter().enumerate() {
814 if i < self.crossbar_array.dimensions.0 {
815 for j in 0..self.crossbar_array.dimensions.1 {
816 let local_calcium = calcium_level * input_val;
817
818 let weight_change = if local_calcium > ltp_threshold {
819 mechanism.learning_rates.potentiation_rate * (local_calcium - ltp_threshold)
820 } else if local_calcium < ltd_threshold {
821 -mechanism.learning_rates.depression_rate * (ltd_threshold - local_calcium)
822 } else {
823 0.0
824 };
825
826 self.crossbar_array.conductances[[i, j]] += weight_change;
827 self.crossbar_array.conductances[[i, j]] =
828 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
829 }
830 }
831 }
832
833 Ok(())
834 }
835
836 async fn apply_voltage_dependent_plasticity(
838 &mut self,
839 input: &ArrayView1<'_, f64>,
840 error: f64,
841 mechanism: &PlasticityMechanism,
842 ) -> SpatialResult<()> {
843 let voltage_threshold = mechanism.thresholds.ltd_threshold;
844
845 for (i, &input_val) in input.iter().enumerate() {
846 if i < self.crossbar_array.dimensions.0 {
847 for j in 0..self.crossbar_array.dimensions.1 {
848 let local_voltage = input_val * error.abs();
849
850 if local_voltage > voltage_threshold {
851 let weight_change = mechanism.learning_rates.potentiation_rate
852 * (local_voltage - voltage_threshold)
853 * error.signum();
854
855 self.crossbar_array.conductances[[i, j]] += weight_change;
856 self.crossbar_array.conductances[[i, j]] =
857 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
858 }
859 }
860 }
861 }
862
863 Ok(())
864 }
865
866 async fn apply_error_based_plasticity(
868 &mut self,
869 input: &ArrayView1<'_, f64>,
870 error: f64,
871 mechanism: &PlasticityMechanism,
872 ) -> SpatialResult<()> {
873 let learning_rate = mechanism.learning_rates.potentiation_rate;
874
875 for (i, &input_val) in input.iter().enumerate() {
876 if i < self.crossbar_array.dimensions.0 {
877 for j in 0..self.crossbar_array.dimensions.1 {
878 let weight_change = learning_rate * error * input_val;
879
880 self.crossbar_array.conductances[[i, j]] += weight_change;
881 self.crossbar_array.conductances[[i, j]] =
882 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
883 }
884 }
885 }
886
887 Ok(())
888 }
889
890 fn compute_calcium_level(input: &ArrayView1<'_, f64>, output: f64, target: f64) -> f64 {
892 let input_activity = input.iter().map(|&x| x.max(0.0)).sum::<f64>();
893 let output_activity = output.max(0.0);
894 let target_activity = target.max(0.0);
895
896 (input_activity * 0.3 + output_activity * 0.4 + target_activity * 0.3).min(1.0)
898 }
899
900 async fn update_memristive_devices(
902 &mut self,
903 input: &ArrayView1<'_, f64>,
904 _error: f64,
905 ) -> SpatialResult<()> {
906 for (i, &input_val) in input.iter().enumerate() {
907 if i < self.crossbar_array.dimensions.0 {
908 for j in 0..self.crossbar_array.dimensions.1 {
909 let conductance = self.crossbar_array.conductances[[i, j]];
911 self.crossbar_array.resistances[[i, j]] = if conductance > 1e-12 {
912 1.0 / conductance
913 } else {
914 1e12
915 };
916
917 if input_val > 0.1 {
919 self.crossbar_array.endurance_cycles[[i, j]] += 1;
920 }
921
922 self.apply_device_aging(i, j);
924
925 self.apply_device_variability(i, j);
927 }
928 }
929 }
930
931 Ok(())
932 }
933
934 fn apply_device_aging(&mut self, row: usize, col: usize) {
936 let cycles = self.crossbar_array.endurance_cycles[[row, col]];
937 let aging_factor = 1.0 - (cycles as f64) * 1e-8; self.crossbar_array.conductances[[row, col]] *= aging_factor.max(0.1);
940 }
941
942 fn apply_device_variability(&mut self, row: usize, col: usize) {
944 let variability = self.crossbar_array.device_variability[[row, col]];
945 let mut rng = rand::rng();
946 let noise = (rng.gen_range(0.0..1.0) - 0.5) * variability;
947
948 self.crossbar_array.conductances[[row, col]] += noise;
949 self.crossbar_array.conductances[[row, col]] =
950 self.crossbar_array.conductances[[row, col]].clamp(0.0, 1.0);
951 }
952
953 async fn apply_homeostatic_regulation(&mut self) -> SpatialResult<()> {
955 let current_rates = self.compute_current_firing_rates();
957 self.homeostatic_system
958 .activity_history
959 .push_back(current_rates);
960
961 if self.homeostatic_system.activity_history.len() > self.homeostatic_system.history_window {
963 self.homeostatic_system.activity_history.pop_front();
964 }
965
966 self.apply_synaptic_scaling().await?;
968 self.apply_intrinsic_excitability_adjustment().await?;
969
970 Ok(())
971 }
972
973 fn compute_current_firing_rates(&self) -> Array1<f64> {
975 let mut rates = Array1::zeros(self.crossbar_array.dimensions.1);
977
978 for j in 0..self.crossbar_array.dimensions.1 {
979 let total_conductance: f64 = (0..self.crossbar_array.dimensions.0)
980 .map(|i| self.crossbar_array.conductances[[i, j]])
981 .sum();
982 rates[j] = Self::sigmoid(total_conductance);
983 }
984
985 rates
986 }
987
988 async fn apply_synaptic_scaling(&mut self) -> SpatialResult<()> {
990 let current_rates = self.compute_current_firing_rates();
991
992 for j in 0..self.crossbar_array.dimensions.1 {
993 let target_rate = self.homeostatic_system.target_firing_rates[j];
994 let current_rate = current_rates[j];
995 let adaptation_rate = self.homeostatic_system.adaptation_rates[j];
996
997 let scaling_factor = 1.0 + adaptation_rate * (target_rate - current_rate);
998
999 for i in 0..self.crossbar_array.dimensions.0 {
1001 self.crossbar_array.conductances[[i, j]] *= scaling_factor;
1002 self.crossbar_array.conductances[[i, j]] =
1003 self.crossbar_array.conductances[[i, j]].clamp(0.0, 1.0);
1004 }
1005 }
1006
1007 Ok(())
1008 }
1009
1010 async fn apply_intrinsic_excitability_adjustment(&mut self) -> SpatialResult<()> {
1012 let current_rates = self.compute_current_firing_rates();
1014
1015 for j in 0..self.crossbar_array.dimensions.1 {
1016 let target_rate = self.homeostatic_system.target_firing_rates[j];
1017 let current_rate = current_rates[j];
1018 let adaptation_rate = self.homeostatic_system.adaptation_rates[j];
1019
1020 let threshold_adjustment = adaptation_rate * (current_rate - target_rate);
1021
1022 for i in 0..self.crossbar_array.dimensions.0 {
1023 self.crossbar_array.switching_thresholds[[i, j]] += threshold_adjustment;
1024 self.crossbar_array.switching_thresholds[[i, j]] =
1025 self.crossbar_array.switching_thresholds[[i, j]].clamp(0.1, 2.0);
1026 }
1027 }
1028
1029 Ok(())
1030 }
1031
1032 async fn apply_metaplasticity_updates(
1034 &mut self,
1035 metrics: &PerformanceMetrics,
1036 ) -> SpatialResult<()> {
1037 self.metaplasticity
1039 .learning_rate_adaptation
1040 .performance_history
1041 .push_back(metrics.accuracy);
1042
1043 if self
1044 .metaplasticity
1045 .learning_rate_adaptation
1046 .performance_history
1047 .len()
1048 > 100
1049 {
1050 self.metaplasticity
1051 .learning_rate_adaptation
1052 .performance_history
1053 .pop_front();
1054 }
1055
1056 self.adapt_learning_rates(metrics).await?;
1058
1059 self.adapt_thresholds(metrics).await?;
1061
1062 if metrics.accuracy > 0.9 {
1064 self.trigger_memory_consolidation().await?;
1065 }
1066
1067 Ok(())
1068 }
1069
1070 async fn adapt_learning_rates(&mut self, _metrics: &PerformanceMetrics) -> SpatialResult<()> {
1072 let performance_trend = self.compute_performance_trend();
1073
1074 for mechanism in &mut self.plasticity_mechanisms {
1075 if performance_trend > 0.0 {
1076 mechanism.learning_rates.potentiation_rate *= 1.01;
1078 mechanism.learning_rates.depression_rate *= 1.01;
1079 } else {
1080 mechanism.learning_rates.potentiation_rate *= 0.99;
1082 mechanism.learning_rates.depression_rate *= 0.99;
1083 }
1084
1085 mechanism.learning_rates.potentiation_rate =
1087 mechanism.learning_rates.potentiation_rate.clamp(1e-6, 0.1);
1088 mechanism.learning_rates.depression_rate =
1089 mechanism.learning_rates.depression_rate.clamp(1e-6, 0.1);
1090 }
1091
1092 Ok(())
1093 }
1094
1095 fn compute_performance_trend(&self) -> f64 {
1097 let history = &self
1098 .metaplasticity
1099 .learning_rate_adaptation
1100 .performance_history;
1101
1102 if history.len() < 10 {
1103 return 0.0;
1104 }
1105
1106 let recent_performance: f64 = history.iter().rev().take(5).sum::<f64>() / 5.0;
1107 let older_performance: f64 = history.iter().rev().skip(5).take(5).sum::<f64>() / 5.0;
1108
1109 recent_performance - older_performance
1110 }
1111
1112 async fn adapt_thresholds(&mut self, metrics: &PerformanceMetrics) -> SpatialResult<()> {
1114 for mechanism in &mut self.plasticity_mechanisms {
1116 if metrics.learning_speed > 1.0 {
1117 mechanism.thresholds.ltp_threshold *= 1.001;
1119 mechanism.thresholds.ltd_threshold *= 1.001;
1120 } else {
1121 mechanism.thresholds.ltp_threshold *= 0.999;
1123 mechanism.thresholds.ltd_threshold *= 0.999;
1124 }
1125
1126 mechanism.thresholds.ltp_threshold = mechanism.thresholds.ltp_threshold.clamp(0.1, 2.0);
1128 mechanism.thresholds.ltd_threshold = mechanism.thresholds.ltd_threshold.clamp(0.1, 2.0);
1129 }
1130
1131 Ok(())
1132 }
1133
1134 async fn update_neuromodulation(&mut self, metrics: &PerformanceMetrics) -> SpatialResult<()> {
1136 let performance_change = metrics.accuracy - 0.5; self.neuromodulation
1139 .dopamine_levels
1140 .mapv_inplace(|x| x + 0.1 * performance_change);
1141
1142 let stability_change = metrics.stability - 0.5;
1144 self.neuromodulation
1145 .serotonin_levels
1146 .mapv_inplace(|x| x + 0.05 * stability_change);
1147
1148 self.neuromodulation
1150 .dopamine_levels
1151 .mapv_inplace(|x| x.clamp(0.0, 1.0));
1152 self.neuromodulation
1153 .serotonin_levels
1154 .mapv_inplace(|x| x.clamp(0.0, 1.0));
1155
1156 Ok(())
1157 }
1158
1159 async fn record_learning_history(
1161 &mut self,
1162 metrics: &PerformanceMetrics,
1163 _timestamp: f64,
1164 ) -> SpatialResult<()> {
1165 self.learning_history
1167 .performance_metrics
1168 .push_back(metrics.clone());
1169
1170 self.learning_history
1172 .weight_changes
1173 .push_back(self.crossbar_array.conductances.clone());
1174
1175 if self.learning_history.performance_metrics.len()
1177 > self.learning_history.max_history_length
1178 {
1179 self.learning_history.performance_metrics.pop_front();
1180 self.learning_history.weight_changes.pop_front();
1181 }
1182
1183 Ok(())
1184 }
1185
1186 fn should_trigger_consolidation(&self, epoch: usize) -> bool {
1188 epoch % 100 == 0
1190 || self
1191 .learning_history
1192 .performance_metrics
1193 .back()
1194 .map(|m| m.accuracy > 0.95)
1195 .unwrap_or(false)
1196 }
1197
1198 async fn trigger_memory_consolidation(&mut self) -> SpatialResult<()> {
1200 self.strengthen_important_connections().await?;
1202
1203 let consolidation_event = ConsolidationEvent {
1205 consolidation_type: ConsolidationType::SynapticConsolidation,
1206 patterns: vec![], strength: 1.0,
1208 timestamp: std::time::SystemTime::now()
1209 .duration_since(std::time::UNIX_EPOCH)
1210 .unwrap()
1211 .as_secs_f64(),
1212 };
1213
1214 self.learning_history
1215 .consolidation_events
1216 .push_back(consolidation_event);
1217
1218 Ok(())
1219 }
1220
1221 async fn strengthen_important_connections(&mut self) -> SpatialResult<()> {
1223 let mut importance_matrix = Array2::zeros(self.crossbar_array.dimensions);
1225
1226 for i in 0..self.crossbar_array.dimensions.0 {
1227 for j in 0..self.crossbar_array.dimensions.1 {
1228 let conductance = self.crossbar_array.conductances[[i, j]];
1229 let usage = self.crossbar_array.endurance_cycles[[i, j]] as f64;
1230
1231 importance_matrix[[i, j]] = conductance * (1.0 + 0.1 * usage.ln_1p());
1233 }
1234 }
1235
1236 let threshold = self.compute_importance_threshold(&importance_matrix, 0.8);
1238
1239 for i in 0..self.crossbar_array.dimensions.0 {
1240 for j in 0..self.crossbar_array.dimensions.1 {
1241 if importance_matrix[[i, j]] > threshold {
1242 self.crossbar_array.conductances[[i, j]] *= 1.05; self.crossbar_array.conductances[[i, j]] =
1244 self.crossbar_array.conductances[[i, j]].min(1.0);
1245 }
1246 }
1247 }
1248
1249 Ok(())
1250 }
1251
1252 fn compute_importance_threshold(
1254 &self,
1255 importance_matrix: &Array2<f64>,
1256 percentile: f64,
1257 ) -> f64 {
1258 let mut values: Vec<f64> = importance_matrix.iter().cloned().collect();
1259 values.sort_by(|a, b| a.partial_cmp(b).unwrap());
1260
1261 let index = (values.len() as f64 * percentile) as usize;
1262 values.get(index).cloned().unwrap_or(0.0)
1263 }
1264
1265 fn sigmoid(x: f64) -> f64 {
1267 1.0 / (1.0 + (-x).exp())
1268 }
1269
1270 fn compute_weight_stability(&self) -> f64 {
1271 let weight_variance = self.crossbar_array.conductances.var(0.0);
1273 1.0 / (1.0 + weight_variance)
1274 }
1275
1276 fn estimate_generalization(&self) -> f64 {
1277 0.8 }
1280
1281 pub fn crossbar_dimensions(&self) -> (usize, usize) {
1283 self.crossbar_array.dimensions
1284 }
1285
1286 pub fn device_type(&self) -> &MemristiveDeviceType {
1288 &self.crossbar_array.device_type
1289 }
1290
1291 pub fn conductances(&self) -> &Array2<f64> {
1293 &self.crossbar_array.conductances
1294 }
1295
1296 pub fn learning_history(&self) -> &LearningHistory {
1298 &self.learning_history
1299 }
1300}
1301
1302impl MemristiveCrossbar {
1303 pub fn new(rows: usize, cols: usize, device_type: MemristiveDeviceType) -> Self {
1305 let mut rng = rand::rng();
1306 let conductances = Array2::from_shape_fn((rows, cols), |_| rng.gen_range(0.0..0.1));
1307 let resistances = conductances.mapv(|g| if g > 1e-12 { 1.0 / g } else { 1e12 });
1308 let switching_thresholds = Array2::from_elem((rows, cols), 0.5);
1309 let retention_times = Array2::from_elem((rows, cols), 1e6);
1310 let endurance_cycles = Array2::zeros((rows, cols));
1311 let programming_voltages = Array2::from_elem((rows, cols), 1.0);
1312 let temperature_coefficients = Array2::from_elem((rows, cols), 0.01);
1313 let device_variability = Array2::from_shape_fn((rows, cols), |_| rng.gen_range(0.0..0.01));
1314
1315 Self {
1316 conductances,
1317 resistances,
1318 switching_thresholds,
1319 retention_times,
1320 endurance_cycles,
1321 programming_voltages,
1322 temperature_coefficients,
1323 device_variability,
1324 dimensions: (rows, cols),
1325 device_type,
1326 }
1327 }
1328}
1329
1330impl PlasticityMechanism {
1331 pub fn new(mechanism_type: PlasticityType) -> Self {
1333 let (time_constants, learning_rates, thresholds) = match mechanism_type {
1334 PlasticityType::STDP => (
1335 PlasticityTimeConstants {
1336 tau_fast: 20.0,
1337 tau_slow: 40.0,
1338 stdp_window: 100.0,
1339 tau_homeostatic: 1000.0,
1340 tau_calcium: 50.0,
1341 },
1342 PlasticityLearningRates {
1343 potentiation_rate: 0.01,
1344 depression_rate: 0.005,
1345 homeostatic_rate: 0.001,
1346 metaplastic_rate: 0.0001,
1347 intrinsic_rate: 0.001,
1348 },
1349 PlasticityThresholds {
1350 ltp_threshold: 0.6,
1351 ltd_threshold: 0.4,
1352 target_activity: 0.5,
1353 metaplasticity_threshold: 0.8,
1354 saturation_threshold: 0.95,
1355 },
1356 ),
1357 _ => (
1358 PlasticityTimeConstants {
1359 tau_fast: 10.0,
1360 tau_slow: 20.0,
1361 stdp_window: 50.0,
1362 tau_homeostatic: 500.0,
1363 tau_calcium: 25.0,
1364 },
1365 PlasticityLearningRates {
1366 potentiation_rate: 0.005,
1367 depression_rate: 0.0025,
1368 homeostatic_rate: 0.0005,
1369 metaplastic_rate: 0.00005,
1370 intrinsic_rate: 0.0005,
1371 },
1372 PlasticityThresholds {
1373 ltp_threshold: 0.5,
1374 ltd_threshold: 0.3,
1375 target_activity: 0.4,
1376 metaplasticity_threshold: 0.7,
1377 saturation_threshold: 0.9,
1378 },
1379 ),
1380 };
1381
1382 Self {
1383 mechanism_type,
1384 time_constants,
1385 learning_rates,
1386 thresholds,
1387 enabled: true,
1388 weight_scaling: 1.0,
1389 }
1390 }
1391}
1392
1393impl HomeostaticSystem {
1394 pub fn new(num_neurons: usize) -> Self {
1396 Self {
1397 target_firing_rates: Array1::from_elem(num_neurons, 0.5),
1398 current_firing_rates: Array1::zeros(num_neurons),
1399 time_constants: Array1::from_elem(num_neurons, 1000.0),
1400 mechanisms: vec![
1401 HomeostaticMechanism::SynapticScaling,
1402 HomeostaticMechanism::IntrinsicExcitability,
1403 ],
1404 adaptation_rates: Array1::from_elem(num_neurons, 0.001),
1405 activity_history: VecDeque::new(),
1406 history_window: 100,
1407 }
1408 }
1409}
1410
1411impl Default for MetaplasticityRules {
1412 fn default() -> Self {
1413 Self::new()
1414 }
1415}
1416
1417impl MetaplasticityRules {
1418 pub fn new() -> Self {
1420 Self {
1421 learning_rate_adaptation: LearningRateAdaptation {
1422 base_rate: 0.01,
1423 adaptation_factor: 0.1,
1424 performance_history: VecDeque::new(),
1425 adaptation_threshold: 0.1,
1426 max_rate: 0.1,
1427 min_rate: 1e-6,
1428 },
1429 threshold_adaptation: ThresholdAdaptation {
1430 adaptive_thresholds: Array1::from_elem(10, 0.5),
1431 update_rates: Array1::from_elem(10, 0.001),
1432 target_activations: Array1::from_elem(10, 0.5),
1433 threshold_bounds: vec![(0.1, 2.0); 10],
1434 },
1435 consolidation_rules: ConsolidationRules {
1436 time_windows: vec![100.0, 1000.0, 10000.0],
1437 consolidation_strengths: Array1::from_elem(3, 1.0),
1438 replay_enabled: true,
1439 replay_patterns: Vec::new(),
1440 systems_consolidation: true,
1441 },
1442 forgetting_protection: ForgettingProtectionRules {
1443 ewc_enabled: false,
1444 fisher_information: Array2::zeros((10, 10)),
1445 synaptic_intelligence: false,
1446 importance_weights: Array1::zeros(10),
1447 protection_strength: 1.0,
1448 },
1449 }
1450 }
1451}
1452
1453impl NeuromodulationSystem {
1454 pub fn new(num_neurons: usize) -> Self {
1456 Self {
1457 dopamine_levels: Array1::from_elem(num_neurons, 0.5),
1458 serotonin_levels: Array1::from_elem(num_neurons, 0.5),
1459 acetylcholine_levels: Array1::from_elem(num_neurons, 0.5),
1460 noradrenaline_levels: Array1::from_elem(num_neurons, 0.5),
1461 modulation_effects: NeuromodulationEffects {
1462 learning_rate_modulation: Array1::from_elem(num_neurons, 1.0),
1463 threshold_modulation: Array1::from_elem(num_neurons, 1.0),
1464 excitability_modulation: Array1::from_elem(num_neurons, 1.0),
1465 attention_modulation: Array1::from_elem(num_neurons, 1.0),
1466 },
1467 release_patterns: NeuromodulatorReleasePatterns {
1468 phasic_dopamine: Vec::new(),
1469 tonic_serotonin: 0.5,
1470 cholinergic_attention: Array1::from_elem(num_neurons, 0.5),
1471 stress_noradrenaline: 0.3,
1472 },
1473 }
1474 }
1475}
1476
1477impl Default for LearningHistory {
1478 fn default() -> Self {
1479 Self::new()
1480 }
1481}
1482
1483impl LearningHistory {
1484 pub fn new() -> Self {
1486 Self {
1487 weight_changes: VecDeque::new(),
1488 performance_metrics: VecDeque::new(),
1489 plasticity_events: VecDeque::new(),
1490 consolidation_events: VecDeque::new(),
1491 max_history_length: 1000,
1492 }
1493 }
1494}
1495
1496#[cfg(test)]
1497mod tests {
1498 use super::*;
1499 use scirs2_core::ndarray::array;
1500
1501 #[test]
1502 fn test_advanced_memristive_learning_creation() {
1503 let learning_system =
1504 AdvancedMemristiveLearning::new(8, 4, MemristiveDeviceType::TitaniumDioxide);
1505 assert_eq!(learning_system.crossbar_dimensions(), (8, 4));
1506 assert_eq!(learning_system.plasticity_mechanisms.len(), 3);
1507 assert!(learning_system.forgetting_protection);
1508 }
1509
1510 #[test]
1511 fn test_memristive_device_types() {
1512 let tio2_system =
1513 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::TitaniumDioxide);
1514 let hfo2_system = AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::HafniumOxide);
1515 let pcm_system = AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange);
1516
1517 assert!(matches!(
1518 tio2_system.device_type(),
1519 MemristiveDeviceType::TitaniumDioxide
1520 ));
1521 assert!(matches!(
1522 hfo2_system.device_type(),
1523 MemristiveDeviceType::HafniumOxide
1524 ));
1525 assert!(matches!(
1526 pcm_system.device_type(),
1527 MemristiveDeviceType::PhaseChange
1528 ));
1529 }
1530
1531 #[test]
1532 fn test_plasticity_mechanism_creation() {
1533 let stdp_mechanism = PlasticityMechanism::new(PlasticityType::STDP);
1534 assert!(stdp_mechanism.enabled);
1535 assert!(matches!(
1536 stdp_mechanism.mechanism_type,
1537 PlasticityType::STDP
1538 ));
1539 assert!(stdp_mechanism.learning_rates.potentiation_rate > 0.0);
1540 }
1541
1542 #[test]
1543 fn test_homeostatic_regulation() {
1544 let target_rates = Array1::from_vec(vec![0.3, 0.7, 0.5, 0.8]);
1545 let learning_system =
1546 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::HafniumOxide)
1547 .with_homeostatic_regulation(target_rates.clone());
1548 assert_eq!(
1549 learning_system.homeostatic_system.target_firing_rates,
1550 target_rates
1551 );
1552 }
1553
1554 #[test]
1555 fn test_forgetting_protection() {
1556 let learning_system =
1557 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange)
1558 .with_forgetting_protection(true);
1559 assert!(learning_system.forgetting_protection);
1560 assert!(
1561 learning_system
1562 .metaplasticity
1563 .forgetting_protection
1564 .ewc_enabled
1565 );
1566
1567 let no_protection_system =
1568 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::PhaseChange)
1569 .with_forgetting_protection(false);
1570 assert!(!no_protection_system.forgetting_protection);
1571 assert!(
1572 !no_protection_system
1573 .metaplasticity
1574 .forgetting_protection
1575 .ewc_enabled
1576 );
1577 }
1578
1579 #[tokio::test]
1580 async fn test_memristive_forward_pass() {
1581 let learning_system =
1582 AdvancedMemristiveLearning::new(3, 2, MemristiveDeviceType::TitaniumDioxide);
1583 let input = array![0.5, 0.8, 0.3];
1584 let result = learning_system.forward_pass(&input.view()).await;
1585 assert!(result.is_ok());
1586 let output = result.unwrap();
1587 assert!(output >= 0.0 && output <= 1.0); }
1589
1590 #[test]
1591 fn test_device_nonlinearity() {
1592 let learning_system =
1593 AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::TitaniumDioxide);
1594
1595 let linear_current = 0.1;
1597 let nonlinear_current = learning_system.apply_device_nonlinearity(linear_current, 0, 0);
1598 assert!(nonlinear_current.is_finite());
1599
1600 let hfo2_system = AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::HafniumOxide);
1602 let hfo2_output = hfo2_system.apply_device_nonlinearity(linear_current, 0, 0);
1603 assert!(hfo2_output.is_finite());
1604
1605 let pcm_system = AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::PhaseChange);
1607 let pcm_output = pcm_system.apply_device_nonlinearity(linear_current, 0, 0);
1608 assert!(pcm_output.is_finite());
1609 }
1610
1611 #[tokio::test]
1612 async fn test_memristive_training() {
1613 let mut learning_system =
1614 AdvancedMemristiveLearning::new(2, 1, MemristiveDeviceType::TitaniumDioxide);
1615
1616 let spatial_data = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
1617 let target_outputs = array![0.0, 1.0, 1.0, 0.0]; let result = learning_system
1620 .train_spatial_data(&spatial_data.view(), &target_outputs.view(), 5)
1621 .await;
1622
1623 assert!(result.is_ok());
1624 let training_result = result.unwrap();
1625 assert_eq!(training_result.training_metrics.len(), 5);
1626 assert!(!training_result.final_weights.is_empty());
1627 }
1628
1629 #[test]
1630 fn test_memristive_crossbar_creation() {
1631 let crossbar = MemristiveCrossbar::new(4, 3, MemristiveDeviceType::SilverSulfide);
1632 assert_eq!(crossbar.dimensions, (4, 3));
1633 assert_eq!(crossbar.conductances.shape(), &[4, 3]);
1634 assert_eq!(crossbar.resistances.shape(), &[4, 3]);
1635 assert_eq!(crossbar.switching_thresholds.shape(), &[4, 3]);
1636 assert!(matches!(
1637 crossbar.device_type,
1638 MemristiveDeviceType::SilverSulfide
1639 ));
1640
1641 for i in 0..4 {
1643 for j in 0..3 {
1644 let conductance = crossbar.conductances[[i, j]];
1645 let resistance = crossbar.resistances[[i, j]];
1646 if conductance > 1e-12 {
1647 assert!((resistance * conductance - 1.0).abs() < 1e-6);
1648 }
1649 }
1650 }
1651 }
1652
1653 #[test]
1654 fn test_device_aging_and_variability() {
1655 let mut learning_system =
1656 AdvancedMemristiveLearning::new(2, 2, MemristiveDeviceType::Organic);
1657
1658 let initial_conductance = learning_system.crossbar_array.conductances[[0, 0]];
1660
1661 learning_system.apply_device_aging(0, 0);
1663 let aged_conductance = learning_system.crossbar_array.conductances[[0, 0]];
1664
1665 assert!(aged_conductance <= initial_conductance);
1667
1668 let pre_variability = learning_system.crossbar_array.conductances[[0, 0]];
1670 learning_system.apply_device_variability(0, 0);
1671 let post_variability = learning_system.crossbar_array.conductances[[0, 0]];
1672
1673 assert!(post_variability >= 0.0 && post_variability <= 1.0);
1675 }
1676
1677 #[test]
1678 fn test_plasticity_mechanisms_configuration() {
1679 let learning_system =
1680 AdvancedMemristiveLearning::new(4, 4, MemristiveDeviceType::TitaniumDioxide)
1681 .enable_plasticity(PlasticityType::CalciumDependent)
1682 .enable_plasticity(PlasticityType::VoltageDependent);
1683
1684 let enabled_mechanisms: Vec<_> = learning_system
1686 .plasticity_mechanisms
1687 .iter()
1688 .filter(|m| m.enabled)
1689 .map(|m| &m.mechanism_type)
1690 .collect();
1691
1692 assert!(!enabled_mechanisms.is_empty());
1693 }
1694
1695 #[test]
1696 fn test_learning_history_tracking() {
1697 let learning_system =
1698 AdvancedMemristiveLearning::new(3, 3, MemristiveDeviceType::MagneticTunnelJunction);
1699
1700 let history = learning_system.learning_history();
1701 assert_eq!(history.max_history_length, 1000);
1702 assert!(history.weight_changes.is_empty());
1703 assert!(history.performance_metrics.is_empty());
1704 }
1705}