use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct IzhikevichNeuron {
pub v: f32, pub u: f32, pub last_spike_time: i64,
pub a: f32, pub b: f32, pub c: f32, pub d: f32, }
impl IzhikevichNeuron {
pub fn new_regular_spiking() -> Self {
let a = 0.02;
let b = 0.2;
let c = -65.0;
Self { v: c, u: b * c, last_spike_time: -1, a, b, c, d: 8.0 }
}
pub fn new_bursting() -> Self {
let a = 0.02;
let b = 0.2;
let c = -55.0;
Self { v: c, u: b * c, last_spike_time: -1, a, b, c, d: 4.0 }
}
pub fn new_fast_spiking() -> Self {
let a = 0.1;
let b = 0.2;
let c = -65.0;
Self { v: c, u: b * c, last_spike_time: -1, a, b, c, d: 2.0 }
}
pub fn new_chattering() -> Self {
let a = 0.02;
let b = 0.2;
let c = -50.0;
Self { v: c, u: b * c, last_spike_time: -1, a, b, c, d: 2.0 }
}
pub fn new_low_threshold() -> Self {
let a = 0.02;
let b = 0.25;
let c = -65.0;
Self { v: c, u: b * c, last_spike_time: -1, a, b, c, d: 2.0 }
}
pub fn step(&mut self, i: f32) -> bool {
self.step_with_time(i, 0)
}
pub fn step_with_time(&mut self, i: f32, current_time: i64) -> bool {
for _ in 0..2 {
self.v += 0.04 * self.v * self.v + 5.0 * self.v + 140.0 - self.u + i;
}
self.u += self.a * (self.b * self.v - self.u);
if self.v >= 30.0 {
self.v = self.c;
self.u += self.d;
self.last_spike_time = current_time;
true
} else {
false
}
}
pub fn reset(&mut self) {
self.v = self.c;
self.u = self.b * self.c;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_regular_spiking_fires_under_sustained_input() {
let mut n = IzhikevichNeuron::new_regular_spiking();
let spikes: usize = (0..100).filter(|_| n.step(10.0)).count();
assert!(spikes > 0, "RS neuron should fire under sustained 10 pA input");
}
#[test]
fn test_fast_spiking_higher_rate_than_regular() {
let mut rs = IzhikevichNeuron::new_regular_spiking();
let mut fs = IzhikevichNeuron::new_fast_spiking();
let rs_spikes: usize = (0..200).filter(|_| rs.step(10.0)).count();
let fs_spikes: usize = (0..200).filter(|_| fs.step(10.0)).count();
assert!(fs_spikes >= rs_spikes, "FS should fire at least as fast as RS");
}
#[test]
fn test_reset_restores_resting_state() {
let mut n = IzhikevichNeuron::new_regular_spiking();
for _ in 0..50 { n.step(10.0); }
n.reset();
assert_eq!(n.v, n.c);
assert!((n.u - n.b * n.c).abs() < 1e-6);
}
#[test]
fn test_no_spike_at_rest() {
let mut n = IzhikevichNeuron::new_regular_spiking();
let spikes: usize = (0..10).filter(|_| n.step(0.0)).count();
assert_eq!(spikes, 0, "No spike without input");
}
}