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 > self.last_pre_spike - 50.0 {
129 let dt = self.last_post_spike - self.last_pre_spike;
131 if dt > 0.0 {
132 let delta_w = self.stdp_rate * (-dt / self.stdp_tau).exp();
133 self.weight += delta_w;
134 }
135 }
136
137 if post_spiked && self.last_pre_spike > self.last_post_spike - 50.0 {
138 let dt = self.last_pre_spike - self.last_post_spike;
140 if dt > 0.0 {
141 let delta_w = -self.stdp_rate * (-dt / self.stdp_tau).exp();
142 self.weight += delta_w;
143 }
144 }
145
146 self.weight = self.weight.clamp(self.min_weight, self.max_weight);
148 }
149
150 pub fn synaptic_current(&self, pre_spike_strength: f64) -> f64 {
161 self.weight * pre_spike_strength
162 }
163
164 pub fn weight(&self) -> f64 {
166 self.weight
167 }
168
169 pub fn set_weight(&mut self, weight: f64) {
171 self.weight = weight.clamp(self.min_weight, self.max_weight);
172 }
173
174 pub fn pre_neuron(&self) -> usize {
176 self.pre_neuron
177 }
178
179 pub fn post_neuron(&self) -> usize {
181 self.post_neuron
182 }
183
184 pub fn stdp_rate(&self) -> f64 {
186 self.stdp_rate
187 }
188
189 pub fn set_stdp_rate(&mut self, rate: f64) {
191 self.stdp_rate = rate;
192 }
193
194 pub fn stdp_tau(&self) -> f64 {
196 self.stdp_tau
197 }
198
199 pub fn set_stdp_tau(&mut self, tau: f64) {
201 self.stdp_tau = tau;
202 }
203
204 pub fn last_pre_spike(&self) -> f64 {
206 self.last_pre_spike
207 }
208
209 pub fn last_post_spike(&self) -> f64 {
211 self.last_post_spike
212 }
213
214 pub fn weight_bounds(&self) -> (f64, f64) {
216 (self.min_weight, self.max_weight)
217 }
218
219 pub fn set_weight_bounds(&mut self, min_weight: f64, max_weight: f64) {
221 self.min_weight = min_weight;
222 self.max_weight = max_weight;
223 self.weight = self.weight.clamp(min_weight, max_weight);
225 }
226
227 pub fn is_excitatory(&self) -> bool {
229 self.weight > 0.0
230 }
231
232 pub fn is_inhibitory(&self) -> bool {
234 self.weight < 0.0
235 }
236
237 pub fn reset_spike_history(&mut self) {
239 self.last_pre_spike = -1000.0;
240 self.last_post_spike = -1000.0;
241 }
242
243 pub fn time_since_pre_spike(&self, current_time: f64) -> f64 {
245 current_time - self.last_pre_spike
246 }
247
248 pub fn time_since_post_spike(&self, current_time: f64) -> f64 {
250 current_time - self.last_post_spike
251 }
252}
253
254#[derive(Debug, Clone)]
260pub struct MetaplasticSynapse {
261 base_synapse: Synapse,
263 learning_history: VecDeque<f64>,
265 max_history_length: usize,
267 metaplasticity_tau: f64,
269 base_learning_rate: f64,
271}
272
273impl MetaplasticSynapse {
274 pub fn new(
282 pre_neuron: usize,
283 post_neuron: usize,
284 initial_weight: f64,
285 base_learning_rate: f64,
286 ) -> Self {
287 let mut base_synapse = Synapse::new(pre_neuron, post_neuron, initial_weight);
288 base_synapse.set_stdp_rate(base_learning_rate);
289
290 Self {
291 base_synapse,
292 learning_history: VecDeque::new(),
293 max_history_length: 100,
294 metaplasticity_tau: 100.0,
295 base_learning_rate,
296 }
297 }
298
299 pub fn update(&mut self, current_time: f64, pre_spiked: bool, post_spiked: bool) {
306 let old_weight = self.base_synapse.weight();
307
308 self.base_synapse
310 .update_stdp(current_time, pre_spiked, post_spiked);
311
312 let weight_change = (self.base_synapse.weight() - old_weight).abs();
314
315 self.learning_history.push_back(weight_change);
317 if self.learning_history.len() > self.max_history_length {
318 self.learning_history.pop_front();
319 }
320
321 self.update_learning_rate();
323 }
324
325 fn update_learning_rate(&mut self) {
327 if self.learning_history.is_empty() {
328 return;
329 }
330
331 let avg_activity: f64 =
333 self.learning_history.iter().sum::<f64>() / self.learning_history.len() as f64;
334
335 let adaptation_factor = (-avg_activity / self.metaplasticity_tau).exp();
337 let new_learning_rate = self.base_learning_rate * adaptation_factor;
338
339 self.base_synapse.set_stdp_rate(new_learning_rate);
340 }
341
342 pub fn base_synapse(&self) -> &Synapse {
344 &self.base_synapse
345 }
346
347 pub fn base_synapse_mut(&mut self) -> &mut Synapse {
349 &mut self.base_synapse
350 }
351
352 pub fn current_learning_rate(&self) -> f64 {
354 self.base_synapse.stdp_rate()
355 }
356
357 pub fn average_recent_activity(&self) -> f64 {
359 if self.learning_history.is_empty() {
360 0.0
361 } else {
362 self.learning_history.iter().sum::<f64>() / self.learning_history.len() as f64
363 }
364 }
365
366 pub fn reset_history(&mut self) {
368 self.learning_history.clear();
369 self.base_synapse.set_stdp_rate(self.base_learning_rate);
370 }
371}
372
373#[derive(Debug, Clone)]
378pub struct HomeostaticSynapse {
379 base_synapse: Synapse,
381 target_activity: f64,
383 current_activity: f64,
385 scaling_rate: f64,
387 activity_tau: f64,
389}
390
391impl HomeostaticSynapse {
392 pub fn new(
400 pre_neuron: usize,
401 post_neuron: usize,
402 initial_weight: f64,
403 target_activity: f64,
404 ) -> Self {
405 Self {
406 base_synapse: Synapse::new(pre_neuron, post_neuron, initial_weight),
407 target_activity,
408 current_activity: 0.0,
409 scaling_rate: 0.001,
410 activity_tau: 1000.0,
411 }
412 }
413
414 pub fn update(&mut self, current_time: f64, pre_spiked: bool, post_spiked: bool, dt: f64) {
422 self.base_synapse
424 .update_stdp(current_time, pre_spiked, post_spiked);
425
426 let activity_input = if post_spiked { 1.0 } else { 0.0 };
428 let decay = (-dt / self.activity_tau).exp();
429 self.current_activity = decay * self.current_activity + (1.0 - decay) * activity_input;
430
431 let activity_error = self.current_activity - self.target_activity;
433 let scaling_factor = 1.0 - self.scaling_rate * activity_error;
434
435 let current_weight = self.base_synapse.weight();
436 self.base_synapse
437 .set_weight(current_weight * scaling_factor);
438 }
439
440 pub fn base_synapse(&self) -> &Synapse {
442 &self.base_synapse
443 }
444
445 pub fn current_activity(&self) -> f64 {
447 self.current_activity
448 }
449
450 pub fn target_activity(&self) -> f64 {
452 self.target_activity
453 }
454
455 pub fn set_target_activity(&mut self, target: f64) {
457 self.target_activity = target;
458 }
459
460 pub fn activity_error(&self) -> f64 {
462 self.current_activity - self.target_activity
463 }
464}
465
466#[cfg(test)]
467mod tests {
468 use super::*;
469
470 #[test]
471 fn test_synapse_creation() {
472 let synapse = Synapse::new(0, 1, 0.5);
473 assert_eq!(synapse.pre_neuron(), 0);
474 assert_eq!(synapse.post_neuron(), 1);
475 assert_eq!(synapse.weight(), 0.5);
476 assert!(synapse.is_excitatory());
477 assert!(!synapse.is_inhibitory());
478 }
479
480 #[test]
481 #[ignore]
482 fn test_stdp_potentiation() {
483 let mut synapse = Synapse::new(0, 1, 0.5);
484 let initial_weight = synapse.weight();
485
486 synapse.update_stdp(10.0, true, false); synapse.update_stdp(15.0, false, true); assert!(synapse.weight() > initial_weight);
492 }
493
494 #[test]
495 #[ignore]
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 #[ignore]
552 fn test_metaplastic_synapse() {
553 let mut meta_synapse = MetaplasticSynapse::new(0, 1, 0.5, 0.01);
554
555 assert_eq!(meta_synapse.current_learning_rate(), 0.01);
556 assert_eq!(meta_synapse.average_recent_activity(), 0.0);
557
558 for i in 0..10 {
560 meta_synapse.update(i as f64, true, false);
561 meta_synapse.update(i as f64 + 0.5, false, true);
562 }
563
564 assert!(meta_synapse.average_recent_activity() > 0.0);
566 }
567
568 #[test]
569 fn test_homeostatic_synapse() {
570 let mut homeostatic = HomeostaticSynapse::new(0, 1, 0.5, 0.1);
571
572 assert_eq!(homeostatic.target_activity(), 0.1);
573 assert_eq!(homeostatic.current_activity(), 0.0);
574
575 for _ in 0..50 {
577 homeostatic.update(1.0, true, true, 0.1);
578 }
579
580 assert!(homeostatic.current_activity() > 0.0);
582 assert!(homeostatic.activity_error() != 0.0);
583 }
584
585 #[test]
586 fn test_synapse_reset() {
587 let mut synapse = Synapse::new(0, 1, 0.5);
588
589 synapse.update_stdp(10.0, true, false);
591 synapse.update_stdp(15.0, false, true);
592
593 synapse.reset_spike_history();
595 assert_eq!(synapse.last_pre_spike(), -1000.0);
596 assert_eq!(synapse.last_post_spike(), -1000.0);
597 }
598
599 #[test]
600 fn test_custom_stdp_parameters() {
601 let synapse = Synapse::with_stdp_params(0, 1, 0.5, 0.05, 10.0, -1.0, 1.0);
602
603 assert_eq!(synapse.stdp_rate(), 0.05);
604 assert_eq!(synapse.stdp_tau(), 10.0);
605 assert_eq!(synapse.weight_bounds(), (-1.0, 1.0));
606 }
607}