use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct LapicqueNeuron {
pub membrane_potential: f32,
pub decay_rate: f32,
pub threshold: f32,
pub base_threshold: f32,
pub last_spike: bool,
pub weights: Vec<f32>,
pub last_spike_time: i64,
}
impl Default for LapicqueNeuron {
fn default() -> Self {
Self {
membrane_potential: 0.0,
decay_rate: 0.15,
threshold: 0.02,
base_threshold: 0.02,
last_spike: false,
weights: Vec::new(),
last_spike_time: -1,
}
}
}
impl LapicqueNeuron {
pub fn new() -> Self {
Self::default()
}
pub fn integrate(&mut self, stimulus: f32) {
self.membrane_potential += stimulus;
self.membrane_potential *= 1.0 - self.decay_rate;
}
pub fn check_for_spike(&mut self, current_time: i64) -> bool {
if self.membrane_potential >= self.threshold {
self.membrane_potential = 0.0;
self.last_spike = true;
self.last_spike_time = current_time;
true
} else {
self.last_spike = false;
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_spike_without_input() {
let mut n = LapicqueNeuron::new();
for t in 0..100 {
n.integrate(0.0);
assert!(!n.check_for_spike(t), "should not spike without input");
}
}
#[test]
fn test_fires_with_sufficient_input() {
let mut n = LapicqueNeuron::new();
let mut fired = false;
for t in 0..1000 {
n.integrate(0.05);
if n.check_for_spike(t) {
fired = true;
break;
}
}
assert!(fired, "Lapicque neuron should fire with sustained suprathreshold input");
}
#[test]
fn test_reset_after_spike() {
let mut n = LapicqueNeuron::new();
n.membrane_potential = 1.0; n.check_for_spike(0);
assert_eq!(n.membrane_potential, 0.0, "potential should reset to 0 after spike");
}
#[test]
fn test_spike_time_recorded() {
let mut n = LapicqueNeuron::new();
n.membrane_potential = 1.0;
n.check_for_spike(42);
assert_eq!(n.last_spike_time, 42);
}
#[test]
fn test_leak_reduces_potential() {
let mut n = LapicqueNeuron::new();
n.membrane_potential = 1.0;
n.integrate(0.0);
assert!(n.membrane_potential < 1.0, "leak should reduce membrane potential");
}
}