1pub mod neuron;
36pub mod synapse;
37pub mod network;
38pub mod attractor;
39pub mod strange_loop;
40pub mod causal;
41pub mod time_crystal;
42pub mod morphogenetic;
43pub mod optimizer;
44pub mod cognitive_engine;
45
46pub use neuron::{LIFNeuron, NeuronState, NeuronConfig, SpikeTrain};
48pub use synapse::{Synapse, STDPConfig, SynapseMatrix};
49pub use network::{SpikingNetwork, NetworkConfig, LayerConfig};
50pub use attractor::{AttractorDynamics, EnergyLandscape, AttractorConfig};
51pub use strange_loop::{MetaCognitiveMinCut, MetaAction, MetaLevel, StrangeLoopConfig};
52pub use causal::{CausalDiscoverySNN, CausalGraph, CausalRelation, CausalConfig};
53pub use time_crystal::{TimeCrystalCPG, OscillatorNeuron, PhaseTopology, CPGConfig};
54pub use morphogenetic::{MorphogeneticSNN, GrowthRules, TuringPattern, MorphConfig};
55pub use optimizer::{NeuralGraphOptimizer, PolicySNN, ValueNetwork, OptimizerConfig, OptimizationResult};
56pub use cognitive_engine::{CognitiveMinCutEngine, EngineConfig, EngineMetrics, OperationMode};
57
58use crate::graph::{DynamicGraph, VertexId, EdgeId, Weight};
59use std::time::{Duration, Instant};
60
61pub type SimTime = f64;
63
64#[derive(Debug, Clone, Copy, PartialEq)]
66pub struct Spike {
67 pub neuron_id: usize,
69 pub time: SimTime,
71}
72
73pub type Vector = Vec<f64>;
75
76#[derive(Debug, Clone)]
78pub struct SNNMinCutConfig {
79 pub dt: f64,
81 pub num_neurons: usize,
83 pub enable_attractors: bool,
85 pub enable_strange_loop: bool,
87 pub enable_causal_discovery: bool,
89 pub enable_time_crystal: bool,
91 pub enable_morphogenetic: bool,
93 pub enable_optimizer: bool,
95}
96
97impl Default for SNNMinCutConfig {
98 fn default() -> Self {
99 Self {
100 dt: 1.0, num_neurons: 1000,
102 enable_attractors: true,
103 enable_strange_loop: true,
104 enable_causal_discovery: true,
105 enable_time_crystal: true,
106 enable_morphogenetic: true,
107 enable_optimizer: true,
108 }
109 }
110}
111
112#[derive(Debug, Clone)]
114pub struct SpikeComputeResult {
115 pub spikes: Vec<Spike>,
117 pub energy: f64,
119 pub duration: Duration,
121 pub mincut_value: Option<f64>,
123}
124
125pub trait SpikeToGraph {
127 fn spikes_to_weights(&self, spikes: &[Spike], graph: &mut DynamicGraph);
129
130 fn graph_to_spike_rates(&self, graph: &DynamicGraph) -> Vec<f64>;
132}
133
134pub trait GraphToSpike {
136 fn weight_to_current(&self, weight: Weight) -> f64;
138
139 fn degree_to_threshold(&self, degree: usize) -> f64;
141}
142
143#[derive(Debug, Clone, Default)]
145pub struct DefaultSpikeGraphTransducer {
146 pub weight_factor: f64,
148 pub current_factor: f64,
150 pub threshold_scale: f64,
152}
153
154impl DefaultSpikeGraphTransducer {
155 pub fn new() -> Self {
157 Self {
158 weight_factor: 0.01,
159 current_factor: 10.0,
160 threshold_scale: 0.5,
161 }
162 }
163}
164
165impl SpikeToGraph for DefaultSpikeGraphTransducer {
166 fn spikes_to_weights(&self, spikes: &[Spike], graph: &mut DynamicGraph) {
167 let mut spike_counts: std::collections::HashMap<usize, usize> =
169 std::collections::HashMap::new();
170
171 for spike in spikes {
172 *spike_counts.entry(spike.neuron_id).or_insert(0) += 1;
173 }
174
175 for edge in graph.edges() {
177 let src_spikes = spike_counts.get(&(edge.source as usize)).copied().unwrap_or(0);
178 let tgt_spikes = spike_counts.get(&(edge.target as usize)).copied().unwrap_or(0);
179
180 let correlation = (src_spikes * tgt_spikes) as f64;
182 let delta_w = self.weight_factor * correlation;
183
184 if delta_w > 0.0 {
185 let new_weight = edge.weight + delta_w;
186 let _ = graph.update_edge_weight(edge.source, edge.target, new_weight);
187 }
188 }
189 }
190
191 fn graph_to_spike_rates(&self, graph: &DynamicGraph) -> Vec<f64> {
192 let vertices = graph.vertices();
193 let mut rates = vec![0.0; vertices.len()];
194
195 for (i, v) in vertices.iter().enumerate() {
196 let degree = graph.degree(*v);
198 let weight_sum: f64 = graph.neighbors(*v)
200 .iter()
201 .filter_map(|(_, eid)| {
202 graph.edges().iter()
203 .find(|e| e.id == *eid)
204 .map(|e| e.weight)
205 })
206 .sum();
207
208 rates[i] = (degree as f64 + weight_sum) * 0.01;
209 }
210
211 rates
212 }
213}
214
215impl GraphToSpike for DefaultSpikeGraphTransducer {
216 fn weight_to_current(&self, weight: Weight) -> f64 {
217 self.current_factor * weight
218 }
219
220 fn degree_to_threshold(&self, degree: usize) -> f64 {
221 if degree == 0 {
223 return 1.0;
224 }
225 1.0 + self.threshold_scale * (degree as f64).ln()
226 }
227}
228
229const MAX_SYNCHRONY_SPIKES: usize = 10_000;
231
232pub fn compute_synchrony(spikes: &[Spike], window_ms: f64) -> f64 {
237 if spikes.len() < 2 {
238 return 0.0;
239 }
240
241 let spikes = if spikes.len() > MAX_SYNCHRONY_SPIKES {
243 &spikes[..MAX_SYNCHRONY_SPIKES]
244 } else {
245 spikes
246 };
247
248 let mut sorted: Vec<_> = spikes.to_vec();
250 sorted.sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap_or(std::cmp::Ordering::Equal));
251
252 let mut coincidences = 0usize;
254 let mut window_start = 0;
255
256 for i in 0..sorted.len() {
257 while window_start < i && sorted[i].time - sorted[window_start].time > window_ms {
259 window_start += 1;
260 }
261
262 for j in window_start..i {
264 if sorted[i].neuron_id != sorted[j].neuron_id {
265 coincidences += 1;
266 }
267 }
268 }
269
270 let n = sorted.len();
272 let mut neuron_counts: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
273 for spike in &sorted {
274 *neuron_counts.entry(spike.neuron_id).or_insert(0) += 1;
275 }
276
277 let total_inter_pairs: usize = {
279 let total = n * (n - 1) / 2;
280 let intra: usize = neuron_counts.values().map(|&c| c * (c - 1) / 2).sum();
281 total - intra
282 };
283
284 if total_inter_pairs == 0 {
285 0.0
286 } else {
287 coincidences as f64 / total_inter_pairs as f64
288 }
289}
290
291pub fn compute_energy(mincut: f64, synchrony: f64) -> f64 {
293 -mincut - synchrony
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_default_config() {
302 let config = SNNMinCutConfig::default();
303 assert_eq!(config.dt, 1.0);
304 assert!(config.enable_attractors);
305 }
306
307 #[test]
308 fn test_synchrony_computation() {
309 let spikes = vec![
310 Spike { neuron_id: 0, time: 0.0 },
311 Spike { neuron_id: 1, time: 0.5 },
312 Spike { neuron_id: 2, time: 10.0 },
313 ];
314
315 let sync_narrow = compute_synchrony(&spikes, 1.0);
316 let sync_wide = compute_synchrony(&spikes, 20.0);
317
318 assert!(sync_wide >= sync_narrow);
320 }
321
322 #[test]
323 fn test_energy_function() {
324 let energy = compute_energy(10.0, 0.5);
325 assert!(energy < 0.0);
326
327 let energy2 = compute_energy(20.0, 0.8);
329 assert!(energy2 < energy);
330 }
331
332 #[test]
333 fn test_spike_train() {
334 let spike = Spike { neuron_id: 42, time: 100.5 };
335 assert_eq!(spike.neuron_id, 42);
336 assert!((spike.time - 100.5).abs() < 1e-10);
337 }
338}