1use crate::calcium::CalciumDynamics;
12use crate::error::{Result, SynapseError};
13use crate::neurotransmitter::Neurotransmitter;
14use crate::plasticity::STDP;
15use crate::receptor::{AMPAReceptor, GABAAReceptor, NMDAReceptor, ReceptorDynamics};
16use crate::vesicle::VesiclePool;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum SynapseType {
21 Excitatory,
23 Inhibitory,
25 Modulatory,
27}
28
29#[derive(Debug, Clone)]
39pub struct Synapse {
40 pub synapse_type: SynapseType,
42
43 pub weight: f64,
45
46 pub w_max: f64,
48
49 pub delay: f64,
51
52 pub vesicle_pool: VesiclePool,
54
55 pub neurotransmitter: Neurotransmitter,
57
58 pub ampa: Option<AMPAReceptor>,
60
61 pub nmda: Option<NMDAReceptor>,
63
64 pub gabaa: Option<GABAAReceptor>,
66
67 pub stdp: Option<STDP>,
69
70 pub presynaptic_calcium: CalciumDynamics,
72
73 pub postsynaptic_calcium: CalciumDynamics,
75
76 last_pre_spike_time: Option<f64>,
78
79 last_post_spike_time: Option<f64>,
81
82 postsynaptic_voltage: f64,
84
85 pending_spike: Option<f64>,
87}
88
89impl Synapse {
90 pub fn excitatory(weight: f64, delay: f64) -> Result<Self> {
92 if weight < 0.0 {
93 return Err(SynapseError::InvalidWeight(weight, 0.0, f64::INFINITY));
94 }
95 if delay < 0.0 {
96 return Err(SynapseError::InvalidDelay(delay));
97 }
98
99 Ok(Self {
100 synapse_type: SynapseType::Excitatory,
101 weight,
102 w_max: 2.0,
103 delay,
104 vesicle_pool: VesiclePool::new(),
105 neurotransmitter: Neurotransmitter::glutamate(),
106 ampa: Some(AMPAReceptor::new()),
107 nmda: Some(NMDAReceptor::new()),
108 gabaa: None,
109 stdp: Some(STDP::new()),
110 presynaptic_calcium: CalciumDynamics::presynaptic(),
111 postsynaptic_calcium: CalciumDynamics::postsynaptic(),
112 last_pre_spike_time: None,
113 last_post_spike_time: None,
114 postsynaptic_voltage: -65.0,
115 pending_spike: None,
116 })
117 }
118
119 pub fn inhibitory(weight: f64, delay: f64) -> Result<Self> {
121 if weight < 0.0 {
122 return Err(SynapseError::InvalidWeight(weight, 0.0, f64::INFINITY));
123 }
124 if delay < 0.0 {
125 return Err(SynapseError::InvalidDelay(delay));
126 }
127
128 Ok(Self {
129 synapse_type: SynapseType::Inhibitory,
130 weight,
131 w_max: 2.0,
132 delay,
133 vesicle_pool: VesiclePool::new(),
134 neurotransmitter: Neurotransmitter::gaba(),
135 ampa: None,
136 nmda: None,
137 gabaa: Some(GABAAReceptor::new()),
138 stdp: Some(STDP::new()),
139 presynaptic_calcium: CalciumDynamics::presynaptic(),
140 postsynaptic_calcium: CalciumDynamics::postsynaptic(),
141 last_pre_spike_time: None,
142 last_post_spike_time: None,
143 postsynaptic_voltage: -65.0,
144 pending_spike: None,
145 })
146 }
147
148 pub fn depressing_excitatory(weight: f64, delay: f64) -> Result<Self> {
150 let mut syn = Self::excitatory(weight, delay)?;
151 syn.vesicle_pool = VesiclePool::depressing();
152 Ok(syn)
153 }
154
155 pub fn facilitating_excitatory(weight: f64, delay: f64) -> Result<Self> {
157 let mut syn = Self::excitatory(weight, delay)?;
158 syn.vesicle_pool = VesiclePool::facilitating();
159 Ok(syn)
160 }
161
162 pub fn presynaptic_spike(&mut self, time: f64) -> Result<()> {
167 self.last_pre_spike_time = Some(time);
168
169 self.pending_spike = Some(time + self.delay);
171
172 if let Some(stdp) = &mut self.stdp {
177 let _dw = stdp.pre_spike(time, self.weight);
178 self.weight = stdp.apply_update(self.weight);
179 }
180
181 Ok(())
182 }
183
184 pub fn postsynaptic_spike(&mut self, time: f64) -> Result<()> {
189 self.last_post_spike_time = Some(time);
190
191 self.postsynaptic_calcium.spike();
193
194 if let Some(stdp) = &mut self.stdp {
196 let _dw = stdp.post_spike(time, self.weight);
197 self.weight = stdp.apply_update(self.weight);
198 }
199
200 Ok(())
201 }
202
203 pub fn update(&mut self, time: f64, postsynaptic_voltage: f64, dt: f64) -> Result<()> {
210 self.postsynaptic_voltage = postsynaptic_voltage;
211
212 if let Some(spike_time) = self.pending_spike {
214 if time >= spike_time {
215 self.presynaptic_calcium.spike();
217
218 let ca_conc = self.presynaptic_calcium.get_concentration();
220 let vesicles_released = self.vesicle_pool.release(ca_conc)?;
221
222 if vesicles_released > 0 {
224 self.neurotransmitter.release();
225 }
226
227 self.pending_spike = None;
228 }
229 }
230
231 self.vesicle_pool.update(dt)?;
233
234 self.neurotransmitter.update(dt)?;
236
237 let nt_conc = self.neurotransmitter.get_concentration();
239
240 if let Some(ampa) = &mut self.ampa {
242 ampa.update(nt_conc, postsynaptic_voltage, dt)?;
243 }
244 if let Some(nmda) = &mut self.nmda {
245 nmda.update(nt_conc, postsynaptic_voltage, dt)?;
246
247 let nmda_current = nmda.current(postsynaptic_voltage);
249 let ca_influx = nmda_current.abs() * 0.001; self.postsynaptic_calcium.add_influx(ca_influx)?;
251 }
252 if let Some(gabaa) = &mut self.gabaa {
253 gabaa.update(nt_conc, postsynaptic_voltage, dt)?;
254 }
255
256 self.presynaptic_calcium.update(0.0, dt)?;
258 self.postsynaptic_calcium.update(0.0, dt)?;
259
260 Ok(())
261 }
262
263 pub fn current(&self, voltage: f64) -> f64 {
271 let mut total_current = 0.0;
272
273 if let Some(ampa) = &self.ampa {
274 total_current += ampa.current(voltage);
275 }
276 if let Some(nmda) = &self.nmda {
277 total_current += nmda.current(voltage);
278 }
279 if let Some(gabaa) = &self.gabaa {
280 total_current += gabaa.current(voltage);
281 }
282
283 total_current * self.weight
284 }
285
286 pub fn conductance(&self) -> f64 {
291 let mut total_conductance = 0.0;
292
293 if let Some(ampa) = &self.ampa {
294 total_conductance += ampa.get_conductance();
295 }
296 if let Some(nmda) = &self.nmda {
297 total_conductance += nmda.get_conductance();
298 }
299 if let Some(gabaa) = &self.gabaa {
300 total_conductance += gabaa.get_conductance();
301 }
302
303 total_conductance * self.weight
304 }
305
306 pub fn effective_weight(&self) -> f64 {
308 self.weight * self.vesicle_pool.release_probability()
309 }
310
311 pub fn set_weight(&mut self, weight: f64) -> Result<()> {
313 if weight < 0.0 || weight > self.w_max {
314 return Err(SynapseError::InvalidWeight(weight, 0.0, self.w_max));
315 }
316 self.weight = weight;
317 Ok(())
318 }
319
320 pub fn enable_stdp(&mut self, a_plus: f64, a_minus: f64, tau_plus: f64, tau_minus: f64) -> Result<()> {
322 self.stdp = Some(STDP::with_params(a_plus, a_minus, tau_plus, tau_minus)?);
323 Ok(())
324 }
325
326 pub fn disable_stdp(&mut self) {
328 self.stdp = None;
329 }
330
331 pub fn reset(&mut self) {
333 self.vesicle_pool.reset();
334 self.neurotransmitter.reset();
335
336 if let Some(ampa) = &mut self.ampa {
337 ampa.reset();
338 }
339 if let Some(nmda) = &mut self.nmda {
340 nmda.reset();
341 }
342 if let Some(gabaa) = &mut self.gabaa {
343 gabaa.reset();
344 }
345 if let Some(stdp) = &mut self.stdp {
346 stdp.reset();
347 }
348
349 self.presynaptic_calcium.reset();
350 self.postsynaptic_calcium.reset();
351 self.last_pre_spike_time = None;
352 self.last_post_spike_time = None;
353 self.pending_spike = None;
354 }
355}
356
357pub struct SynapseBuilder {
359 synapse_type: SynapseType,
360 weight: f64,
361 w_max: f64,
362 delay: f64,
363 vesicle_pool: VesiclePool,
364 neurotransmitter: Neurotransmitter,
365 enable_stdp: bool,
366}
367
368impl SynapseBuilder {
369 pub fn new(synapse_type: SynapseType) -> Self {
371 Self {
372 synapse_type,
373 weight: 1.0,
374 w_max: 2.0,
375 delay: 1.0,
376 vesicle_pool: VesiclePool::new(),
377 neurotransmitter: Neurotransmitter::glutamate(),
378 enable_stdp: true,
379 }
380 }
381
382 pub fn weight(mut self, weight: f64) -> Self {
384 self.weight = weight;
385 self
386 }
387
388 pub fn max_weight(mut self, w_max: f64) -> Self {
390 self.w_max = w_max;
391 self
392 }
393
394 pub fn delay(mut self, delay: f64) -> Self {
396 self.delay = delay;
397 self
398 }
399
400 pub fn vesicle_pool(mut self, pool: VesiclePool) -> Self {
402 self.vesicle_pool = pool;
403 self
404 }
405
406 pub fn neurotransmitter(mut self, nt: Neurotransmitter) -> Self {
408 self.neurotransmitter = nt;
409 self
410 }
411
412 pub fn stdp(mut self, enable: bool) -> Self {
414 self.enable_stdp = enable;
415 self
416 }
417
418 pub fn build(self) -> Result<Synapse> {
420 if self.weight < 0.0 {
421 return Err(SynapseError::InvalidWeight(self.weight, 0.0, self.w_max));
422 }
423 if self.delay < 0.0 {
424 return Err(SynapseError::InvalidDelay(self.delay));
425 }
426
427 let (ampa, nmda, gabaa) = match self.synapse_type {
428 SynapseType::Excitatory => (Some(AMPAReceptor::new()), Some(NMDAReceptor::new()), None),
429 SynapseType::Inhibitory => (None, None, Some(GABAAReceptor::new())),
430 SynapseType::Modulatory => (None, None, None),
431 };
432
433 Ok(Synapse {
434 synapse_type: self.synapse_type,
435 weight: self.weight,
436 w_max: self.w_max,
437 delay: self.delay,
438 vesicle_pool: self.vesicle_pool,
439 neurotransmitter: self.neurotransmitter,
440 ampa,
441 nmda,
442 gabaa,
443 stdp: if self.enable_stdp { Some(STDP::new()) } else { None },
444 presynaptic_calcium: CalciumDynamics::presynaptic(),
445 postsynaptic_calcium: CalciumDynamics::postsynaptic(),
446 last_pre_spike_time: None,
447 last_post_spike_time: None,
448 postsynaptic_voltage: -65.0,
449 pending_spike: None,
450 })
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_excitatory_synapse_creation() {
460 let syn = Synapse::excitatory(1.0, 1.0).unwrap();
461 assert_eq!(syn.synapse_type, SynapseType::Excitatory);
462 assert!(syn.ampa.is_some());
463 assert!(syn.nmda.is_some());
464 assert!(syn.gabaa.is_none());
465 }
466
467 #[test]
468 fn test_inhibitory_synapse_creation() {
469 let syn = Synapse::inhibitory(1.0, 1.0).unwrap();
470 assert_eq!(syn.synapse_type, SynapseType::Inhibitory);
471 assert!(syn.gabaa.is_some());
472 assert!(syn.ampa.is_none());
473 assert!(syn.nmda.is_none());
474 }
475
476 #[test]
477 fn test_synaptic_transmission() {
478 let mut syn = Synapse::excitatory(1.0, 1.0).unwrap();
479
480 syn.presynaptic_spike(0.0).unwrap();
482
483 for t in 0..50 {
485 syn.update(t as f64 * 0.1, -65.0, 0.1).unwrap();
486 }
487
488 let conductance = syn.conductance();
490 let nt_conc = syn.neurotransmitter.get_concentration();
491 assert!(conductance > 0.0 || nt_conc > 0.0, "conductance: {}, nt_conc: {}", conductance, nt_conc);
492 }
493
494 #[test]
495 fn test_synaptic_delay() {
496 let mut syn = Synapse::excitatory(1.0, 5.0).unwrap(); syn.presynaptic_spike(0.0).unwrap();
499
500 syn.update(1.0, -65.0, 1.0).unwrap();
502 assert_eq!(syn.conductance(), 0.0);
503
504 for t in 6..20 {
506 syn.update(t as f64, -65.0, 0.5).unwrap();
507 }
508 assert!(syn.conductance() > 0.0 || syn.neurotransmitter.get_concentration() > 0.0);
509 }
510
511 #[test]
512 fn test_short_term_depression() {
513 let mut syn = Synapse::depressing_excitatory(1.0, 1.0).unwrap();
514
515 let initial_prob = syn.vesicle_pool.release_probability();
516
517 for i in 0..5 {
519 syn.presynaptic_spike(i as f64 * 10.0).unwrap();
520 syn.update((i as f64 + 1.0) * 10.0, -65.0, 10.0).unwrap();
521 }
522
523 assert!(syn.vesicle_pool.release_probability() < initial_prob);
525 }
526
527 #[test]
528 fn test_stdp_potentiation() {
529 let mut syn = Synapse::excitatory(0.5, 1.0).unwrap();
530
531 let initial_weight = syn.weight;
532
533 syn.presynaptic_spike(0.0).unwrap();
535 syn.postsynaptic_spike(10.0).unwrap();
536
537 assert!(syn.weight > initial_weight);
539 }
540
541 #[test]
542 fn test_stdp_depression() {
543 let mut syn = Synapse::excitatory(0.5, 1.0).unwrap();
544
545 let initial_weight = syn.weight;
546
547 syn.postsynaptic_spike(0.0).unwrap();
549 syn.presynaptic_spike(10.0).unwrap();
550
551 assert!(syn.weight < initial_weight);
553 }
554
555 #[test]
556 fn test_synapse_builder() {
557 let syn = SynapseBuilder::new(SynapseType::Excitatory)
558 .weight(1.5)
559 .delay(2.0)
560 .stdp(false)
561 .build()
562 .unwrap();
563
564 assert_eq!(syn.weight, 1.5);
565 assert_eq!(syn.delay, 2.0);
566 assert!(syn.stdp.is_none());
567 }
568
569 #[test]
570 fn test_synapse_reset() {
571 let mut syn = Synapse::excitatory(1.0, 1.0).unwrap();
572
573 syn.presynaptic_spike(0.0).unwrap();
574 syn.update(2.0, -65.0, 2.0).unwrap();
575
576 syn.reset();
577
578 assert_eq!(syn.conductance(), 0.0);
579 assert_eq!(syn.last_pre_spike_time, None);
580 }
581}