use crate::error::{Result, SynapseError};
pub trait ReceptorDynamics {
fn update(&mut self, nt_concentration: f64, voltage: f64, dt: f64) -> Result<()>;
fn get_conductance(&self) -> f64;
fn reversal_potential(&self) -> f64;
fn reset(&mut self);
}
#[derive(Debug, Clone)]
pub struct AMPAReceptor {
pub open_probability: f64,
rise_state: f64,
pub tau_rise: f64,
pub tau_decay: f64,
pub alpha: f64,
pub beta: f64,
pub e_rev: f64,
pub g_max: f64,
}
impl Default for AMPAReceptor {
fn default() -> Self {
Self {
open_probability: 0.0,
rise_state: 0.0,
tau_rise: 0.2, tau_decay: 2.0, alpha: 1.1, beta: 0.19, e_rev: 0.0, g_max: 1.0, }
}
}
impl AMPAReceptor {
pub fn new() -> Self {
Self::default()
}
pub fn with_params(tau_rise: f64, tau_decay: f64, g_max: f64) -> Result<Self> {
if tau_rise <= 0.0 {
return Err(SynapseError::InvalidTimeConstant(tau_rise));
}
if tau_decay <= 0.0 {
return Err(SynapseError::InvalidTimeConstant(tau_decay));
}
Ok(Self {
tau_rise,
tau_decay,
g_max,
..Self::default()
})
}
pub fn current(&self, voltage: f64) -> f64 {
self.g_max * self.open_probability * (voltage - self.e_rev)
}
}
impl ReceptorDynamics for AMPAReceptor {
fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
- self.beta * self.rise_state;
self.rise_state += dr * dt;
self.rise_state = self.rise_state.clamp(0.0, 1.0);
let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
let tau_eff = self.tau_decay;
self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
self.open_probability = self.open_probability.clamp(0.0, 1.0);
Ok(())
}
fn get_conductance(&self) -> f64 {
self.g_max * self.open_probability
}
fn reversal_potential(&self) -> f64 {
self.e_rev
}
fn reset(&mut self) {
self.open_probability = 0.0;
self.rise_state = 0.0;
}
}
#[derive(Debug, Clone)]
pub struct NMDAReceptor {
pub open_probability: f64,
rise_state: f64,
pub tau_rise: f64,
pub tau_decay: f64,
pub alpha: f64,
pub beta: f64,
pub e_rev: f64,
pub g_max: f64,
pub mg_concentration: f64,
}
impl Default for NMDAReceptor {
fn default() -> Self {
Self {
open_probability: 0.0,
rise_state: 0.0,
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, }
}
}
impl NMDAReceptor {
pub fn new() -> Self {
Self::default()
}
pub fn with_params(tau_rise: f64, tau_decay: f64, g_max: f64) -> Result<Self> {
if tau_rise <= 0.0 {
return Err(SynapseError::InvalidTimeConstant(tau_rise));
}
if tau_decay <= 0.0 {
return Err(SynapseError::InvalidTimeConstant(tau_decay));
}
Ok(Self {
tau_rise,
tau_decay,
g_max,
..Self::default()
})
}
pub fn mg_block(&self, voltage: f64) -> f64 {
1.0 / (1.0 + (self.mg_concentration / 3.57) * (-0.062 * voltage).exp())
}
pub fn current(&self, voltage: f64) -> f64 {
let mg_block = self.mg_block(voltage);
self.g_max * self.open_probability * mg_block * (voltage - self.e_rev)
}
}
impl ReceptorDynamics for NMDAReceptor {
fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
- self.beta * self.rise_state;
self.rise_state += dr * dt;
self.rise_state = self.rise_state.clamp(0.0, 1.0);
let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
let tau_eff = self.tau_decay;
self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
self.open_probability = self.open_probability.clamp(0.0, 1.0);
Ok(())
}
fn get_conductance(&self) -> f64 {
self.g_max * self.open_probability
}
fn reversal_potential(&self) -> f64 {
self.e_rev
}
fn reset(&mut self) {
self.open_probability = 0.0;
self.rise_state = 0.0;
}
}
#[derive(Debug, Clone)]
pub struct GABAAReceptor {
pub open_probability: f64,
rise_state: f64,
pub tau_rise: f64,
pub tau_decay: f64,
pub alpha: f64,
pub beta: f64,
pub e_rev: f64,
pub g_max: f64,
}
impl Default for GABAAReceptor {
fn default() -> Self {
Self {
open_probability: 0.0,
rise_state: 0.0,
tau_rise: 0.5, tau_decay: 5.0, alpha: 5.0, beta: 0.18, e_rev: -70.0, g_max: 1.0, }
}
}
impl GABAAReceptor {
pub fn new() -> Self {
Self::default()
}
pub fn current(&self, voltage: f64) -> f64 {
self.g_max * self.open_probability * (voltage - self.e_rev)
}
}
impl ReceptorDynamics for GABAAReceptor {
fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
let dr = self.alpha * nt_concentration * (1.0 - self.rise_state)
- self.beta * self.rise_state;
self.rise_state += dr * dt;
self.rise_state = self.rise_state.clamp(0.0, 1.0);
let target = self.rise_state * self.tau_decay / (self.tau_rise + self.tau_decay);
let tau_eff = self.tau_decay;
self.open_probability += (target - self.open_probability) * (1.0 - (-dt / tau_eff).exp());
self.open_probability = self.open_probability.clamp(0.0, 1.0);
Ok(())
}
fn get_conductance(&self) -> f64 {
self.g_max * self.open_probability
}
fn reversal_potential(&self) -> f64 {
self.e_rev
}
fn reset(&mut self) {
self.open_probability = 0.0;
self.rise_state = 0.0;
}
}
#[derive(Debug, Clone)]
pub struct GABABReceptor {
pub activation: f64,
pub g_protein: f64,
pub tau_rise: f64,
pub tau_decay: f64,
pub tau_gprotein: f64,
pub alpha: f64,
pub beta: f64,
pub e_rev: f64,
pub g_max: f64,
}
impl Default for GABABReceptor {
fn default() -> Self {
Self {
activation: 0.0,
g_protein: 0.0,
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, }
}
}
impl GABABReceptor {
pub fn new() -> Self {
Self::default()
}
pub fn current(&self, voltage: f64) -> f64 {
self.g_max * self.g_protein * (voltage - self.e_rev)
}
}
impl ReceptorDynamics for GABABReceptor {
fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
let dr = self.alpha * nt_concentration * (1.0 - self.activation)
- self.beta * self.activation;
self.activation += dr * dt;
self.activation = self.activation.clamp(0.0, 1.0);
let dg = (self.activation - self.g_protein) / self.tau_gprotein;
self.g_protein += dg * dt;
self.g_protein = self.g_protein.clamp(0.0, 1.0);
Ok(())
}
fn get_conductance(&self) -> f64 {
self.g_max * self.g_protein
}
fn reversal_potential(&self) -> f64 {
self.e_rev
}
fn reset(&mut self) {
self.activation = 0.0;
self.g_protein = 0.0;
}
}
#[derive(Debug, Clone)]
pub struct MetabotropicGlutamateReceptor {
pub activation: f64,
pub g_protein: f64,
pub tau_activation: f64,
pub tau_deactivation: f64,
pub alpha: f64,
pub beta: f64,
}
impl Default for MetabotropicGlutamateReceptor {
fn default() -> Self {
Self {
activation: 0.0,
g_protein: 0.0,
tau_activation: 100.0, tau_deactivation: 500.0, alpha: 0.05, beta: 0.002, }
}
}
impl MetabotropicGlutamateReceptor {
pub fn new() -> Self {
Self::default()
}
pub fn get_gprotein_activation(&self) -> f64 {
self.g_protein
}
}
impl ReceptorDynamics for MetabotropicGlutamateReceptor {
fn update(&mut self, nt_concentration: f64, _voltage: f64, dt: f64) -> Result<()> {
let dr = self.alpha * nt_concentration * (1.0 - self.activation)
- self.beta * self.activation;
self.activation += dr * dt;
self.activation = self.activation.clamp(0.0, 1.0);
let tau = if self.activation > self.g_protein {
self.tau_activation
} else {
self.tau_deactivation
};
let dg = (self.activation - self.g_protein) / tau;
self.g_protein += dg * dt;
self.g_protein = self.g_protein.clamp(0.0, 1.0);
Ok(())
}
fn get_conductance(&self) -> f64 {
0.0
}
fn reversal_potential(&self) -> f64 {
0.0
}
fn reset(&mut self) {
self.activation = 0.0;
self.g_protein = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ampa_receptor_creation() {
let ampa = AMPAReceptor::new();
assert_eq!(ampa.open_probability, 0.0);
assert_eq!(ampa.e_rev, 0.0);
}
#[test]
fn test_ampa_receptor_activation() {
let mut ampa = AMPAReceptor::new();
let nt_conc = 1.0; let voltage = -65.0;
let dt = 0.1;
for _ in 0..100 {
ampa.update(nt_conc, voltage, dt).unwrap();
}
assert!(ampa.open_probability > 0.0);
assert!(ampa.open_probability <= 1.0);
}
#[test]
fn test_nmda_mg_block() {
let nmda = NMDAReceptor::new();
let block_hyperpol = nmda.mg_block(-70.0);
assert!(block_hyperpol < 0.1);
let block_depol = nmda.mg_block(0.0);
assert!(block_depol > block_hyperpol);
let block_strong_depol = nmda.mg_block(40.0);
assert!(block_strong_depol > 0.8);
}
#[test]
fn test_nmda_receptor_kinetics() {
let mut nmda = NMDAReceptor::new();
let nt_conc = 0.5;
let voltage = 0.0;
let dt = 0.1;
for _ in 0..1000 {
nmda.update(nt_conc, voltage, dt).unwrap();
}
assert!(nmda.open_probability > 0.0);
}
#[test]
fn test_gabaa_receptor() {
let mut gabaa = GABAAReceptor::new();
assert_eq!(gabaa.e_rev, -70.0);
gabaa.update(1.0, -65.0, 0.1).unwrap();
assert!(gabaa.open_probability >= 0.0);
}
#[test]
fn test_gabab_receptor_gprotein() {
let mut gabab = GABABReceptor::new();
for _ in 0..1000 {
gabab.update(1.0, -65.0, 0.1).unwrap();
}
assert!(gabab.g_protein > 0.0);
assert!(gabab.activation > 0.0);
}
#[test]
fn test_receptor_reset() {
let mut ampa = AMPAReceptor::new();
ampa.update(1.0, -65.0, 0.1).unwrap();
ampa.reset();
assert_eq!(ampa.open_probability, 0.0);
assert_eq!(ampa.rise_state, 0.0);
}
}