1use std::collections::VecDeque;
8
9#[derive(Debug, Clone)]
33pub struct Synapse {
34 pub pre_neuron: usize,
36 pub post_neuron: usize,
38 weight: f64,
40 last_pre_spike: f64,
42 last_post_spike: f64,
44 stdp_rate: f64,
46 stdp_tau: f64,
48 min_weight: f64,
50 max_weight: f64,
52}
53
54impl Synapse {
55 pub fn new(pre_neuron: usize, post_neuron: usize, initial_weight: f64) -> Self {
65 Self {
66 pre_neuron,
67 post_neuron,
68 weight: initial_weight,
69 last_pre_spike: -1000.0,
70 last_post_spike: -1000.0,
71 stdp_rate: 0.01,
72 stdp_tau: 20.0,
73 min_weight: -2.0,
74 max_weight: 2.0,
75 }
76 }
77
78 pub fn with_stdp_params(
89 pre_neuron: usize,
90 post_neuron: usize,
91 initial_weight: f64,
92 stdp_rate: f64,
93 stdp_tau: f64,
94 min_weight: f64,
95 max_weight: f64,
96 ) -> Self {
97 Self {
98 pre_neuron,
99 post_neuron,
100 weight: initial_weight,
101 last_pre_spike: -1000.0,
102 last_post_spike: -1000.0,
103 stdp_rate,
104 stdp_tau,
105 min_weight,
106 max_weight,
107 }
108 }
109
110 pub fn update_stdp(&mut self, current_time: f64, pre_spiked: bool, post_spiked: bool) {
120 if pre_spiked {
121 self.last_pre_spike = current_time;
122 }
123 if post_spiked {
124 self.last_post_spike = current_time;
125 }
126
127 if pre_spiked && self.last_post_spike > current_time - 50.0 {
131 let dt = current_time - self.last_post_spike;
132 if dt > 0.0 {
133 let delta_w = -self.stdp_rate * (-dt / self.stdp_tau).exp();
134 self.weight += delta_w;
135 }
136 }
137
138 if post_spiked && self.last_pre_spike > current_time - 50.0 {
141 let dt = current_time - self.last_pre_spike;
142 if dt > 0.0 {
143 let delta_w = self.stdp_rate * (-dt / self.stdp_tau).exp();
144 self.weight += delta_w;
145 }
146 }
147
148 self.weight = self.weight.clamp(self.min_weight, self.max_weight);
150 }
151
152 pub fn synaptic_current(&self, pre_spike_strength: f64) -> f64 {
163 self.weight * pre_spike_strength
164 }
165
166 pub fn weight(&self) -> f64 {
168 self.weight
169 }
170
171 pub fn set_weight(&mut self, weight: f64) {
173 self.weight = weight.clamp(self.min_weight, self.max_weight);
174 }
175
176 pub fn pre_neuron(&self) -> usize {
178 self.pre_neuron
179 }
180
181 pub fn post_neuron(&self) -> usize {
183 self.post_neuron
184 }
185
186 pub fn stdp_rate(&self) -> f64 {
188 self.stdp_rate
189 }
190
191 pub fn set_stdp_rate(&mut self, rate: f64) {
193 self.stdp_rate = rate;
194 }
195
196 pub fn stdp_tau(&self) -> f64 {
198 self.stdp_tau
199 }
200
201 pub fn set_stdp_tau(&mut self, tau: f64) {
203 self.stdp_tau = tau;
204 }
205
206 pub fn last_pre_spike(&self) -> f64 {
208 self.last_pre_spike
209 }
210
211 pub fn last_post_spike(&self) -> f64 {
213 self.last_post_spike
214 }
215
216 pub fn weight_bounds(&self) -> (f64, f64) {
218 (self.min_weight, self.max_weight)
219 }
220
221 pub fn set_weight_bounds(&mut self, min_weight: f64, max_weight: f64) {
223 self.min_weight = min_weight;
224 self.max_weight = max_weight;
225 self.weight = self.weight.clamp(min_weight, max_weight);
227 }
228
229 pub fn is_excitatory(&self) -> bool {
231 self.weight > 0.0
232 }
233
234 pub fn is_inhibitory(&self) -> bool {
236 self.weight < 0.0
237 }
238
239 pub fn reset_spike_history(&mut self) {
241 self.last_pre_spike = -1000.0;
242 self.last_post_spike = -1000.0;
243 }
244
245 pub fn time_since_pre_spike(&self, current_time: f64) -> f64 {
247 current_time - self.last_pre_spike
248 }
249
250 pub fn time_since_post_spike(&self, current_time: f64) -> f64 {
252 current_time - self.last_post_spike
253 }
254}
255
256#[derive(Debug, Clone)]
262pub struct MetaplasticSynapse {
263 base_synapse: Synapse,
265 learning_history: VecDeque<f64>,
267 max_history_length: usize,
269 metaplasticity_tau: f64,
271 base_learning_rate: f64,
273}
274
275impl MetaplasticSynapse {
276 pub fn new(
284 pre_neuron: usize,
285 post_neuron: usize,
286 initial_weight: f64,
287 base_learning_rate: f64,
288 ) -> Self {
289 let mut base_synapse = Synapse::new(pre_neuron, post_neuron, initial_weight);
290 base_synapse.set_stdp_rate(base_learning_rate);
291
292 Self {
293 base_synapse,
294 learning_history: VecDeque::new(),
295 max_history_length: 100,
296 metaplasticity_tau: 100.0,
297 base_learning_rate,
298 }
299 }
300
301 pub fn update(&mut self, current_time: f64, pre_spiked: bool, post_spiked: bool) {
308 let old_weight = self.base_synapse.weight();
309
310 self.base_synapse
312 .update_stdp(current_time, pre_spiked, post_spiked);
313
314 let weight_change = (self.base_synapse.weight() - old_weight).abs();
316
317 self.learning_history.push_back(weight_change);
319 if self.learning_history.len() > self.max_history_length {
320 self.learning_history.pop_front();
321 }
322
323 self.update_learning_rate();
325 }
326
327 fn update_learning_rate(&mut self) {
329 if self.learning_history.is_empty() {
330 return;
331 }
332
333 let avg_activity: f64 =
335 self.learning_history.iter().sum::<f64>() / self.learning_history.len() as f64;
336
337 let adaptation_factor = (-avg_activity / self.metaplasticity_tau).exp();
339 let new_learning_rate = self.base_learning_rate * adaptation_factor;
340
341 self.base_synapse.set_stdp_rate(new_learning_rate);
342 }
343
344 pub fn base_synapse(&self) -> &Synapse {
346 &self.base_synapse
347 }
348
349 pub fn base_synapse_mut(&mut self) -> &mut Synapse {
351 &mut self.base_synapse
352 }
353
354 pub fn current_learning_rate(&self) -> f64 {
356 self.base_synapse.stdp_rate()
357 }
358
359 pub fn average_recent_activity(&self) -> f64 {
361 if self.learning_history.is_empty() {
362 0.0
363 } else {
364 self.learning_history.iter().sum::<f64>() / self.learning_history.len() as f64
365 }
366 }
367
368 pub fn reset_history(&mut self) {
370 self.learning_history.clear();
371 self.base_synapse.set_stdp_rate(self.base_learning_rate);
372 }
373}
374
375#[derive(Debug, Clone)]
380pub struct HomeostaticSynapse {
381 base_synapse: Synapse,
383 target_activity: f64,
385 current_activity: f64,
387 scaling_rate: f64,
389 activity_tau: f64,
391}
392
393impl HomeostaticSynapse {
394 pub fn new(
402 pre_neuron: usize,
403 post_neuron: usize,
404 initial_weight: f64,
405 target_activity: f64,
406 ) -> Self {
407 Self {
408 base_synapse: Synapse::new(pre_neuron, post_neuron, initial_weight),
409 target_activity,
410 current_activity: 0.0,
411 scaling_rate: 0.001,
412 activity_tau: 1000.0,
413 }
414 }
415
416 pub fn update(&mut self, current_time: f64, pre_spiked: bool, post_spiked: bool, dt: f64) {
424 self.base_synapse
426 .update_stdp(current_time, pre_spiked, post_spiked);
427
428 let activity_input = if post_spiked { 1.0 } else { 0.0 };
430 let decay = (-dt / self.activity_tau).exp();
431 self.current_activity = decay * self.current_activity + (1.0 - decay) * activity_input;
432
433 let activity_error = self.current_activity - self.target_activity;
435 let scaling_factor = 1.0 - self.scaling_rate * activity_error;
436
437 let current_weight = self.base_synapse.weight();
438 self.base_synapse
439 .set_weight(current_weight * scaling_factor);
440 }
441
442 pub fn base_synapse(&self) -> &Synapse {
444 &self.base_synapse
445 }
446
447 pub fn current_activity(&self) -> f64 {
449 self.current_activity
450 }
451
452 pub fn target_activity(&self) -> f64 {
454 self.target_activity
455 }
456
457 pub fn set_target_activity(&mut self, target: f64) {
459 self.target_activity = target;
460 }
461
462 pub fn activity_error(&self) -> f64 {
464 self.current_activity - self.target_activity
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn test_synapse_creation() {
474 let synapse = Synapse::new(0, 1, 0.5);
475 assert_eq!(synapse.pre_neuron(), 0);
476 assert_eq!(synapse.post_neuron(), 1);
477 assert_eq!(synapse.weight(), 0.5);
478 assert!(synapse.is_excitatory());
479 assert!(!synapse.is_inhibitory());
480 }
481
482 #[test]
483 fn test_stdp_potentiation() {
484 let mut synapse = Synapse::new(0, 1, 0.5);
485 let initial_weight = synapse.weight();
486
487 synapse.update_stdp(10.0, true, false); synapse.update_stdp(15.0, false, true); assert!(synapse.weight() > initial_weight);
493 }
494
495 #[test]
496 fn test_stdp_depression() {
497 let mut synapse = Synapse::new(0, 1, 0.5);
498 let initial_weight = synapse.weight();
499
500 synapse.update_stdp(10.0, false, true); synapse.update_stdp(15.0, true, false); assert!(synapse.weight() < initial_weight);
506 }
507
508 #[test]
509 fn test_synaptic_current() {
510 let synapse = Synapse::new(0, 1, 0.5);
511 let current = synapse.synaptic_current(2.0);
512 assert_eq!(current, 1.0); }
514
515 #[test]
516 fn test_weight_bounds() {
517 let mut synapse = Synapse::new(0, 1, 0.0);
518
519 synapse.set_weight(10.0);
521 assert_eq!(synapse.weight(), 2.0); synapse.set_weight(-10.0);
524 assert_eq!(synapse.weight(), -2.0); }
526
527 #[test]
528 fn test_inhibitory_synapse() {
529 let synapse = Synapse::new(0, 1, -0.5);
530 assert!(!synapse.is_excitatory());
531 assert!(synapse.is_inhibitory());
532
533 let current = synapse.synaptic_current(1.0);
534 assert_eq!(current, -0.5);
535 }
536
537 #[test]
538 fn test_spike_timing() {
539 let mut synapse = Synapse::new(0, 1, 0.5);
540
541 synapse.update_stdp(10.0, true, false);
542 assert_eq!(synapse.last_pre_spike(), 10.0);
543 assert_eq!(synapse.time_since_pre_spike(15.0), 5.0);
544
545 synapse.update_stdp(12.0, false, true);
546 assert_eq!(synapse.last_post_spike(), 12.0);
547 assert_eq!(synapse.time_since_post_spike(15.0), 3.0);
548 }
549
550 #[test]
551 fn test_metaplastic_synapse() {
552 let mut meta_synapse = MetaplasticSynapse::new(0, 1, 0.5, 0.01);
553
554 assert_eq!(meta_synapse.current_learning_rate(), 0.01);
555 assert_eq!(meta_synapse.average_recent_activity(), 0.0);
556
557 for i in 0..10 {
559 meta_synapse.update(i as f64, true, false);
560 meta_synapse.update(i as f64 + 0.5, false, true);
561 }
562
563 assert!(meta_synapse.average_recent_activity() > 0.0);
565 }
566
567 #[test]
568 fn test_homeostatic_synapse() {
569 let mut homeostatic = HomeostaticSynapse::new(0, 1, 0.5, 0.1);
570
571 assert_eq!(homeostatic.target_activity(), 0.1);
572 assert_eq!(homeostatic.current_activity(), 0.0);
573
574 for _ in 0..50 {
576 homeostatic.update(1.0, true, true, 0.1);
577 }
578
579 assert!(homeostatic.current_activity() > 0.0);
581 assert!(homeostatic.activity_error() != 0.0);
582 }
583
584 #[test]
585 fn test_synapse_reset() {
586 let mut synapse = Synapse::new(0, 1, 0.5);
587
588 synapse.update_stdp(10.0, true, false);
590 synapse.update_stdp(15.0, false, true);
591
592 synapse.reset_spike_history();
594 assert_eq!(synapse.last_pre_spike(), -1000.0);
595 assert_eq!(synapse.last_post_spike(), -1000.0);
596 }
597
598 #[test]
599 fn test_custom_stdp_parameters() {
600 let synapse = Synapse::with_stdp_params(0, 1, 0.5, 0.05, 10.0, -1.0, 1.0);
601
602 assert_eq!(synapse.stdp_rate(), 0.05);
603 assert_eq!(synapse.stdp_tau(), 10.0);
604 assert_eq!(synapse.weight_bounds(), (-1.0, 1.0));
605 }
606}