pub mod algorithms;
pub mod core;
pub use core::events::{SpikeEvent, SpikeSequence};
pub use core::neurons::{AdaptiveSpikingNeuron, SpikingNeuron};
pub use core::synapses::{HomeostaticSynapse, MetaplasticSynapse, Synapse};
pub use algorithms::competitive_learning::{
AdaptationScale, CompetitiveNeuralClusterer, HomeostaticNeuralClusterer, HomeostaticNeuron,
LearningRateAdaptation, MetaplasticityController, MultiTimescaleAdaptation,
};
pub use algorithms::memristive_learning::{
AdvancedMemristiveLearning, ConsolidationEvent, ConsolidationRules, ConsolidationType,
ForgettingProtectionRules, HomeostaticMechanism, HomeostaticSystem, LearningHistory,
LearningRateAdaptation as MemristiveLearningRateAdaptation, MemristiveCrossbar,
MemristiveDeviceType, MetaplasticityRules, NeuromodulationEffects, NeuromodulationSystem,
NeuromodulatorReleasePatterns, PerformanceMetrics, PlasticityEvent, PlasticityEventType,
PlasticityLearningRates, PlasticityMechanism, PlasticityThresholds, PlasticityTimeConstants,
PlasticityType, ThresholdAdaptation, TrainingResult,
};
pub use algorithms::processing::NeuromorphicProcessor;
pub use algorithms::spiking_clustering::{NetworkStats, SpikingNeuralClusterer};
pub trait NeuromorphicAlgorithm<T> {
type Input;
type Output;
type Error;
fn fit(&mut self, data: &Self::Input) -> Result<Self::Output, Self::Error>;
fn predict(&self, data: &Self::Input) -> Result<Self::Output, Self::Error>;
fn parameters(&self) -> T;
fn reset(&mut self);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NeuromorphicCapability {
SpikePlasticity,
HomeostaticRegulation,
CompetitiveDynamics,
MemristiveComputing,
EventDrivenProcessing,
TemporalCoding,
Neuromodulation,
MemoryConsolidation,
OnlineLearning,
ForgettingProtection,
}
#[derive(Debug, Clone)]
pub struct NeuromorphicConfig {
pub capabilities: Vec<NeuromorphicCapability>,
pub num_neurons: usize,
pub input_dims: usize,
pub learning_rate: f64,
pub spike_threshold: f64,
pub time_step: f64,
pub max_time: f64,
pub debug_mode: bool,
}
impl Default for NeuromorphicConfig {
fn default() -> Self {
Self {
capabilities: vec![
NeuromorphicCapability::SpikePlasticity,
NeuromorphicCapability::EventDrivenProcessing,
],
num_neurons: 10,
input_dims: 2,
learning_rate: 0.01,
spike_threshold: 1.0,
time_step: 0.1,
max_time: 100.0,
debug_mode: false,
}
}
}
impl NeuromorphicConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_neurons(mut self, num_neurons: usize) -> Self {
self.num_neurons = num_neurons;
self
}
pub fn with_input_dims(mut self, input_dims: usize) -> Self {
self.input_dims = input_dims;
self
}
pub fn with_learning_rate(mut self, learning_rate: f64) -> Self {
self.learning_rate = learning_rate;
self
}
pub fn with_capability(mut self, capability: NeuromorphicCapability) -> Self {
if !self.capabilities.contains(&capability) {
self.capabilities.push(capability);
}
self
}
pub fn without_capability(mut self, capability: &NeuromorphicCapability) -> Self {
self.capabilities.retain(|c| c != capability);
self
}
pub fn with_debug(mut self, debug: bool) -> Self {
self.debug_mode = debug;
self
}
pub fn has_capability(&self, capability: &NeuromorphicCapability) -> bool {
self.capabilities.contains(capability)
}
}
pub struct NeuromorphicFactory;
impl NeuromorphicFactory {
pub fn create_spiking_clusterer(config: &NeuromorphicConfig) -> SpikingNeuralClusterer {
let mut clusterer = SpikingNeuralClusterer::new(config.num_neurons)
.with_spike_threshold(config.spike_threshold)
.with_time_step(config.time_step);
if config.has_capability(&NeuromorphicCapability::SpikePlasticity) {
clusterer = clusterer.with_stdp_learning(true);
}
if config.has_capability(&NeuromorphicCapability::CompetitiveDynamics) {
clusterer = clusterer.with_lateral_inhibition(true);
}
clusterer
}
pub fn create_competitive_clusterer(config: &NeuromorphicConfig) -> CompetitiveNeuralClusterer {
CompetitiveNeuralClusterer::new(config.num_neurons, config.input_dims)
}
pub fn create_homeostatic_clusterer(config: &NeuromorphicConfig) -> HomeostaticNeuralClusterer {
let mut clusterer = HomeostaticNeuralClusterer::new(config.num_neurons, config.input_dims);
if config.has_capability(&NeuromorphicCapability::HomeostaticRegulation) {
clusterer = clusterer.with_homeostatic_params(0.1, 1000.0);
}
clusterer
}
pub fn create_memristive_system(
config: &NeuromorphicConfig,
device_type: MemristiveDeviceType,
) -> AdvancedMemristiveLearning {
let mut system =
AdvancedMemristiveLearning::new(config.input_dims, config.num_neurons, device_type);
if config.has_capability(&NeuromorphicCapability::ForgettingProtection) {
system = system.with_forgetting_protection(true);
}
if config.has_capability(&NeuromorphicCapability::HomeostaticRegulation) {
let target_rates = scirs2_core::ndarray::Array1::from_elem(config.num_neurons, 0.1);
system = system.with_homeostatic_regulation(target_rates);
}
system
}
pub fn create_processor(config: &NeuromorphicConfig) -> NeuromorphicProcessor {
let mut processor = NeuromorphicProcessor::new();
if config.has_capability(&NeuromorphicCapability::MemristiveComputing) {
processor = processor.with_memristive_crossbar(true);
}
if config.has_capability(&NeuromorphicCapability::TemporalCoding) {
processor = processor.with_temporal_coding(true);
}
processor
}
}
pub mod utils {
use super::*;
use crate::error::SpatialResult;
use scirs2_core::ndarray::ArrayView2;
pub fn spatial_to_spikes(
data: &ArrayView2<f64>,
time_window: f64,
max_rate: f64,
) -> SpatialResult<Vec<SpikeEvent>> {
let (n_points, n_dims) = data.dim();
let mut events = Vec::new();
for (point_idx, point) in data.outer_iter().enumerate() {
for (dim, &value) in point.iter().enumerate() {
let normalized = (value + 10.0) / 20.0; let spike_rate = normalized.clamp(0.0, 1.0) * max_rate;
let num_spikes = (spike_rate * time_window) as usize;
for spike_idx in 0..num_spikes {
let timestamp = (spike_idx as f64) * (time_window / num_spikes as f64);
let event =
SpikeEvent::new(point_idx * n_dims + dim, timestamp, 1.0, point.to_vec());
events.push(event);
}
}
}
events.sort_by(|a, b| {
a.timestamp()
.partial_cmp(&b.timestamp())
.expect("Operation failed")
});
Ok(events)
}
pub fn analyze_spike_patterns(events: &[SpikeEvent]) -> SpikePatternAnalysis {
if events.is_empty() {
return SpikePatternAnalysis::default();
}
let total_events = events.len();
let time_span = events.last().expect("Operation failed").timestamp()
- events.first().expect("Operation failed").timestamp();
let avg_rate = if time_span > 0.0 {
total_events as f64 / time_span
} else {
0.0
};
let mut intervals = Vec::new();
for i in 1..events.len() {
intervals.push(events[i].timestamp() - events[i - 1].timestamp());
}
let avg_interval = if !intervals.is_empty() {
intervals.iter().sum::<f64>() / intervals.len() as f64
} else {
0.0
};
let interval_var = if intervals.len() > 1 {
let mean = avg_interval;
let variance =
intervals.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / intervals.len() as f64;
variance.sqrt() / mean.max(1e-10)
} else {
0.0
};
SpikePatternAnalysis {
total_spikes: total_events,
time_span,
average_rate: avg_rate,
average_interval: avg_interval,
regularity: 1.0 / (1.0 + interval_var), unique_neurons: events
.iter()
.map(|e| e.neuron_id())
.collect::<std::collections::HashSet<_>>()
.len(),
}
}
#[derive(Debug, Clone)]
pub struct SpikePatternAnalysis {
pub total_spikes: usize,
pub time_span: f64,
pub average_rate: f64,
pub average_interval: f64,
pub regularity: f64,
pub unique_neurons: usize,
}
impl Default for SpikePatternAnalysis {
fn default() -> Self {
Self {
total_spikes: 0,
time_span: 0.0,
average_rate: 0.0,
average_interval: 0.0,
regularity: 0.0,
unique_neurons: 0,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::Array2;
#[test]
fn test_neuromorphic_config() {
let config = NeuromorphicConfig::new()
.with_neurons(5)
.with_input_dims(3)
.with_capability(NeuromorphicCapability::HomeostaticRegulation)
.without_capability(&NeuromorphicCapability::SpikePlasticity);
assert_eq!(config.num_neurons, 5);
assert_eq!(config.input_dims, 3);
assert!(config.has_capability(&NeuromorphicCapability::HomeostaticRegulation));
assert!(!config.has_capability(&NeuromorphicCapability::SpikePlasticity));
}
#[test]
fn test_neuromorphic_factory() {
let config = NeuromorphicConfig::new()
.with_neurons(3)
.with_input_dims(2)
.with_capability(NeuromorphicCapability::CompetitiveDynamics);
let spiking_clusterer = NeuromorphicFactory::create_spiking_clusterer(&config);
assert_eq!(spiking_clusterer.num_clusters(), 3);
assert!(spiking_clusterer.is_lateral_inhibition_enabled());
let competitive_clusterer = NeuromorphicFactory::create_competitive_clusterer(&config);
assert_eq!(competitive_clusterer.num_clusters(), 3);
let processor = NeuromorphicFactory::create_processor(&config);
assert!(!processor.is_memristive_enabled()); }
#[test]
fn test_utils_spatial_to_spikes() {
let data =
Array2::from_shape_vec((2, 2), vec![0.0, 1.0, -1.0, 0.5]).expect("Operation failed");
let events = utils::spatial_to_spikes(&data.view(), 1.0, 10.0).expect("Operation failed");
assert!(!events.is_empty());
for i in 1..events.len() {
assert!(events[i - 1].timestamp() <= events[i].timestamp());
}
}
#[test]
fn test_utils_spike_pattern_analysis() {
let events = vec![
SpikeEvent::new(0, 0.0, 1.0, vec![0.0, 0.0]),
SpikeEvent::new(1, 1.0, 1.0, vec![1.0, 0.0]),
SpikeEvent::new(0, 2.0, 1.0, vec![0.0, 1.0]),
SpikeEvent::new(2, 3.0, 1.0, vec![1.0, 1.0]),
];
let analysis = utils::analyze_spike_patterns(&events);
assert_eq!(analysis.total_spikes, 4);
assert_eq!(analysis.time_span, 3.0);
assert!(analysis.average_rate > 0.0);
assert_eq!(analysis.unique_neurons, 3);
}
#[test]
fn test_empty_spike_analysis() {
let events = Vec::new();
let analysis = utils::analyze_spike_patterns(&events);
assert_eq!(analysis.total_spikes, 0);
assert_eq!(analysis.time_span, 0.0);
assert_eq!(analysis.average_rate, 0.0);
}
}