scirs2_spatial/neuromorphic/core/
neurons.rs1#[derive(Debug, Clone)]
37pub struct SpikingNeuron {
38 pub membrane_potential: f64,
40 pub threshold: f64,
42 pub refractory_period: f64,
44 pub time_since_spike: f64,
46 pub leak_constant: f64,
48 pub input_current: f64,
50 pub position: Vec<f64>,
52 pub learning_rate: f64,
54}
55
56impl SpikingNeuron {
57 pub fn new(position: Vec<f64>) -> Self {
65 Self {
66 membrane_potential: 0.0,
67 threshold: 1.0,
68 refractory_period: 2.0,
69 time_since_spike: 2.1, leak_constant: 0.1,
71 input_current: 0.0,
72 position,
73 learning_rate: 0.01,
74 }
75 }
76
77 pub fn with_params(
86 position: Vec<f64>,
87 threshold: f64,
88 refractory_period: f64,
89 leak_constant: f64,
90 learning_rate: f64,
91 ) -> Self {
92 Self {
93 membrane_potential: 0.0,
94 threshold,
95 refractory_period,
96 time_since_spike: refractory_period + 0.1, leak_constant,
98 input_current: 0.0,
99 position,
100 learning_rate,
101 }
102 }
103
104 pub fn update(&mut self, dt: f64, input_current: f64) -> bool {
116 self.time_since_spike += dt;
117
118 if self.time_since_spike < self.refractory_period {
120 return false;
121 }
122
123 self.input_current = input_current;
125 let leak_term = -self.leak_constant * self.membrane_potential;
126 self.membrane_potential += dt * (leak_term + input_current);
127
128 if self.membrane_potential >= self.threshold {
130 self.membrane_potential = 0.0; self.time_since_spike = 0.0; true
133 } else {
134 false
135 }
136 }
137
138 pub fn calculate_influence(&self, other_position: &[f64]) -> f64 {
149 if self.position.len() != other_position.len() {
150 return 0.0;
151 }
152
153 let distance: f64 = self
154 .position
155 .iter()
156 .zip(other_position.iter())
157 .map(|(&a, &b)| (a - b).powi(2))
158 .sum::<f64>()
159 .sqrt();
160
161 (-distance.powi(2) / 2.0).exp()
163 }
164
165 pub fn position(&self) -> &[f64] {
167 &self.position
168 }
169
170 pub fn set_position(&mut self, position: Vec<f64>) {
172 self.position = position;
173 }
174
175 pub fn membrane_potential(&self) -> f64 {
177 self.membrane_potential
178 }
179
180 pub fn set_membrane_potential(&mut self, potential: f64) {
182 self.membrane_potential = potential;
183 }
184
185 pub fn threshold(&self) -> f64 {
187 self.threshold
188 }
189
190 pub fn set_threshold(&mut self, threshold: f64) {
192 self.threshold = threshold;
193 }
194
195 pub fn refractory_period(&self) -> f64 {
197 self.refractory_period
198 }
199
200 pub fn set_refractory_period(&mut self, period: f64) {
202 self.refractory_period = period;
203 }
204
205 pub fn leak_constant(&self) -> f64 {
207 self.leak_constant
208 }
209
210 pub fn set_leak_constant(&mut self, leak: f64) {
212 self.leak_constant = leak;
213 }
214
215 pub fn learning_rate(&self) -> f64 {
217 self.learning_rate
218 }
219
220 pub fn set_learning_rate(&mut self, rate: f64) {
222 self.learning_rate = rate;
223 }
224
225 pub fn is_refractory(&self) -> bool {
227 self.time_since_spike < self.refractory_period
228 }
229
230 pub fn time_since_spike(&self) -> f64 {
232 self.time_since_spike
233 }
234
235 pub fn reset(&mut self) {
237 self.membrane_potential = 0.0;
238 self.time_since_spike = 0.0;
239 self.input_current = 0.0;
240 }
241
242 pub fn inject_current(&mut self, current: f64) {
244 self.input_current += current;
245 }
246
247 pub fn distance_to(&self, other: &SpikingNeuron) -> Option<f64> {
255 if self.position.len() != other.position.len() {
256 return None;
257 }
258
259 let distance = self
260 .position
261 .iter()
262 .zip(other.position.iter())
263 .map(|(&a, &b)| (a - b).powi(2))
264 .sum::<f64>()
265 .sqrt();
266
267 Some(distance)
268 }
269
270 pub fn adapt_threshold(&mut self, target_rate: f64, actual_rate: f64, adaptation_rate: f64) {
277 let rate_error = actual_rate - target_rate;
278 self.threshold += adaptation_rate * rate_error;
279
280 self.threshold = self.threshold.clamp(0.1, 10.0);
282 }
283
284 pub fn adapt_learning_rate(&mut self, performance_factor: f64, adaptation_rate: f64) {
290 let adjustment = adaptation_rate * (1.0 - performance_factor);
292 self.learning_rate += adjustment;
293
294 self.learning_rate = self.learning_rate.clamp(0.001, 1.0);
296 }
297}
298
299#[derive(Debug, Clone)]
304pub struct AdaptiveSpikingNeuron {
305 base_neuron: SpikingNeuron,
307 target_firing_rate: f64,
309 recent_firing_rate: f64,
311 threshold_adaptation_rate: f64,
313 spike_count: usize,
315 rate_estimation_window: f64,
317 current_time: f64,
319}
320
321impl AdaptiveSpikingNeuron {
322 pub fn new(position: Vec<f64>, target_firing_rate: f64) -> Self {
328 Self {
329 base_neuron: SpikingNeuron::new(position),
330 target_firing_rate,
331 recent_firing_rate: 0.0,
332 threshold_adaptation_rate: 0.001,
333 spike_count: 0,
334 rate_estimation_window: 100.0,
335 current_time: 0.0,
336 }
337 }
338
339 pub fn update(&mut self, dt: f64, input_current: f64) -> bool {
348 self.current_time += dt;
349
350 let spiked = self.base_neuron.update(dt, input_current);
352
353 if spiked {
354 self.spike_count += 1;
355 }
356
357 if self.current_time >= self.rate_estimation_window {
359 self.recent_firing_rate = self.spike_count as f64 / self.current_time;
360
361 self.base_neuron.adapt_threshold(
363 self.target_firing_rate,
364 self.recent_firing_rate,
365 self.threshold_adaptation_rate,
366 );
367
368 self.spike_count = 0;
370 self.current_time = 0.0;
371 }
372
373 spiked
374 }
375
376 pub fn base_neuron(&self) -> &SpikingNeuron {
378 &self.base_neuron
379 }
380
381 pub fn base_neuron_mut(&mut self) -> &mut SpikingNeuron {
383 &mut self.base_neuron
384 }
385
386 pub fn target_firing_rate(&self) -> f64 {
388 self.target_firing_rate
389 }
390
391 pub fn set_target_firing_rate(&mut self, rate: f64) {
393 self.target_firing_rate = rate;
394 }
395
396 pub fn recent_firing_rate(&self) -> f64 {
398 self.recent_firing_rate
399 }
400
401 pub fn spike_count(&self) -> usize {
403 self.spike_count
404 }
405
406 pub fn reset_adaptation(&mut self) {
408 self.spike_count = 0;
409 self.current_time = 0.0;
410 self.recent_firing_rate = 0.0;
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_spiking_neuron_creation() {
420 let neuron = SpikingNeuron::new(vec![0.0, 0.0]);
421 assert_eq!(neuron.position(), &[0.0, 0.0]);
422 assert_eq!(neuron.membrane_potential(), 0.0);
423 assert_eq!(neuron.threshold(), 1.0);
424 assert!(!neuron.is_refractory());
425 }
426
427 #[test]
428 fn test_neuron_spiking() {
429 let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
430
431 let spiked = neuron.update(0.1, 0.1);
433 assert!(!spiked);
434 assert!(neuron.membrane_potential() > 0.0);
435
436 let spiked = neuron.update(0.1, 10.0);
438 assert!(spiked);
439 assert_eq!(neuron.membrane_potential(), 0.0); }
441
442 #[test]
443 fn test_refractory_period() {
444 let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
445
446 neuron.update(0.1, 10.0);
448 assert!(neuron.is_refractory());
449
450 let spiked = neuron.update(0.1, 10.0);
452 assert!(!spiked);
453
454 for _ in 0..25 {
456 neuron.update(0.1, 0.0);
458 }
459 assert!(!neuron.is_refractory());
460
461 let spiked = neuron.update(0.1, 10.0);
463 assert!(spiked);
464 }
465
466 #[test]
467 fn test_neuron_influence() {
468 let neuron1 = SpikingNeuron::new(vec![0.0, 0.0]);
469 let neuron2 = SpikingNeuron::new(vec![1.0, 1.0]);
470
471 let influence = neuron1.calculate_influence(neuron2.position());
472 assert!(influence > 0.0 && influence <= 1.0);
473
474 let self_influence = neuron1.calculate_influence(neuron1.position());
476 assert!((self_influence - 1.0).abs() < 1e-10);
477 }
478
479 #[test]
480 fn test_neuron_distance() {
481 let neuron1 = SpikingNeuron::new(vec![0.0, 0.0]);
482 let neuron2 = SpikingNeuron::new(vec![3.0, 4.0]);
483
484 let distance = neuron1.distance_to(&neuron2).unwrap();
485 assert!((distance - 5.0).abs() < 1e-10);
486
487 let neuron3 = SpikingNeuron::new(vec![1.0]);
489 assert!(neuron1.distance_to(&neuron3).is_none());
490 }
491
492 #[test]
493 #[ignore]
494 fn test_threshold_adaptation() {
495 let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
496 let initial_threshold = neuron.threshold();
497
498 neuron.adapt_threshold(0.1, 0.5, 0.1); assert!(neuron.threshold() > initial_threshold);
501
502 neuron.adapt_threshold(0.1, 0.05, 0.1); assert!(neuron.threshold() < initial_threshold);
505 }
506
507 #[test]
508 fn test_adaptive_neuron() {
509 let mut adaptive_neuron = AdaptiveSpikingNeuron::new(vec![0.0, 0.0], 0.1);
510
511 assert_eq!(adaptive_neuron.target_firing_rate(), 0.1);
512 assert_eq!(adaptive_neuron.spike_count(), 0);
513 assert_eq!(adaptive_neuron.recent_firing_rate(), 0.0);
514
515 for _ in 0..50 {
517 adaptive_neuron.update(0.1, 2.0);
518 }
519
520 assert!(adaptive_neuron.spike_count() > 0);
522 }
523
524 #[test]
525 fn test_neuron_reset() {
526 let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
527
528 neuron.update(0.1, 5.0);
530 neuron.inject_current(1.0);
531
532 neuron.reset();
534 assert_eq!(neuron.membrane_potential(), 0.0);
535 assert_eq!(neuron.time_since_spike(), 0.0);
536 assert_eq!(neuron.input_current, 0.0);
537 }
538
539 #[test]
540 fn test_parameter_setters() {
541 let mut neuron = SpikingNeuron::new(vec![0.0, 0.0]);
542
543 neuron.set_threshold(2.0);
544 assert_eq!(neuron.threshold(), 2.0);
545
546 neuron.set_refractory_period(5.0);
547 assert_eq!(neuron.refractory_period(), 5.0);
548
549 neuron.set_leak_constant(0.2);
550 assert_eq!(neuron.leak_constant(), 0.2);
551
552 neuron.set_learning_rate(0.05);
553 assert_eq!(neuron.learning_rate(), 0.05);
554
555 neuron.set_position(vec![1.0, 2.0, 3.0]);
556 assert_eq!(neuron.position(), &[1.0, 2.0, 3.0]);
557 }
558}