1use crate::error::{Result, SynapseError};
7
8pub trait ReceptorDynamics {
10 fn update(&mut self, nt_concentration: f64, voltage: f64, dt: f64) -> Result<()>;
17
18 fn get_conductance(&self) -> f64;
20
21 fn reversal_potential(&self) -> f64;
23
24 fn reset(&mut self);
26}
27
28#[derive(Debug, Clone)]
38pub struct AMPAReceptor {
39 pub open_probability: f64,
41
42 rise_state: f64,
44
45 pub tau_rise: f64,
47
48 pub tau_decay: f64,
50
51 pub alpha: f64,
53
54 pub beta: f64,
56
57 pub e_rev: f64,
59
60 pub g_max: f64,
62}
63
64impl Default for AMPAReceptor {
65 fn default() -> Self {
66 Self {
67 open_probability: 0.0,
68 rise_state: 0.0,
69 tau_rise: 0.2, tau_decay: 2.0, alpha: 1.1, beta: 0.19, e_rev: 0.0, g_max: 1.0, }
76 }
77}
78
79impl AMPAReceptor {
80 pub fn new() -> Self {
82 Self::default()
83 }
84
85 pub fn with_params(tau_rise: f64, tau_decay: f64, g_max: f64) -> Result<Self> {
87 if tau_rise <= 0.0 {
88 return Err(SynapseError::InvalidTimeConstant(tau_rise));
89 }
90 if tau_decay <= 0.0 {
91 return Err(SynapseError::InvalidTimeConstant(tau_decay));
92 }
93
94 Ok(Self {
95 tau_rise,
96 tau_decay,
97 g_max,
98 ..Self::default()
99 })
100 }
101
102 pub fn current(&self, voltage: f64) -> f64 {
104 self.g_max * self.open_probability * (voltage - self.e_rev)
105 }
106}
107
108impl ReceptorDynamics for AMPAReceptor {
109 fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
110 let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
112 - self.beta * self.rise_state;
113 self.rise_state += dr * dt;
114 self.rise_state = self.rise_state.clamp(0.0, 1.0);
115
116 let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
119 let tau_eff = self.tau_decay;
120 self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
121 self.open_probability = self.open_probability.clamp(0.0, 1.0);
122
123 Ok(())
124 }
125
126 fn get_conductance(&self) -> f64 {
127 self.g_max * self.open_probability
128 }
129
130 fn reversal_potential(&self) -> f64 {
131 self.e_rev
132 }
133
134 fn reset(&mut self) {
135 self.open_probability = 0.0;
136 self.rise_state = 0.0;
137 }
138}
139
140#[derive(Debug, Clone)]
150pub struct NMDAReceptor {
151 pub open_probability: f64,
153
154 rise_state: f64,
156
157 pub tau_rise: f64,
159
160 pub tau_decay: f64,
162
163 pub alpha: f64,
165
166 pub beta: f64,
168
169 pub e_rev: f64,
171
172 pub g_max: f64,
174
175 pub mg_concentration: f64,
177}
178
179impl Default for NMDAReceptor {
180 fn default() -> Self {
181 Self {
182 open_probability: 0.0,
183 rise_state: 0.0,
184 tau_rise: 2.0, tau_decay: 100.0, alpha: 0.72, beta: 0.0066, e_rev: 0.0, g_max: 1.0, mg_concentration: 1.0, }
192 }
193}
194
195impl NMDAReceptor {
196 pub fn new() -> Self {
198 Self::default()
199 }
200
201 pub fn with_params(tau_rise: f64, tau_decay: f64, g_max: f64) -> Result<Self> {
203 if tau_rise <= 0.0 {
204 return Err(SynapseError::InvalidTimeConstant(tau_rise));
205 }
206 if tau_decay <= 0.0 {
207 return Err(SynapseError::InvalidTimeConstant(tau_decay));
208 }
209
210 Ok(Self {
211 tau_rise,
212 tau_decay,
213 g_max,
214 ..Self::default()
215 })
216 }
217
218 pub fn mg_block(&self, voltage: f64) -> f64 {
223 1.0 / (1.0 + (self.mg_concentration / 3.57) * (-0.062 * voltage).exp())
224 }
225
226 pub fn current(&self, voltage: f64) -> f64 {
228 let mg_block = self.mg_block(voltage);
229 self.g_max * self.open_probability * mg_block * (voltage - self.e_rev)
230 }
231}
232
233impl ReceptorDynamics for NMDAReceptor {
234 fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
235 let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
237 - self.beta * self.rise_state;
238 self.rise_state += dr * dt;
239 self.rise_state = self.rise_state.clamp(0.0, 1.0);
240
241 let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
243 let tau_eff = self.tau_decay;
244 self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
245 self.open_probability = self.open_probability.clamp(0.0, 1.0);
246
247 Ok(())
248 }
249
250 fn get_conductance(&self) -> f64 {
251 self.g_max * self.open_probability
252 }
253
254 fn reversal_potential(&self) -> f64 {
255 self.e_rev
256 }
257
258 fn reset(&mut self) {
259 self.open_probability = 0.0;
260 self.rise_state = 0.0;
261 }
262}
263
264#[derive(Debug, Clone)]
269pub struct GABAAReceptor {
270 pub open_probability: f64,
272
273 rise_state: f64,
275
276 pub tau_rise: f64,
278
279 pub tau_decay: f64,
281
282 pub alpha: f64,
284
285 pub beta: f64,
287
288 pub e_rev: f64,
290
291 pub g_max: f64,
293}
294
295impl Default for GABAAReceptor {
296 fn default() -> Self {
297 Self {
298 open_probability: 0.0,
299 rise_state: 0.0,
300 tau_rise: 0.5, tau_decay: 5.0, alpha: 5.0, beta: 0.18, e_rev: -70.0, g_max: 1.0, }
307 }
308}
309
310impl GABAAReceptor {
311 pub fn new() -> Self {
313 Self::default()
314 }
315
316 pub fn current(&self, voltage: f64) -> f64 {
318 self.g_max * self.open_probability * (voltage - self.e_rev)
319 }
320}
321
322impl ReceptorDynamics for GABAAReceptor {
323 fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
324 let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
326 - self.beta * self.rise_state;
327 self.rise_state += dr * dt;
328 self.rise_state = self.rise_state.clamp(0.0, 1.0);
329
330 let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
332 let tau_eff = self.tau_decay;
333 self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
334 self.open_probability = self.open_probability.clamp(0.0, 1.0);
335
336 Ok(())
337 }
338
339 fn get_conductance(&self) -> f64 {
340 self.g_max * self.open_probability
341 }
342
343 fn reversal_potential(&self) -> f64 {
344 self.e_rev
345 }
346
347 fn reset(&mut self) {
348 self.open_probability = 0.0;
349 self.rise_state = 0.0;
350 }
351}
352
353#[derive(Debug, Clone)]
358pub struct GABABReceptor {
359 pub activation: f64,
361
362 pub g_protein: f64,
364
365 pub tau_rise: f64,
367
368 pub tau_decay: f64,
370
371 pub tau_gprotein: f64,
373
374 pub alpha: f64,
376
377 pub beta: f64,
379
380 pub e_rev: f64,
382
383 pub g_max: f64,
385}
386
387impl Default for GABABReceptor {
388 fn default() -> Self {
389 Self {
390 activation: 0.0,
391 g_protein: 0.0,
392 tau_rise: 50.0, tau_decay: 200.0, tau_gprotein: 100.0, alpha: 0.09, beta: 0.0012, e_rev: -90.0, g_max: 1.0, }
400 }
401}
402
403impl GABABReceptor {
404 pub fn new() -> Self {
406 Self::default()
407 }
408
409 pub fn current(&self, voltage: f64) -> f64 {
411 self.g_max * self.g_protein * (voltage - self.e_rev)
413 }
414}
415
416impl ReceptorDynamics for GABABReceptor {
417 fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
418 let dr = self.alpha * nt_concentration * (1.0 - self.activation)
420 - self.beta * self.activation;
421 self.activation += dr * dt;
422 self.activation = self.activation.clamp(0.0, 1.0);
423
424 let dg = (self.activation - self.g_protein) / self.tau_gprotein;
426 self.g_protein += dg * dt;
427 self.g_protein = self.g_protein.clamp(0.0, 1.0);
428
429 Ok(())
430 }
431
432 fn get_conductance(&self) -> f64 {
433 self.g_max * self.g_protein
434 }
435
436 fn reversal_potential(&self) -> f64 {
437 self.e_rev
438 }
439
440 fn reset(&mut self) {
441 self.activation = 0.0;
442 self.g_protein = 0.0;
443 }
444}
445
446#[derive(Debug, Clone)]
451pub struct MetabotropicGlutamateReceptor {
452 pub activation: f64,
454
455 pub g_protein: f64,
457
458 pub tau_activation: f64,
460
461 pub tau_deactivation: f64,
463
464 pub alpha: f64,
466
467 pub beta: f64,
469}
470
471impl Default for MetabotropicGlutamateReceptor {
472 fn default() -> Self {
473 Self {
474 activation: 0.0,
475 g_protein: 0.0,
476 tau_activation: 100.0, tau_deactivation: 500.0, alpha: 0.05, beta: 0.002, }
481 }
482}
483
484impl MetabotropicGlutamateReceptor {
485 pub fn new() -> Self {
487 Self::default()
488 }
489
490 pub fn get_gprotein_activation(&self) -> f64 {
492 self.g_protein
493 }
494}
495
496impl ReceptorDynamics for MetabotropicGlutamateReceptor {
497 fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
498 let dr = self.alpha * nt_concentration * (1.0 - self.activation)
500 - self.beta * self.activation;
501 self.activation += dr * dt;
502 self.activation = self.activation.clamp(0.0, 1.0);
503
504 let tau = if self.activation > self.g_protein {
506 self.tau_activation
507 } else {
508 self.tau_deactivation
509 };
510 let dg = (self.activation - self.g_protein) / tau;
511 self.g_protein += dg * dt;
512 self.g_protein = self.g_protein.clamp(0.0, 1.0);
513
514 Ok(())
515 }
516
517 fn get_conductance(&self) -> f64 {
518 0.0
520 }
521
522 fn reversal_potential(&self) -> f64 {
523 0.0
524 }
525
526 fn reset(&mut self) {
527 self.activation = 0.0;
528 self.g_protein = 0.0;
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 #[test]
537 fn test_ampa_receptor_creation() {
538 let ampa = AMPAReceptor::new();
539 assert_eq!(ampa.open_probability, 0.0);
540 assert_eq!(ampa.e_rev, 0.0);
541 }
542
543 #[test]
544 fn test_ampa_receptor_activation() {
545 let mut ampa = AMPAReceptor::new();
546 let nt_conc = 1.0; let voltage = -65.0;
548 let dt = 0.1;
549
550 for _ in 0..100 {
552 ampa.update(nt_conc, voltage, dt).unwrap();
553 }
554
555 assert!(ampa.open_probability > 0.0);
556 assert!(ampa.open_probability <= 1.0);
557 }
558
559 #[test]
560 fn test_nmda_mg_block() {
561 let nmda = NMDAReceptor::new();
562
563 let block_hyperpol = nmda.mg_block(-70.0);
565 assert!(block_hyperpol < 0.1);
566
567 let block_depol = nmda.mg_block(0.0);
569 assert!(block_depol > block_hyperpol);
570
571 let block_strong_depol = nmda.mg_block(40.0);
573 assert!(block_strong_depol > 0.8);
574 }
575
576 #[test]
577 fn test_nmda_receptor_kinetics() {
578 let mut nmda = NMDAReceptor::new();
579 let nt_conc = 0.5;
580 let voltage = 0.0;
581 let dt = 0.1;
582
583 for _ in 0..1000 {
585 nmda.update(nt_conc, voltage, dt).unwrap();
586 }
587
588 assert!(nmda.open_probability > 0.0);
589 }
590
591 #[test]
592 fn test_gabaa_receptor() {
593 let mut gabaa = GABAAReceptor::new();
594 assert_eq!(gabaa.e_rev, -70.0);
595
596 gabaa.update(1.0, -65.0, 0.1).unwrap();
597 assert!(gabaa.open_probability >= 0.0);
598 }
599
600 #[test]
601 fn test_gabab_receptor_gprotein() {
602 let mut gabab = GABABReceptor::new();
603
604 for _ in 0..1000 {
606 gabab.update(1.0, -65.0, 0.1).unwrap();
607 }
608
609 assert!(gabab.g_protein > 0.0);
611 assert!(gabab.activation > 0.0);
612 }
613
614 #[test]
615 fn test_receptor_reset() {
616 let mut ampa = AMPAReceptor::new();
617 ampa.update(1.0, -65.0, 0.1).unwrap();
618
619 ampa.reset();
620 assert_eq!(ampa.open_probability, 0.0);
621 assert_eq!(ampa.rise_state, 0.0);
622 }
623}