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| {
420 a.timestamp()
421 .partial_cmp(&b.timestamp())
422 .expect("Operation failed")
423 });
424 Ok(events)
425 }
426
427 pub fn analyze_spike_patterns(events: &[SpikeEvent]) -> SpikePatternAnalysis {
432 if events.is_empty() {
433 return SpikePatternAnalysis::default();
434 }
435
436 let total_events = events.len();
437 let time_span = events.last().expect("Operation failed").timestamp()
438 - events.first().expect("Operation failed").timestamp();
439 let avg_rate = if time_span > 0.0 {
440 total_events as f64 / time_span
441 } else {
442 0.0
443 };
444
445 let mut intervals = Vec::new();
447 for i in 1..events.len() {
448 intervals.push(events[i].timestamp() - events[i - 1].timestamp());
449 }
450
451 let avg_interval = if !intervals.is_empty() {
452 intervals.iter().sum::<f64>() / intervals.len() as f64
453 } else {
454 0.0
455 };
456
457 let interval_var = if intervals.len() > 1 {
459 let mean = avg_interval;
460 let variance =
461 intervals.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / intervals.len() as f64;
462 variance.sqrt() / mean.max(1e-10)
463 } else {
464 0.0
465 };
466
467 SpikePatternAnalysis {
468 total_spikes: total_events,
469 time_span,
470 average_rate: avg_rate,
471 average_interval: avg_interval,
472 regularity: 1.0 / (1.0 + interval_var), unique_neurons: events
474 .iter()
475 .map(|e| e.neuron_id())
476 .collect::<std::collections::HashSet<_>>()
477 .len(),
478 }
479 }
480
481 #[derive(Debug, Clone)]
483 pub struct SpikePatternAnalysis {
484 pub total_spikes: usize,
486 pub time_span: f64,
488 pub average_rate: f64,
490 pub average_interval: f64,
492 pub regularity: f64,
494 pub unique_neurons: usize,
496 }
497
498 impl Default for SpikePatternAnalysis {
499 fn default() -> Self {
500 Self {
501 total_spikes: 0,
502 time_span: 0.0,
503 average_rate: 0.0,
504 average_interval: 0.0,
505 regularity: 0.0,
506 unique_neurons: 0,
507 }
508 }
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use super::*;
515 use scirs2_core::ndarray::Array2;
516
517 #[test]
518 fn test_neuromorphic_config() {
519 let config = NeuromorphicConfig::new()
520 .with_neurons(5)
521 .with_input_dims(3)
522 .with_capability(NeuromorphicCapability::HomeostaticRegulation)
523 .without_capability(&NeuromorphicCapability::SpikePlasticity);
524
525 assert_eq!(config.num_neurons, 5);
526 assert_eq!(config.input_dims, 3);
527 assert!(config.has_capability(&NeuromorphicCapability::HomeostaticRegulation));
528 assert!(!config.has_capability(&NeuromorphicCapability::SpikePlasticity));
529 }
530
531 #[test]
532 fn test_neuromorphic_factory() {
533 let config = NeuromorphicConfig::new()
534 .with_neurons(3)
535 .with_input_dims(2)
536 .with_capability(NeuromorphicCapability::CompetitiveDynamics);
537
538 let spiking_clusterer = NeuromorphicFactory::create_spiking_clusterer(&config);
539 assert_eq!(spiking_clusterer.num_clusters(), 3);
540 assert!(spiking_clusterer.is_lateral_inhibition_enabled());
541
542 let competitive_clusterer = NeuromorphicFactory::create_competitive_clusterer(&config);
543 assert_eq!(competitive_clusterer.num_clusters(), 3);
544
545 let processor = NeuromorphicFactory::create_processor(&config);
546 assert!(!processor.is_memristive_enabled()); }
548
549 #[test]
550 fn test_utils_spatial_to_spikes() {
551 let data =
552 Array2::from_shape_vec((2, 2), vec![0.0, 1.0, -1.0, 0.5]).expect("Operation failed");
553 let events = utils::spatial_to_spikes(&data.view(), 1.0, 10.0).expect("Operation failed");
554
555 assert!(!events.is_empty());
557
558 for i in 1..events.len() {
560 assert!(events[i - 1].timestamp() <= events[i].timestamp());
561 }
562 }
563
564 #[test]
565 fn test_utils_spike_pattern_analysis() {
566 let events = vec![
567 SpikeEvent::new(0, 0.0, 1.0, vec![0.0, 0.0]),
568 SpikeEvent::new(1, 1.0, 1.0, vec![1.0, 0.0]),
569 SpikeEvent::new(0, 2.0, 1.0, vec![0.0, 1.0]),
570 SpikeEvent::new(2, 3.0, 1.0, vec![1.0, 1.0]),
571 ];
572
573 let analysis = utils::analyze_spike_patterns(&events);
574 assert_eq!(analysis.total_spikes, 4);
575 assert_eq!(analysis.time_span, 3.0);
576 assert!(analysis.average_rate > 0.0);
577 assert_eq!(analysis.unique_neurons, 3);
578 }
579
580 #[test]
581 fn test_empty_spike_analysis() {
582 let events = Vec::new();
583 let analysis = utils::analyze_spike_patterns(&events);
584 assert_eq!(analysis.total_spikes, 0);
585 assert_eq!(analysis.time_span, 0.0);
586 assert_eq!(analysis.average_rate, 0.0);
587 }
588}