scirs2_spatial/neuromorphic/
mod.rs1pub mod algorithms;
136pub mod core;
137
138pub use core::events::{SpikeEvent, SpikeSequence};
140pub use core::neurons::{AdaptiveSpikingNeuron, SpikingNeuron};
141pub use core::synapses::{HomeostaticSynapse, MetaplasticSynapse, Synapse};
142
143pub use algorithms::competitive_learning::{
145 AdaptationScale, CompetitiveNeuralClusterer, HomeostaticNeuralClusterer, HomeostaticNeuron,
146 LearningRateAdaptation, MetaplasticityController, MultiTimescaleAdaptation,
147};
148pub use algorithms::memristive_learning::{
149 AdvancedMemristiveLearning, ConsolidationEvent, ConsolidationRules, ConsolidationType,
150 ForgettingProtectionRules, HomeostaticMechanism, HomeostaticSystem, LearningHistory,
151 LearningRateAdaptation as MemristiveLearningRateAdaptation, MemristiveCrossbar,
152 MemristiveDeviceType, MetaplasticityRules, NeuromodulationEffects, NeuromodulationSystem,
153 NeuromodulatorReleasePatterns, PerformanceMetrics, PlasticityEvent, PlasticityEventType,
154 PlasticityLearningRates, PlasticityMechanism, PlasticityThresholds, PlasticityTimeConstants,
155 PlasticityType, ThresholdAdaptation, TrainingResult,
156};
157pub use algorithms::processing::NeuromorphicProcessor;
158pub use algorithms::spiking_clustering::{NetworkStats, SpikingNeuralClusterer};
159
160pub trait NeuromorphicAlgorithm<T> {
165 type Input;
167 type Output;
169 type Error;
171
172 fn fit(&mut self, data: &Self::Input) -> Result<Self::Output, Self::Error>;
174
175 fn predict(&self, data: &Self::Input) -> Result<Self::Output, Self::Error>;
177
178 fn parameters(&self) -> T;
180
181 fn reset(&mut self);
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum NeuromorphicCapability {
191 SpikePlasticity,
193 HomeostaticRegulation,
195 CompetitiveDynamics,
197 MemristiveComputing,
199 EventDrivenProcessing,
201 TemporalCoding,
203 Neuromodulation,
205 MemoryConsolidation,
207 OnlineLearning,
209 ForgettingProtection,
211}
212
213#[derive(Debug, Clone)]
218pub struct NeuromorphicConfig {
219 pub capabilities: Vec<NeuromorphicCapability>,
221 pub num_neurons: usize,
223 pub input_dims: usize,
225 pub learning_rate: f64,
227 pub spike_threshold: f64,
229 pub time_step: f64,
231 pub max_time: f64,
233 pub debug_mode: bool,
235}
236
237impl Default for NeuromorphicConfig {
238 fn default() -> Self {
239 Self {
240 capabilities: vec![
241 NeuromorphicCapability::SpikePlasticity,
242 NeuromorphicCapability::EventDrivenProcessing,
243 ],
244 num_neurons: 10,
245 input_dims: 2,
246 learning_rate: 0.01,
247 spike_threshold: 1.0,
248 time_step: 0.1,
249 max_time: 100.0,
250 debug_mode: false,
251 }
252 }
253}
254
255impl NeuromorphicConfig {
256 pub fn new() -> Self {
258 Self::default()
259 }
260
261 pub fn with_neurons(mut self, num_neurons: usize) -> Self {
263 self.num_neurons = num_neurons;
264 self
265 }
266
267 pub fn with_input_dims(mut self, input_dims: usize) -> Self {
269 self.input_dims = input_dims;
270 self
271 }
272
273 pub fn with_learning_rate(mut self, learning_rate: f64) -> Self {
275 self.learning_rate = learning_rate;
276 self
277 }
278
279 pub fn with_capability(mut self, capability: NeuromorphicCapability) -> Self {
281 if !self.capabilities.contains(&capability) {
282 self.capabilities.push(capability);
283 }
284 self
285 }
286
287 pub fn without_capability(mut self, capability: &NeuromorphicCapability) -> Self {
289 self.capabilities.retain(|c| c != capability);
290 self
291 }
292
293 pub fn with_debug(mut self, debug: bool) -> Self {
295 self.debug_mode = debug;
296 self
297 }
298
299 pub fn has_capability(&self, capability: &NeuromorphicCapability) -> bool {
301 self.capabilities.contains(capability)
302 }
303}
304
305pub struct NeuromorphicFactory;
310
311impl NeuromorphicFactory {
312 pub fn create_spiking_clusterer(config: &NeuromorphicConfig) -> SpikingNeuralClusterer {
314 let mut clusterer = SpikingNeuralClusterer::new(config.num_neurons)
315 .with_spike_threshold(config.spike_threshold)
316 .with_time_step(config.time_step);
317
318 if config.has_capability(&NeuromorphicCapability::SpikePlasticity) {
319 clusterer = clusterer.with_stdp_learning(true);
320 }
321
322 if config.has_capability(&NeuromorphicCapability::CompetitiveDynamics) {
323 clusterer = clusterer.with_lateral_inhibition(true);
324 }
325
326 clusterer
327 }
328
329 pub fn create_competitive_clusterer(config: &NeuromorphicConfig) -> CompetitiveNeuralClusterer {
331 CompetitiveNeuralClusterer::new(config.num_neurons, config.input_dims)
332 }
333
334 pub fn create_homeostatic_clusterer(config: &NeuromorphicConfig) -> HomeostaticNeuralClusterer {
336 let mut clusterer = HomeostaticNeuralClusterer::new(config.num_neurons, config.input_dims);
337
338 if config.has_capability(&NeuromorphicCapability::HomeostaticRegulation) {
339 clusterer = clusterer.with_homeostatic_params(0.1, 1000.0);
340 }
341
342 clusterer
343 }
344
345 pub fn create_memristive_system(
347 config: &NeuromorphicConfig,
348 device_type: MemristiveDeviceType,
349 ) -> AdvancedMemristiveLearning {
350 let mut system =
351 AdvancedMemristiveLearning::new(config.input_dims, config.num_neurons, device_type);
352
353 if config.has_capability(&NeuromorphicCapability::ForgettingProtection) {
354 system = system.with_forgetting_protection(true);
355 }
356
357 if config.has_capability(&NeuromorphicCapability::HomeostaticRegulation) {
358 let target_rates = scirs2_core::ndarray::Array1::from_elem(config.num_neurons, 0.1);
359 system = system.with_homeostatic_regulation(target_rates);
360 }
361
362 system
363 }
364
365 pub fn create_processor(config: &NeuromorphicConfig) -> NeuromorphicProcessor {
367 let mut processor = NeuromorphicProcessor::new();
368
369 if config.has_capability(&NeuromorphicCapability::MemristiveComputing) {
370 processor = processor.with_memristive_crossbar(true);
371 }
372
373 if config.has_capability(&NeuromorphicCapability::TemporalCoding) {
374 processor = processor.with_temporal_coding(true);
375 }
376
377 processor
378 }
379}
380
381pub mod utils {
385 use super::*;
386 use crate::error::SpatialResult;
387 use scirs2_core::ndarray::ArrayView2;
388
389 pub fn spatial_to_spikes(
394 data: &ArrayView2<f64>,
395 time_window: f64,
396 max_rate: f64,
397 ) -> SpatialResult<Vec<SpikeEvent>> {
398 let (n_points, n_dims) = data.dim();
399 let mut events = Vec::new();
400
401 for (point_idx, point) in data.outer_iter().enumerate() {
402 for (dim, &value) in point.iter().enumerate() {
403 let normalized = (value + 10.0) / 20.0; let spike_rate = normalized.clamp(0.0, 1.0) * max_rate;
406
407 let num_spikes = (spike_rate * time_window) as usize;
409 for spike_idx in 0..num_spikes {
410 let timestamp = (spike_idx as f64) * (time_window / num_spikes as f64);
411 let event =
412 SpikeEvent::new(point_idx * n_dims + dim, timestamp, 1.0, point.to_vec());
413 events.push(event);
414 }
415 }
416 }
417
418 events.sort_by(|a, b| a.timestamp().partial_cmp(&b.timestamp()).unwrap());
420 Ok(events)
421 }
422
423 pub fn analyze_spike_patterns(events: &[SpikeEvent]) -> SpikePatternAnalysis {
428 if events.is_empty() {
429 return SpikePatternAnalysis::default();
430 }
431
432 let total_events = events.len();
433 let time_span = events.last().unwrap().timestamp() - events.first().unwrap().timestamp();
434 let avg_rate = if time_span > 0.0 {
435 total_events as f64 / time_span
436 } else {
437 0.0
438 };
439
440 let mut intervals = Vec::new();
442 for i in 1..events.len() {
443 intervals.push(events[i].timestamp() - events[i - 1].timestamp());
444 }
445
446 let avg_interval = if !intervals.is_empty() {
447 intervals.iter().sum::<f64>() / intervals.len() as f64
448 } else {
449 0.0
450 };
451
452 let interval_var = if intervals.len() > 1 {
454 let mean = avg_interval;
455 let variance =
456 intervals.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / intervals.len() as f64;
457 variance.sqrt() / mean.max(1e-10)
458 } else {
459 0.0
460 };
461
462 SpikePatternAnalysis {
463 total_spikes: total_events,
464 time_span,
465 average_rate: avg_rate,
466 average_interval: avg_interval,
467 regularity: 1.0 / (1.0 + interval_var), unique_neurons: events
469 .iter()
470 .map(|e| e.neuron_id())
471 .collect::<std::collections::HashSet<_>>()
472 .len(),
473 }
474 }
475
476 #[derive(Debug, Clone)]
478 pub struct SpikePatternAnalysis {
479 pub total_spikes: usize,
481 pub time_span: f64,
483 pub average_rate: f64,
485 pub average_interval: f64,
487 pub regularity: f64,
489 pub unique_neurons: usize,
491 }
492
493 impl Default for SpikePatternAnalysis {
494 fn default() -> Self {
495 Self {
496 total_spikes: 0,
497 time_span: 0.0,
498 average_rate: 0.0,
499 average_interval: 0.0,
500 regularity: 0.0,
501 unique_neurons: 0,
502 }
503 }
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510 use scirs2_core::ndarray::Array2;
511
512 #[test]
513 fn test_neuromorphic_config() {
514 let config = NeuromorphicConfig::new()
515 .with_neurons(5)
516 .with_input_dims(3)
517 .with_capability(NeuromorphicCapability::HomeostaticRegulation)
518 .without_capability(&NeuromorphicCapability::SpikePlasticity);
519
520 assert_eq!(config.num_neurons, 5);
521 assert_eq!(config.input_dims, 3);
522 assert!(config.has_capability(&NeuromorphicCapability::HomeostaticRegulation));
523 assert!(!config.has_capability(&NeuromorphicCapability::SpikePlasticity));
524 }
525
526 #[test]
527 fn test_neuromorphic_factory() {
528 let config = NeuromorphicConfig::new()
529 .with_neurons(3)
530 .with_input_dims(2)
531 .with_capability(NeuromorphicCapability::CompetitiveDynamics);
532
533 let spiking_clusterer = NeuromorphicFactory::create_spiking_clusterer(&config);
534 assert_eq!(spiking_clusterer.num_clusters(), 3);
535 assert!(spiking_clusterer.is_lateral_inhibition_enabled());
536
537 let competitive_clusterer = NeuromorphicFactory::create_competitive_clusterer(&config);
538 assert_eq!(competitive_clusterer.num_clusters(), 3);
539
540 let processor = NeuromorphicFactory::create_processor(&config);
541 assert!(!processor.is_memristive_enabled()); }
543
544 #[test]
545 fn test_utils_spatial_to_spikes() {
546 let data = Array2::from_shape_vec((2, 2), vec![0.0, 1.0, -1.0, 0.5]).unwrap();
547 let events = utils::spatial_to_spikes(&data.view(), 1.0, 10.0).unwrap();
548
549 assert!(!events.is_empty());
551
552 for i in 1..events.len() {
554 assert!(events[i - 1].timestamp() <= events[i].timestamp());
555 }
556 }
557
558 #[test]
559 fn test_utils_spike_pattern_analysis() {
560 let events = vec![
561 SpikeEvent::new(0, 0.0, 1.0, vec![0.0, 0.0]),
562 SpikeEvent::new(1, 1.0, 1.0, vec![1.0, 0.0]),
563 SpikeEvent::new(0, 2.0, 1.0, vec![0.0, 1.0]),
564 SpikeEvent::new(2, 3.0, 1.0, vec![1.0, 1.0]),
565 ];
566
567 let analysis = utils::analyze_spike_patterns(&events);
568 assert_eq!(analysis.total_spikes, 4);
569 assert_eq!(analysis.time_span, 3.0);
570 assert!(analysis.average_rate > 0.0);
571 assert_eq!(analysis.unique_neurons, 3);
572 }
573
574 #[test]
575 fn test_empty_spike_analysis() {
576 let events = Vec::new();
577 let analysis = utils::analyze_spike_patterns(&events);
578 assert_eq!(analysis.total_spikes, 0);
579 assert_eq!(analysis.time_span, 0.0);
580 assert_eq!(analysis.average_rate, 0.0);
581 }
582}