neural_dynamics/lib.rs
1//! # Neural Dynamics Library
2//!
3//! A comprehensive library for large-scale neural network simulations with advanced
4//! connectivity patterns, dynamics analysis, and mean-field approximations.
5//!
6//! ## Overview
7//!
8//! This library provides tools for simulating networks of biophysically realistic neurons
9//! with complex connectivity patterns and analyzing their collective dynamics. It integrates:
10//!
11//! - **Hodgkin-Huxley neurons**: Detailed biophysical neuron models
12//! - **Synaptic models**: Realistic synaptic transmission and plasticity
13//! - **Network topologies**: Small-world, scale-free, spatial networks
14//! - **Population dynamics**: Mean-field approximations (Wilson-Cowan)
15//! - **Analysis tools**: Synchrony, criticality, avalanche detection
16//!
17//! ## Quick Start
18//!
19//! ### Create a simple excitatory-inhibitory network
20//!
21//! ```rust
22//! use neural_dynamics::{Network, NetworkBuilder, ConnectionPattern, SynapseType};
23//! use neural_dynamics::stimulation::CurrentInjection;
24//!
25//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
26//! // Build an E-I network
27//! let mut network = NetworkBuilder::new(0.1)?
28//! .add_excitatory_population("E", 80)?
29//! .add_inhibitory_population("I", 20)?
30//! .connect(0, 0, ConnectionPattern::FixedProbability(0.1),
31//! SynapseType::Excitatory, 0.5, 1.0)?
32//! .connect(0, 1, ConnectionPattern::FixedProbability(0.2),
33//! SynapseType::Excitatory, 0.8, 1.0)?
34//! .connect(1, 0, ConnectionPattern::FixedProbability(0.3),
35//! SynapseType::Inhibitory, 1.5, 0.5)?
36//! .with_spike_recording()
37//! .build();
38//!
39//! // Add external drive to excitatory population
40//! let stim = CurrentInjection::new(5.0, 0.0, 100.0);
41//! network.add_stimulation(0, Box::new(stim))?;
42//!
43//! // Run simulation
44//! network.run(100.0)?;
45//!
46//! // Analyze results
47//! let stats = network.statistics();
48//! println!("Total spikes: {}", stats.total_spikes);
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! ### Analyze network synchrony
54//!
55//! ```rust
56//! use neural_dynamics::analysis::kuramoto_order_parameter;
57//! use std::f64::consts::PI;
58//!
59//! let phases = vec![0.0, 0.1, 0.05, 0.0]; // Nearly synchronized
60//! let order_param = kuramoto_order_parameter(&phases);
61//! println!("Synchrony: {:.2}", order_param); // Close to 1.0
62//! ```
63//!
64//! ### Detect network avalanches
65//!
66//! ```rust
67//! use neural_dynamics::analysis::detect_avalanches;
68//!
69//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
70//! let spike_trains = vec![
71//! vec![1.0, 2.0, 10.0],
72//! vec![1.5, 2.5, 10.5],
73//! vec![2.0, 11.0],
74//! ];
75//!
76//! let avalanches = detect_avalanches(&spike_trains, 1.0, 1)?;
77//! println!("Detected {} avalanches", avalanches.len());
78//! # Ok(())
79//! # }
80//! ```
81//!
82//! ### Wilson-Cowan mean-field model
83//!
84//! ```rust
85//! use neural_dynamics::mean_field::WilsonCowanModel;
86//!
87//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
88//! let mut model = WilsonCowanModel::balanced_network()?;
89//! model.set_input(0.5, 0.0);
90//!
91//! // Simulate population dynamics
92//! let trace = model.simulate(100.0, 0.1)?;
93//!
94//! // Analyze fixed points
95//! let fixed_points = model.find_fixed_points(20);
96//! println!("Found {} fixed points", fixed_points.len());
97//! # Ok(())
98//! # }
99//! ```
100//!
101//! ## Architecture
102//!
103//! ### Populations
104//!
105//! A `NeuralPopulation` groups neurons with similar properties:
106//! - Homogeneous: All neurons identical
107//! - Heterogeneous: Parameter variability across neurons
108//! - Efficient parallel updates using rayon
109//!
110//! ### Projections
111//!
112//! A `Projection` connects two populations with:
113//! - Flexible connectivity patterns (all-to-all, small-world, scale-free, etc.)
114//! - Synaptic transmission delays
115//! - Weight distributions (constant, uniform, normal)
116//! - Event-driven spike propagation
117//!
118//! ### Network
119//!
120//! The `Network` orchestrates:
121//! - Multiple populations and projections
122//! - External stimulation protocols
123//! - Recording (spikes, voltages, rates)
124//! - Efficient simulation with delay queues
125//!
126//! ## Connectivity Patterns
127//!
128//! - **AllToAll**: Dense connectivity
129//! - **OneToOne**: Identity mapping
130//! - **FixedProbability(p)**: Erdős-Rényi random graph
131//! - **FixedNumber(n)**: Fixed in-degree
132//! - **SmallWorld{k, p}**: Watts-Strogatz model
133//! - **ScaleFree{m}**: Barabási-Albert model
134//! - **Gaussian{σ}**: Distance-dependent connectivity
135//! - **Custom**: User-defined connectivity matrix
136//!
137//! ## Analysis Tools
138//!
139//! ### Synchrony Measures
140//! - **Kuramoto order parameter**: R ∈ [0,1], 1 = perfect synchrony
141//! - **Cross-correlation**: Temporal relationships between spike trains
142//! - **Phase locking**: Relative spike timing analysis
143//!
144//! ### Criticality
145//! - **Avalanche detection**: Contiguous activity bursts
146//! - **Branching parameter**: σ = ⟨n_{t+1}⟩/⟨n_t⟩, σ=1 is critical
147//! - **Power-law distributions**: Scale-free avalanche statistics
148//!
149//! ### Firing Statistics
150//! - **Population rates**: Average activity levels
151//! - **CV_ISI**: Coefficient of variation of interspike intervals
152//! - **Spike count distributions**
153//!
154//! ## Mathematical Models
155//!
156//! ### Hodgkin-Huxley Neurons
157//!
158//! ```text
159//! C_m dV/dt = -I_Na - I_K - I_K(Ca) - I_leak + I_ext + I_syn
160//! ```
161//!
162//! ### Wilson-Cowan Equations
163//!
164//! ```text
165//! τ_E dE/dt = -E + S(w_EE·E - w_EI·I + I_E)
166//! τ_I dI/dt = -I + S(w_IE·E - w_II·I + I_I)
167//! ```
168//!
169//! where S(x) = 1/(1 + exp(-gain·(x - θ))) is the sigmoid transfer function.
170//!
171//! ### Kuramoto Order Parameter
172//!
173//! ```text
174//! R = |1/N Σ_j exp(iθ_j)|
175//! ```
176//!
177//! ## Performance
178//!
179//! - **Parallel updates**: Population dynamics computed in parallel using rayon
180//! - **Sparse connectivity**: Efficient storage and computation
181//! - **Event-driven spikes**: Lazy propagation through delay queues
182//! - **Memory efficient**: Minimal allocations in simulation loops
183//!
184//! ## Features
185//!
186//! - ✅ Biophysically realistic neurons (Hodgkin-Huxley)
187//! - ✅ Complex synaptic dynamics (AMPA, NMDA, GABA)
188//! - ✅ Short-term plasticity (depression, facilitation)
189//! - ✅ Long-term plasticity (STDP)
190//! - ✅ Multiple connectivity patterns
191//! - ✅ Mean-field approximations
192//! - ✅ Comprehensive analysis tools
193//! - ✅ Parallel computation
194//! - ✅ Extensive test coverage
195//!
196//! ## Examples
197//!
198//! See the examples directory for complete simulations:
199//! - `balanced_network.rs`: E-I balance and oscillations
200//! - `small_world.rs`: Small-world connectivity and synchronization
201//! - `critical_dynamics.rs`: Self-organized criticality
202//! - `wilson_cowan.rs`: Mean-field population dynamics
203//!
204//! ## References
205//!
206//! - Hodgkin & Huxley (1952). A quantitative description of membrane current and its application to conduction and excitation in nerve.
207//! - Wilson & Cowan (1972). Excitatory and inhibitory interactions in localized populations of model neurons.
208//! - Watts & Strogatz (1998). Collective dynamics of 'small-world' networks.
209//! - Barabási & Albert (1999). Emergence of scaling in random networks.
210//! - Beggs & Plenz (2003). Neuronal avalanches in neocortical circuits.
211//! - Kuramoto (1984). Chemical Oscillations, Waves, and Turbulence.
212
213pub mod analysis;
214pub mod connectivity;
215pub mod error;
216pub mod mean_field;
217pub mod network;
218pub mod population;
219pub mod projection;
220pub mod recording;
221pub mod stimulation;
222
223// Re-export commonly used types
224pub use error::{NeuralDynamicsError, Result};
225pub use network::{Network, NetworkBuilder, NetworkStats, SynapseType};
226pub use population::{NeuralPopulation, PopulationStats};
227pub use projection::{Connection, DelayInit, Projection, WeightInit, WeightStats};
228pub use connectivity::{ConnectionPattern, NetworkStats as ConnectivityStats};
229pub use recording::{PopulationRateRecorder, SpikeRecorder, VoltageRecorder};
230pub use mean_field::{WilsonCowanModel, PopulationRateModel};
231
232// Re-export from dependencies
233pub use hodgkin_huxley;
234pub use synapse_models;
235
236/// Library version
237pub const VERSION: &str = env!("CARGO_PKG_VERSION");
238
239#[cfg(test)]
240mod integration_tests {
241 use super::*;
242 use analysis::*;
243 use connectivity::*;
244 use stimulation::*;
245 use approx::assert_relative_eq;
246
247 #[test]
248 fn test_small_network_simulation() {
249 let mut network = NetworkBuilder::new(0.1)
250 .unwrap()
251 .add_excitatory_population("E", 10)
252 .unwrap()
253 .with_spike_recording()
254 .build();
255
256 // Add stimulation
257 let stim = CurrentInjection::new(10.0, 0.0, 50.0);
258 network.add_stimulation(0, Box::new(stim)).unwrap();
259
260 // Run simulation
261 network.run(50.0).unwrap();
262
263 // Check that spikes occurred
264 let stats = network.statistics();
265 assert!(stats.total_spikes > 0);
266 }
267
268 #[test]
269 fn test_ei_network() {
270 let mut network = NetworkBuilder::new(0.1)
271 .unwrap()
272 .add_excitatory_population("E", 20)
273 .unwrap()
274 .add_inhibitory_population("I", 5)
275 .unwrap()
276 .connect(
277 0, 0,
278 ConnectionPattern::FixedProbability(0.2),
279 SynapseType::Excitatory,
280 0.5, 1.0
281 )
282 .unwrap()
283 .connect(
284 0, 1,
285 ConnectionPattern::FixedProbability(0.3),
286 SynapseType::Excitatory,
287 0.8, 1.0
288 )
289 .unwrap()
290 .connect(
291 1, 0,
292 ConnectionPattern::FixedProbability(0.4),
293 SynapseType::Inhibitory,
294 1.2, 0.5
295 )
296 .unwrap()
297 .with_spike_recording()
298 .build();
299
300 // Stimulate excitatory population
301 let stim = CurrentInjection::new(8.0, 0.0, 100.0);
302 network.add_stimulation(0, Box::new(stim)).unwrap();
303
304 // Run
305 network.run(100.0).unwrap();
306
307 // Should have activity in both populations
308 let recorder = network.spike_recorder.as_ref().unwrap();
309 assert!(recorder.total_spikes() > 0);
310 }
311
312 #[test]
313 fn test_synchrony_emergence() {
314 let mut network = NetworkBuilder::new(0.1)
315 .unwrap()
316 .add_excitatory_population("E", 30)
317 .unwrap()
318 .connect(
319 0, 0,
320 ConnectionPattern::SmallWorld { k: 6, p: 0.1 },
321 SynapseType::Excitatory,
322 1.0, 1.0
323 )
324 .unwrap()
325 .with_spike_recording()
326 .build();
327
328 // Uniform stimulation
329 let stim = CurrentInjection::new(6.0, 0.0, 200.0);
330 network.add_stimulation(0, Box::new(stim)).unwrap();
331
332 network.run(200.0).unwrap();
333
334 // Calculate synchrony (would need phase extraction from spikes)
335 let recorder = network.spike_recorder.as_ref().unwrap();
336 assert!(recorder.total_spikes() > 50);
337 }
338
339 #[test]
340 fn test_avalanche_detection_in_network() {
341 let mut network = NetworkBuilder::new(0.1)
342 .unwrap()
343 .add_excitatory_population("E", 50)
344 .unwrap()
345 .connect(
346 0, 0,
347 ConnectionPattern::FixedProbability(0.08),
348 SynapseType::Excitatory,
349 0.8, 1.0
350 )
351 .unwrap()
352 .with_spike_recording()
353 .build();
354
355 // Weak stimulation to maintain near-critical dynamics
356 let stim = CurrentInjection::new(3.0, 0.0, 500.0);
357 network.add_stimulation(0, Box::new(stim)).unwrap();
358
359 network.run(500.0).unwrap();
360
361 // Extract spike trains
362 let pop = network.get_population(0).unwrap();
363 let spike_trains: Vec<Vec<f64>> = (0..pop.size)
364 .map(|i| pop.get_spike_times(i).unwrap().to_vec())
365 .collect();
366
367 // Detect avalanches
368 let avalanches = detect_avalanches(&spike_trains, 1.0, 2).unwrap();
369 assert!(!avalanches.is_empty());
370 }
371
372 #[test]
373 fn test_wilson_cowan_integration() {
374 let mut model = WilsonCowanModel::balanced_network().unwrap();
375 model.set_input(1.0, 0.5);
376
377 let trace = model.simulate(100.0, 0.1).unwrap();
378
379 // Should reach some steady state
380 let final_e = trace.last().unwrap().1;
381 let final_i = trace.last().unwrap().2;
382
383 assert!(final_e > 0.0 && final_e < 1.0);
384 assert!(final_i > 0.0 && final_i < 1.0);
385 }
386
387 #[test]
388 fn test_connectivity_patterns() {
389 let mut rng = rand::thread_rng();
390
391 // Test each connectivity pattern
392 let patterns = vec![
393 ConnectionPattern::AllToAll,
394 ConnectionPattern::OneToOne,
395 ConnectionPattern::FixedProbability(0.3),
396 ConnectionPattern::FixedNumber(5),
397 ConnectionPattern::SmallWorld { k: 4, p: 0.2 },
398 ConnectionPattern::ScaleFree { m: 3 },
399 ConnectionPattern::Gaussian { sigma: 0.2 },
400 ];
401
402 for pattern in patterns {
403 let connections = pattern.generate(20, 20, &mut rng);
404 assert!(connections.is_ok());
405 }
406 }
407
408 #[test]
409 fn test_population_rate_calculation() {
410 let spike_trains = vec![
411 vec![10.0, 20.0, 30.0, 40.0],
412 vec![15.0, 25.0, 35.0],
413 vec![12.0, 22.0, 32.0, 42.0],
414 ];
415
416 let rate = population_firing_rate(&spike_trains, (0.0, 50.0));
417
418 // 11 spikes / (3 neurons * 50 ms) = 73.33 Hz
419 assert!(rate > 60.0 && rate < 90.0);
420 }
421
422 #[test]
423 fn test_branching_parameter() {
424 let spike_trains = vec![
425 vec![1.0, 2.0, 3.0, 4.0, 5.0],
426 vec![1.5, 2.5, 3.5, 4.5],
427 vec![2.0, 3.0, 4.0, 5.0],
428 ];
429
430 let sigma = branching_parameter(&spike_trains, 0.5).unwrap();
431 assert!(sigma > 0.0 && sigma < 3.0);
432 }
433
434 #[test]
435 fn test_kuramoto_synchrony() {
436 use std::f64::consts::PI;
437
438 // Perfect synchrony
439 let phases = vec![0.0; 10];
440 let r = kuramoto_order_parameter(&phases);
441 assert_relative_eq!(r, 1.0, epsilon = 1e-10);
442
443 // Complete desynchronization
444 let phases: Vec<f64> = (0..10).map(|i| 2.0 * PI * i as f64 / 10.0).collect();
445 let r = kuramoto_order_parameter(&phases);
446 assert!(r < 0.3);
447 }
448}