#[derive(Debug, Clone)]
pub struct SpikingNeuron {
pub membrane_potential: f64,
pub threshold: f64,
pub refractory_period: f64,
pub time_since_spike: f64,
pub leak_constant: f64,
pub input_current: f64,
pub position: Vec<f64>,
pub learning_rate: f64,
}
impl SpikingNeuron {
pub fn new(position: Vec<f64>) -> Self {
Self {
membrane_potential: 0.0,
threshold: 1.0,
refractory_period: 2.0,
time_since_spike: 2.1, leak_constant: 0.1,
input_current: 0.0,
position,
learning_rate: 0.01,
}
}
pub fn with_params(
position: Vec<f64>,
threshold: f64,
refractory_period: f64,
leak_constant: f64,
learning_rate: f64,
) -> Self {
Self {
membrane_potential: 0.0,
threshold,
refractory_period,
time_since_spike: refractory_period + 0.1, leak_constant,
input_current: 0.0,
position,
learning_rate,
}
}
pub fn update(&mut self, dt: f64, input_current: f64) -> bool {
self.time_since_spike += dt;
if self.time_since_spike < self.refractory_period {
return false;
}
self.input_current = input_current;
let leak_term = -self.leak_constant * self.membrane_potential;
self.membrane_potential += dt * (leak_term + input_current);
if self.membrane_potential >= self.threshold {
self.membrane_potential = 0.0; self.time_since_spike = 0.0; true
} else {
false
}
}
pub fn calculate_influence(&self, other_position: &[f64]) -> f64 {
if self.position.len() != other_position.len() {
return 0.0;
}
let distance: f64 = self
.position
.iter()
.zip(other_position.iter())
.map(|(&a, &b)| (a - b).powi(2))
.sum::<f64>()
.sqrt();
(-distance.powi(2) / 2.0).exp()
}
pub fn position(&self) -> &[f64] {
&self.position
}
pub fn set_position(&mut self, position: Vec<f64>) {
self.position = position;
}
pub fn membrane_potential(&self) -> f64 {
self.membrane_potential
}
pub fn set_membrane_potential(&mut self, potential: f64) {
self.membrane_potential = potential;
}
pub fn threshold(&self) -> f64 {
self.threshold
}
pub fn set_threshold(&mut self, threshold: f64) {
self.threshold = threshold;
}
pub fn refractory_period(&self) -> f64 {
self.refractory_period
}
pub fn set_refractory_period(&mut self, period: f64) {
self.refractory_period = period;
}
pub fn leak_constant(&self) -> f64 {
self.leak_constant
}
pub fn set_leak_constant(&mut self, leak: f64) {
self.leak_constant = leak;
}
pub fn learning_rate(&self) -> f64 {
self.learning_rate
}
pub fn set_learning_rate(&mut self, rate: f64) {
self.learning_rate = rate;
}
pub fn is_refractory(&self) -> bool {
self.time_since_spike < self.refractory_period
}
pub fn time_since_spike(&self) -> f64 {
self.time_since_spike
}
pub fn reset(&mut self) {
self.membrane_potential = 0.0;
self.time_since_spike = 0.0;
self.input_current = 0.0;
}
pub fn inject_current(&mut self, current: f64) {
self.input_current += current;
}
pub fn distance_to(&self, other: &SpikingNeuron) -> Option<f64> {
if self.position.len() != other.position.len() {
return None;
}
let distance = self
.position
.iter()
.zip(other.position.iter())
.map(|(&a, &b)| (a - b).powi(2))
.sum::<f64>()
.sqrt();
Some(distance)
}
pub fn adapt_threshold(&mut self, target_rate: f64, actual_rate: f64, adaptation_rate: f64) {
let rate_error = actual_rate - target_rate;
self.threshold += adaptation_rate * rate_error;
self.threshold = self.threshold.clamp(0.1, 10.0);
}
pub fn adapt_learning_rate(&mut self, performance_factor: f64, adaptation_rate: f64) {
let adjustment = adaptation_rate * (1.0 - performance_factor);
self.learning_rate += adjustment;
self.learning_rate = self.learning_rate.clamp(0.001, 1.0);
}
}
#[derive(Debug, Clone)]
pub struct AdaptiveSpikingNeuron {
base_neuron: SpikingNeuron,
target_firing_rate: f64,
recent_firing_rate: f64,
threshold_adaptation_rate: f64,
spike_count: usize,
rate_estimation_window: f64,
current_time: f64,
}
impl AdaptiveSpikingNeuron {
pub fn new(position: Vec<f64>, target_firing_rate: f64) -> Self {
Self {
base_neuron: SpikingNeuron::new(position),
target_firing_rate,
recent_firing_rate: 0.0,
threshold_adaptation_rate: 0.001,
spike_count: 0,
rate_estimation_window: 100.0,
current_time: 0.0,
}
}
pub fn update(&mut self, dt: f64, input_current: f64) -> bool {
self.current_time += dt;
let spiked = self.base_neuron.update(dt, input_current);
if spiked {
self.spike_count += 1;
}
if self.current_time >= self.rate_estimation_window {
self.recent_firing_rate = self.spike_count as f64 / self.current_time;
self.base_neuron.adapt_threshold(
self.target_firing_rate,
self.recent_firing_rate,
self.threshold_adaptation_rate,
);
self.spike_count = 0;
self.current_time = 0.0;
}
spiked
}
pub fn base_neuron(&self) -> &SpikingNeuron {
&self.base_neuron
}
pub fn base_neuron_mut(&mut self) -> &mut SpikingNeuron {
&mut self.base_neuron
}
pub fn target_firing_rate(&self) -> f64 {
self.target_firing_rate
}
pub fn set_target_firing_rate(&mut self, rate: f64) {
self.target_firing_rate = rate;
}
pub fn recent_firing_rate(&self) -> f64 {
self.recent_firing_rate
}
pub fn spike_count(&self) -> usize {
self.spike_count
}
pub fn reset_adaptation(&mut self) {
self.spike_count = 0;
self.current_time = 0.0;
self.recent_firing_rate = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spiking_neuron_creation() {
let neuron = SpikingNeuron::new(vec![0.0, 0.0]);
assert_eq!(neuron.position(), &[0.0, 0.0]);
assert_eq!(neuron.membrane_potential(), 0.0);
assert_eq!(neuron.threshold(), 1.0);
assert!(!neuron.is_refractory());
}
#[test]
fn test_neuron_spiking() {
let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
let spiked = neuron.update(0.1, 0.1);
assert!(!spiked);
assert!(neuron.membrane_potential() > 0.0);
let spiked = neuron.update(0.1, 10.0);
assert!(spiked);
assert_eq!(neuron.membrane_potential(), 0.0); }
#[test]
fn test_refractory_period() {
let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
neuron.update(0.1, 10.0);
assert!(neuron.is_refractory());
let spiked = neuron.update(0.1, 10.0);
assert!(!spiked);
for _ in 0..25 {
neuron.update(0.1, 0.0);
}
assert!(!neuron.is_refractory());
let spiked = neuron.update(0.1, 10.0);
assert!(spiked);
}
#[test]
fn test_neuron_influence() {
let neuron1 = SpikingNeuron::new(vec![0.0, 0.0]);
let neuron2 = SpikingNeuron::new(vec![1.0, 1.0]);
let influence = neuron1.calculate_influence(neuron2.position());
assert!(influence > 0.0 && influence <= 1.0);
let self_influence = neuron1.calculate_influence(neuron1.position());
assert!((self_influence - 1.0).abs() < 1e-10);
}
#[test]
fn test_neuron_distance() {
let neuron1 = SpikingNeuron::new(vec![0.0, 0.0]);
let neuron2 = SpikingNeuron::new(vec![3.0, 4.0]);
let distance = neuron1.distance_to(&neuron2).expect("Operation failed");
assert!((distance - 5.0).abs() < 1e-10);
let neuron3 = SpikingNeuron::new(vec![1.0]);
assert!(neuron1.distance_to(&neuron3).is_none());
}
#[test]
#[ignore = "Test failure - assertion failed: neuron.threshold() < initial_threshold at line 503"]
fn test_threshold_adaptation() {
let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
let initial_threshold = neuron.threshold();
neuron.adapt_threshold(0.1, 0.5, 0.1); assert!(neuron.threshold() > initial_threshold);
neuron.adapt_threshold(0.1, 0.05, 0.1); assert!(neuron.threshold() < initial_threshold);
}
#[test]
fn test_adaptive_neuron() {
let mut adaptive_neuron = AdaptiveSpikingNeuron::new(vec![0.0, 0.0], 0.1);
assert_eq!(adaptive_neuron.target_firing_rate(), 0.1);
assert_eq!(adaptive_neuron.spike_count(), 0);
assert_eq!(adaptive_neuron.recent_firing_rate(), 0.0);
for _ in 0..50 {
adaptive_neuron.update(0.1, 2.0);
}
assert!(adaptive_neuron.spike_count() > 0);
}
#[test]
fn test_neuron_reset() {
let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
neuron.update(0.1, 5.0);
neuron.inject_current(1.0);
neuron.reset();
assert_eq!(neuron.membrane_potential(), 0.0);
assert_eq!(neuron.time_since_spike(), 0.0);
assert_eq!(neuron.input_current, 0.0);
}
#[test]
fn test_parameter_setters() {
let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
neuron.set_threshold(2.0);
assert_eq!(neuron.threshold(), 2.0);
neuron.set_refractory_period(5.0);
assert_eq!(neuron.refractory_period(), 5.0);
neuron.set_leak_constant(0.2);
assert_eq!(neuron.leak_constant(), 0.2);
neuron.set_learning_rate(0.05);
assert_eq!(neuron.learning_rate(), 0.05);
neuron.set_position(vec![1.0, 2.0, 3.0]);
assert_eq!(neuron.position(), &[1.0, 2.0, 3.0]);
}
}