Skip to main content

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}