ruvector_nervous_system/routing/
mod.rs

1//! Neural routing mechanisms for the nervous system
2//!
3//! This module implements three complementary routing strategies inspired by
4//! computational neuroscience:
5//!
6//! 1. **Predictive Coding** (`predictive`) - Bandwidth reduction through residual transmission
7//! 2. **Communication Through Coherence** (`coherence`) - Phase-locked oscillatory routing
8//! 3. **Global Workspace** (`workspace`) - Limited-capacity broadcast with competition
9//!
10//! # Architecture
11//!
12//! ```text
13//! ┌─────────────────────────────────────────────────────────┐
14//! │              CoherenceGatedSystem                       │
15//! ├─────────────────────────────────────────────────────────┤
16//! │                                                         │
17//! │  ┌──────────────────┐      ┌──────────────────┐        │
18//! │  │  Predictive      │      │  Oscillatory     │        │
19//! │  │  Layers          │─────▶│  Router          │        │
20//! │  │                  │      │  (Kuramoto)      │        │
21//! │  └──────────────────┘      └──────────────────┘        │
22//! │         │                          │                    │
23//! │         │                          ▼                    │
24//! │         │                  ┌──────────────────┐        │
25//! │         └─────────────────▶│  Global          │        │
26//! │                            │  Workspace       │        │
27//! │                            └──────────────────┘        │
28//! └─────────────────────────────────────────────────────────┘
29//! ```
30//!
31//! # Performance Characteristics
32//!
33//! - **Predictive coding**: 90-99% bandwidth reduction on stable signals
34//! - **Oscillator step**: <1μs per module (tested up to 100 modules)
35//! - **Communication gain**: <100ns per pair computation
36//! - **Workspace capacity**: 4-7 items (Miller's Law)
37//!
38//! # Examples
39//!
40//! ## Basic Coherence Routing
41//!
42//! ```rust
43//! use ruvector_nervous_system::routing::{OscillatoryRouter, Representation, GlobalWorkspace};
44//!
45//! // Create 40Hz gamma-band router
46//! let mut router = OscillatoryRouter::new(5, 40.0);
47//!
48//! // Advance oscillator dynamics
49//! for _ in 0..1000 {
50//!     router.step(0.001); // 1ms time steps
51//! }
52//!
53//! // Route message based on phase coherence
54//! let message = vec![1.0, 2.0, 3.0];
55//! let receivers = vec![1, 2, 3];
56//! let routed = router.route(&message, 0, &receivers);
57//! ```
58//!
59//! ## Predictive Bandwidth Reduction
60//!
61//! ```rust
62//! use ruvector_nervous_system::routing::PredictiveLayer;
63//!
64//! let mut layer = PredictiveLayer::new(128, 0.2);
65//!
66//! // Only transmits when prediction error exceeds 20%
67//! let signal = vec![0.5; 128];
68//! if let Some(residual) = layer.residual_gated_write(&signal) {
69//!     // Transmit residual (surprise)
70//!     println!("Transmitting residual");
71//! } else {
72//!     // No transmission needed (predictable)
73//!     println!("Signal predicted - no transmission");
74//! }
75//! ```
76//!
77//! ## Global Workspace Broadcast
78//!
79//! ```rust
80//! use ruvector_nervous_system::routing::{GlobalWorkspace, Representation};
81//!
82//! let mut workspace = GlobalWorkspace::new(7); // 7-item capacity
83//!
84//! // Compete for broadcast access
85//! let rep1 = Representation::new(vec![1.0], 0.8, 0u16, 0);
86//! let rep2 = Representation::new(vec![2.0], 0.3, 1u16, 0);
87//!
88//! workspace.broadcast(rep1); // High salience - accepted
89//! workspace.broadcast(rep2); // Low salience - may be rejected
90//!
91//! // Run competitive dynamics
92//! workspace.compete();
93//!
94//! // Retrieve winning representations
95//! let winners = workspace.retrieve_top_k(3);
96//! ```
97
98pub mod circadian;
99pub mod coherence;
100pub mod predictive;
101pub mod workspace;
102
103pub use circadian::{
104    BudgetGuardrail, CircadianController, CircadianPhase, CircadianScheduler, HysteresisTracker,
105    NervousSystemMetrics, NervousSystemScorecard, PhaseModulation, ScorecardTargets,
106};
107pub use coherence::OscillatoryRouter;
108pub use predictive::PredictiveLayer;
109pub use workspace::{GlobalWorkspace, Representation};
110
111/// Integrated coherence-gated system combining all routing mechanisms
112#[derive(Debug, Clone)]
113pub struct CoherenceGatedSystem {
114    /// Oscillatory router for phase-based communication
115    router: OscillatoryRouter,
116    /// Global workspace for broadcast
117    workspace: GlobalWorkspace,
118    /// Predictive layers for each module
119    predictive: Vec<PredictiveLayer>,
120}
121
122impl CoherenceGatedSystem {
123    /// Create a new coherence-gated system
124    ///
125    /// # Arguments
126    /// * `num_modules` - Number of communicating modules
127    /// * `vector_dim` - Dimension of vectors being transmitted
128    /// * `gamma_frequency` - Base oscillation frequency (Hz, typically 30-90)
129    /// * `workspace_capacity` - Global workspace capacity (typically 4-7)
130    pub fn new(
131        num_modules: usize,
132        vector_dim: usize,
133        gamma_frequency: f32,
134        workspace_capacity: usize,
135    ) -> Self {
136        Self {
137            router: OscillatoryRouter::new(num_modules, gamma_frequency),
138            workspace: GlobalWorkspace::new(workspace_capacity),
139            predictive: (0..num_modules)
140                .map(|_| PredictiveLayer::new(vector_dim, 0.2))
141                .collect(),
142        }
143    }
144
145    /// Step oscillator dynamics forward in time
146    pub fn step_oscillators(&mut self, dt: f32) {
147        self.router.step(dt);
148    }
149
150    /// Route message with coherence gating and predictive filtering
151    ///
152    /// # Process
153    /// 1. Compute predictive residual
154    /// 2. If residual significant, apply coherence-based routing
155    /// 3. Broadcast to workspace if salience high enough
156    ///
157    /// # Returns
158    /// Vector of (receiver_id, weighted_residual) for successful routes
159    pub fn route_with_coherence(
160        &mut self,
161        message: &[f32],
162        sender: usize,
163        receivers: &[usize],
164        dt: f32,
165    ) -> Vec<(usize, Vec<f32>)> {
166        // Step 1: Advance oscillator dynamics
167        self.step_oscillators(dt);
168
169        // Step 2: Predictive filtering
170        if sender >= self.predictive.len() {
171            return Vec::new();
172        }
173
174        let residual = match self.predictive[sender].residual_gated_write(message) {
175            Some(res) => res,
176            None => return Vec::new(), // Predictable - no transmission
177        };
178
179        // Step 3: Coherence-based routing
180        let routed = self.router.route(&residual, sender, receivers);
181
182        // Step 4: Attempt global workspace broadcast for high-coherence routes
183        for (receiver, weighted_msg) in &routed {
184            let gain = self.router.communication_gain(sender, *receiver);
185
186            if gain > 0.7 {
187                // High coherence - try to broadcast to workspace
188                let salience = gain;
189                let rep = Representation::new(
190                    weighted_msg.clone(),
191                    salience,
192                    sender as u16,
193                    0, // Timestamp managed by workspace
194                );
195                self.workspace.broadcast(rep);
196            }
197        }
198
199        routed
200    }
201
202    /// Get current oscillator phases
203    pub fn phases(&self) -> &[f32] {
204        self.router.phases()
205    }
206
207    /// Get workspace contents
208    pub fn workspace_contents(&self) -> Vec<Representation> {
209        self.workspace.retrieve()
210    }
211
212    /// Run workspace competition
213    pub fn compete_workspace(&mut self) {
214        self.workspace.compete();
215    }
216
217    /// Get synchronization level (order parameter)
218    pub fn synchronization(&self) -> f32 {
219        self.router.order_parameter()
220    }
221
222    /// Get workspace occupancy (0.0 to 1.0)
223    pub fn workspace_occupancy(&self) -> f32 {
224        self.workspace.len() as f32 / self.workspace.capacity() as f32
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn test_integrated_system() {
234        let mut system = CoherenceGatedSystem::new(
235            5,    // 5 modules
236            128,  // 128-dim vectors
237            40.0, // 40Hz gamma
238            7,    // 7-item workspace
239        );
240
241        assert_eq!(system.phases().len(), 5);
242        assert_eq!(system.workspace_contents().len(), 0);
243    }
244
245    #[test]
246    fn test_route_with_coherence() {
247        let mut system = CoherenceGatedSystem::new(3, 16, 40.0, 5);
248
249        // Synchronize oscillators first
250        for _ in 0..1000 {
251            system.step_oscillators(0.001);
252        }
253
254        let message = vec![1.0; 16];
255        let receivers = vec![1, 2];
256
257        // Should transmit first time (no prediction yet)
258        let routed = system.route_with_coherence(&message, 0, &receivers, 0.001);
259        assert!(!routed.is_empty());
260    }
261
262    #[test]
263    fn test_predictive_suppression() {
264        let mut system = CoherenceGatedSystem::new(2, 16, 40.0, 5);
265
266        let stable_message = vec![1.0; 16];
267        let receivers = vec![1];
268
269        // First transmission should go through
270        let first = system.route_with_coherence(&stable_message, 0, &receivers, 0.001);
271        assert!(!first.is_empty());
272
273        // After learning, stable message should be suppressed
274        for _ in 0..50 {
275            system.route_with_coherence(&stable_message, 0, &receivers, 0.001);
276        }
277
278        // Should eventually suppress (prediction learned)
279        let mut suppressed_count = 0;
280        for _ in 0..20 {
281            let result = system.route_with_coherence(&stable_message, 0, &receivers, 0.001);
282            if result.is_empty() {
283                suppressed_count += 1;
284            }
285        }
286
287        assert!(suppressed_count > 10, "Should suppress predictable signals");
288    }
289
290    #[test]
291    fn test_workspace_integration() {
292        let mut system = CoherenceGatedSystem::new(3, 8, 40.0, 3);
293
294        // Synchronize for high coherence
295        for _ in 0..2000 {
296            system.step_oscillators(0.001);
297        }
298
299        let message = vec![1.0; 8];
300        let receivers = vec![1, 2];
301
302        // Route with high coherence
303        system.route_with_coherence(&message, 0, &receivers, 0.001);
304
305        // Workspace should receive broadcast
306        let workspace_items = system.workspace_contents();
307        assert!(!workspace_items.is_empty(), "Workspace should have items");
308    }
309
310    #[test]
311    fn test_synchronization_metric() {
312        let mut system = CoherenceGatedSystem::new(10, 16, 40.0, 7);
313
314        let initial_sync = system.synchronization();
315
316        // Run dynamics with oscillators
317        for _ in 0..5000 {
318            system.step_oscillators(0.001);
319        }
320
321        let final_sync = system.synchronization();
322
323        // Synchronization should be a valid metric in [0, 1] range
324        assert!(
325            final_sync >= 0.0 && final_sync <= 1.0,
326            "Synchronization should be in valid range: {}",
327            final_sync
328        );
329        // Verify the metric works correctly
330        assert!(
331            initial_sync >= 0.0 && initial_sync <= 1.0,
332            "Initial sync should be valid: {}",
333            initial_sync
334        );
335    }
336
337    #[test]
338    fn test_workspace_occupancy() {
339        let mut system = CoherenceGatedSystem::new(3, 8, 40.0, 4);
340
341        assert_eq!(system.workspace_occupancy(), 0.0);
342
343        // Fill workspace manually
344        for i in 0..3 {
345            let rep = Representation::new(vec![1.0; 8], 0.8, i as u16, 0);
346            system.workspace.broadcast(rep);
347        }
348
349        assert_eq!(system.workspace_occupancy(), 0.75); // 3/4
350    }
351
352    #[test]
353    fn test_workspace_competition() {
354        let mut system = CoherenceGatedSystem::new(2, 8, 40.0, 3);
355
356        // Add weak representation
357        let rep = Representation::new(vec![1.0; 8], 0.3, 0_u16, 0);
358        system.workspace.broadcast(rep);
359
360        system.compete_workspace();
361
362        // Salience should decay
363        let contents = system.workspace_contents();
364        if !contents.is_empty() {
365            assert!(contents[0].salience < 0.3, "Salience should decay");
366        }
367    }
368
369    #[test]
370    fn test_end_to_end_routing() {
371        let mut system = CoherenceGatedSystem::new(4, 32, 40.0, 5);
372
373        // Synchronize oscillators
374        for _ in 0..1000 {
375            system.step_oscillators(0.0001);
376        }
377
378        // Send varying signal
379        let mut routed_count = 0;
380        for i in 0..100 {
381            let signal_strength = (i as f32 * 0.1).sin();
382            let message: Vec<f32> = (0..32).map(|_| signal_strength).collect();
383            let receivers = vec![1, 2, 3];
384
385            let routed = system.route_with_coherence(&message, 0, &receivers, 0.0001);
386
387            // Count successful routes
388            if !routed.is_empty() {
389                routed_count += 1;
390            }
391        }
392
393        // Should have some successful routes (predictive coding may suppress some)
394        assert!(
395            routed_count > 0,
396            "Should have at least some successful routes, got {}",
397            routed_count
398        );
399
400        // Workspace should have accumulated some representations
401        system.compete_workspace();
402
403        // Expect valid workspace state
404        assert!(system.workspace_occupancy() <= 1.0);
405    }
406
407    #[test]
408    fn test_performance_integrated() {
409        let mut system = CoherenceGatedSystem::new(50, 128, 40.0, 7);
410
411        let message = vec![1.0; 128];
412        let receivers: Vec<usize> = (1..50).collect();
413
414        let start = std::time::Instant::now();
415
416        for _ in 0..100 {
417            system.route_with_coherence(&message, 0, &receivers, 0.001);
418        }
419
420        let elapsed = start.elapsed();
421        let avg_route = elapsed.as_micros() / 100;
422
423        println!("Average route time: {}μs (50 modules, 128-dim)", avg_route);
424
425        // Should be reasonably fast (<1ms per route)
426        assert!(avg_route < 1000, "Routing should be fast");
427    }
428}