strange_loop/
strange_attractor.rs

1//! Temporal strange attractors with nanosecond precision
2//!
3//! This module implements various types of strange attractors that can be used
4//! as the basis for temporal consciousness and self-referential loops.
5
6use crate::error::{LoopError, Result};
7use crate::types::Vector3D;
8use serde::{Deserialize, Serialize};
9
10/// Types of strange attractors available
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12#[allow(missing_docs)]
13pub enum AttractorType {
14    /// Lorenz attractor with chaotic dynamics
15    Lorenz { sigma: f64, rho: f64, beta: f64 },
16    /// Rössler attractor with simpler chaos
17    Rossler { a: f64, b: f64, c: f64 },
18    /// Chua's circuit attractor
19    Chua { alpha: f64, beta: f64, gamma: f64 },
20    /// Custom attractor with user-defined parameters
21    Custom {
22        name: String,
23        parameters: Vec<f64>,
24        equations: String, // Mathematical description
25    },
26}
27
28impl Default for AttractorType {
29    fn default() -> Self {
30        Self::Lorenz {
31            sigma: 10.0,
32            rho: 28.0,
33            beta: 8.0 / 3.0,
34        }
35    }
36}
37
38impl AttractorType {
39    /// Get the parameter count for this attractor type
40    pub fn parameter_count(&self) -> usize {
41        match self {
42            Self::Lorenz { .. } => 3,
43            Self::Rossler { .. } => 3,
44            Self::Chua { .. } => 3,
45            Self::Custom { parameters, .. } => parameters.len(),
46        }
47    }
48
49    /// Get parameter names
50    pub fn parameter_names(&self) -> Vec<&'static str> {
51        match self {
52            Self::Lorenz { .. } => vec!["sigma", "rho", "beta"],
53            Self::Rossler { .. } => vec!["a", "b", "c"],
54            Self::Chua { .. } => vec!["alpha", "beta", "gamma"],
55            Self::Custom { .. } => vec!["param"], // Generic name for custom
56        }
57    }
58
59    /// Validate parameters for stability
60    pub fn validate(&self) -> Result<()> {
61        match self {
62            Self::Lorenz { sigma, rho, beta } => {
63                if *sigma <= 0.0 || *rho <= 0.0 || *beta <= 0.0 {
64                    return Err(LoopError::math_error("Lorenz parameters must be positive"));
65                }
66                if *rho > 100.0 {
67                    return Err(LoopError::math_error("Lorenz rho parameter too large (unstable)"));
68                }
69            }
70            Self::Rossler { a, b, c } => {
71                if *a < 0.0 || *b < 0.0 || *c < 0.0 {
72                    return Err(LoopError::math_error("Rössler parameters must be non-negative"));
73                }
74            }
75            Self::Chua { alpha, beta, gamma } => {
76                if *alpha <= 0.0 || *beta <= 0.0 || *gamma <= 0.0 {
77                    return Err(LoopError::math_error("Chua parameters must be positive"));
78                }
79            }
80            Self::Custom { parameters, .. } => {
81                if parameters.is_empty() {
82                    return Err(LoopError::math_error("Custom attractor must have parameters"));
83                }
84                if parameters.iter().any(|&p| !p.is_finite()) {
85                    return Err(LoopError::math_error("Custom parameters must be finite"));
86                }
87            }
88        }
89        Ok(())
90    }
91
92    /// Get the Lyapunov exponent (measure of chaos)
93    pub fn lyapunov_exponent(&self) -> f64 {
94        match self {
95            Self::Lorenz { sigma, rho, beta } => {
96                // Approximate largest Lyapunov exponent for Lorenz system
97                let r = *rho;
98                if r > 1.0 {
99                    0.9056 // Typical value for classical parameters
100                } else {
101                    -(sigma + beta + 1.0) // Stable case
102                }
103            }
104            Self::Rossler { a, b, c: _ } => {
105                // Approximate for Rössler system
106                if *a > 0.0 && *b > 0.0 {
107                    0.0714 // Typical positive value
108                } else {
109                    -1.0 // Stable case
110                }
111            }
112            Self::Chua { alpha, .. } => {
113                // Approximate for Chua's circuit
114                if *alpha > 7.0 {
115                    0.3 // Chaotic regime
116                } else {
117                    -0.5 // Stable regime
118                }
119            }
120            Self::Custom { .. } => 0.0, // Unknown for custom attractors
121        }
122    }
123}
124
125/// Configuration for temporal attractor
126#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct AttractorConfig {
128    /// Type of attractor
129    pub attractor_type: AttractorType,
130    /// Integration time step (nanoseconds)
131    pub dt_ns: u64,
132    /// Number of integration steps per frame
133    pub steps_per_frame: usize,
134    /// Enable adaptive time stepping
135    pub adaptive_stepping: bool,
136    /// Tolerance for adaptive stepping
137    pub tolerance: f64,
138    /// Maximum allowed deviation from attractor
139    pub max_deviation: f64,
140}
141
142impl Default for AttractorConfig {
143    fn default() -> Self {
144        Self {
145            attractor_type: AttractorType::default(),
146            dt_ns: 1000, // 1 microsecond steps
147            steps_per_frame: 100,
148            adaptive_stepping: true,
149            tolerance: 1e-6,
150            max_deviation: 10.0,
151        }
152    }
153}
154
155impl AttractorConfig {
156    /// Create configuration optimized for consciousness experiments
157    pub fn consciousness_mode() -> Self {
158        Self {
159            attractor_type: AttractorType::Lorenz {
160                sigma: 10.0,
161                rho: 28.0,
162                beta: 8.0 / 3.0,
163            },
164            dt_ns: 100, // 100 nanosecond precision
165            steps_per_frame: 1000,
166            adaptive_stepping: true,
167            tolerance: 1e-9,
168            max_deviation: 5.0,
169        }
170    }
171
172    /// Create configuration for high-speed computation
173    pub fn high_speed_mode() -> Self {
174        Self {
175            attractor_type: AttractorType::Rossler {
176                a: 0.2,
177                b: 0.2,
178                c: 5.7,
179            },
180            dt_ns: 10_000, // 10 microseconds (faster but less precise)
181            steps_per_frame: 10,
182            adaptive_stepping: false,
183            tolerance: 1e-3,
184            max_deviation: 20.0,
185        }
186    }
187
188    /// Validate configuration
189    pub fn validate(&self) -> Result<()> {
190        self.attractor_type.validate()?;
191
192        if self.dt_ns == 0 {
193            return Err(LoopError::math_error("Time step must be positive"));
194        }
195        if self.steps_per_frame == 0 {
196            return Err(LoopError::math_error("Steps per frame must be positive"));
197        }
198        if self.tolerance <= 0.0 {
199            return Err(LoopError::math_error("Tolerance must be positive"));
200        }
201        if self.max_deviation <= 0.0 {
202            return Err(LoopError::math_error("Max deviation must be positive"));
203        }
204
205        Ok(())
206    }
207}
208
209/// Temporal strange attractor implementation
210pub struct TemporalAttractor {
211    config: AttractorConfig,
212    state: Vector3D,
213    time_ns: u128,
214    trajectory: Vec<Vector3D>,
215    max_trajectory_length: usize,
216}
217
218impl TemporalAttractor {
219    /// Create a new temporal attractor
220    pub fn new(config: AttractorConfig) -> Result<Self> {
221        config.validate()?;
222
223        Ok(Self {
224            config,
225            state: Vector3D::new(1.0, 1.0, 1.0), // Initial condition
226            time_ns: 0,
227            trajectory: Vec::new(),
228            max_trajectory_length: 10_000,
229        })
230    }
231
232    /// Create with custom initial state
233    pub fn with_initial_state(config: AttractorConfig, initial_state: Vector3D) -> Result<Self> {
234        config.validate()?;
235
236        Ok(Self {
237            config,
238            state: initial_state,
239            time_ns: 0,
240            trajectory: Vec::new(),
241            max_trajectory_length: 10_000,
242        })
243    }
244
245    /// Get current state
246    pub fn state(&self) -> Vector3D {
247        self.state
248    }
249
250    /// Get current time in nanoseconds
251    pub fn time_ns(&self) -> u128 {
252        self.time_ns
253    }
254
255    /// Get trajectory history
256    pub fn trajectory(&self) -> &[Vector3D] {
257        &self.trajectory
258    }
259
260    /// Set maximum trajectory length
261    pub fn set_max_trajectory_length(&mut self, length: usize) {
262        self.max_trajectory_length = length;
263        if self.trajectory.len() > length {
264            self.trajectory.drain(0..self.trajectory.len() - length);
265        }
266    }
267
268    /// Step the attractor forward in time
269    pub fn step(&mut self) -> Result<Vector3D> {
270        let dt = self.config.dt_ns as f64 / 1_000_000_000.0; // Convert to seconds
271
272        if self.config.adaptive_stepping {
273            self.adaptive_step(dt)?;
274        } else {
275            self.fixed_step(dt)?;
276        }
277
278        self.time_ns += self.config.dt_ns as u128;
279
280        // Store in trajectory
281        self.trajectory.push(self.state);
282        if self.trajectory.len() > self.max_trajectory_length {
283            self.trajectory.remove(0);
284        }
285
286        // Check for instability
287        if self.state.norm() > self.config.max_deviation {
288            return Err(LoopError::math_error("Attractor state exceeded maximum deviation"));
289        }
290
291        Ok(self.state)
292    }
293
294    /// Perform multiple steps efficiently
295    pub fn step_multiple(&mut self, steps: usize) -> Result<Vec<Vector3D>> {
296        let mut results = Vec::with_capacity(steps);
297        for _ in 0..steps {
298            results.push(self.step()?);
299        }
300        Ok(results)
301    }
302
303    /// Fixed time step integration using RK4
304    fn fixed_step(&mut self, dt: f64) -> Result<()> {
305        let k1 = self.compute_derivative(self.state);
306        let k2 = self.compute_derivative(self.state + k1 * dt * 0.5);
307        let k3 = self.compute_derivative(self.state + k2 * dt * 0.5);
308        let k4 = self.compute_derivative(self.state + k3 * dt);
309
310        self.state = self.state + (k1 + k2 * 2.0 + k3 * 2.0 + k4) * dt / 6.0;
311        Ok(())
312    }
313
314    /// Adaptive time step integration using embedded RK methods
315    fn adaptive_step(&mut self, dt: f64) -> Result<()> {
316        let mut current_dt = dt;
317        let mut attempts = 0;
318        const MAX_ATTEMPTS: usize = 10;
319
320        while attempts < MAX_ATTEMPTS {
321            // RK4 step
322            let k1 = self.compute_derivative(self.state);
323            let k2 = self.compute_derivative(self.state + k1 * current_dt * 0.5);
324            let k3 = self.compute_derivative(self.state + k2 * current_dt * 0.5);
325            let k4 = self.compute_derivative(self.state + k3 * current_dt);
326
327            let new_state_rk4 = self.state + (k1 + k2 * 2.0 + k3 * 2.0 + k4) * current_dt / 6.0;
328
329            // RK2 step for error estimation
330            let k1_rk2 = self.compute_derivative(self.state);
331            let k2_rk2 = self.compute_derivative(self.state + k1_rk2 * current_dt);
332            let new_state_rk2 = self.state + (k1_rk2 + k2_rk2) * current_dt / 2.0;
333
334            // Error estimation
335            let error = (new_state_rk4 - new_state_rk2).norm();
336
337            if error < self.config.tolerance {
338                self.state = new_state_rk4;
339                break;
340            } else {
341                // Reduce time step
342                current_dt *= 0.5;
343                attempts += 1;
344            }
345        }
346
347        if attempts == MAX_ATTEMPTS {
348            return Err(LoopError::math_error("Adaptive stepping failed to converge"));
349        }
350
351        Ok(())
352    }
353
354    /// Compute derivative for the specific attractor type
355    #[inline(always)]
356    fn compute_derivative(&self, state: Vector3D) -> Vector3D {
357        let x = state[0];
358        let y = state[1];
359        let z = state[2];
360
361        match &self.config.attractor_type {
362            AttractorType::Lorenz { sigma, rho, beta } => {
363                Vector3D::new(
364                    sigma * (y - x),
365                    x * (rho - z) - y,
366                    x * y - beta * z,
367                )
368            }
369            AttractorType::Rossler { a, b, c } => {
370                Vector3D::new(
371                    -y - z,
372                    x + a * y,
373                    b + z * (x - c),
374                )
375            }
376            AttractorType::Chua { alpha, beta, gamma } => {
377                let f = if x.abs() <= 1.0 {
378                    beta * x
379                } else {
380                    alpha * x + (beta - alpha) * x.signum()
381                };
382                Vector3D::new(
383                    alpha * (y - x - f),
384                    x - y + z,
385                    -beta * y - gamma * z,
386                )
387            }
388            AttractorType::Custom { parameters, .. } => {
389                // Simple example custom attractor (Thomas attractor)
390                let b = parameters.get(0).copied().unwrap_or(0.208186);
391                Vector3D::new(
392                    -(b * x) + (y).sin(),
393                    -(b * y) + (z).sin(),
394                    -(b * z) + (x).sin(),
395                )
396            }
397        }
398    }
399
400    /// Calculate the attractor's correlation dimension
401    pub fn correlation_dimension(&self, embedding_dim: usize) -> f64 {
402        if self.trajectory.len() < embedding_dim * 2 {
403            return 0.0;
404        }
405
406        // Simplified correlation dimension calculation
407        // In practice, this would require more sophisticated algorithms
408        let n = self.trajectory.len().min(1000); // Limit for performance
409        let mut correlation_sum = 0.0;
410        let mut count = 0;
411
412        for i in 0..n {
413            for j in (i + 1)..n {
414                let distance = (self.trajectory[i] - self.trajectory[j]).norm();
415                if distance < 1.0 { // Threshold
416                    correlation_sum += 1.0;
417                }
418                count += 1;
419            }
420        }
421
422        if count > 0 {
423            (correlation_sum / count as f64).log10() / (1.0_f64).log10()
424        } else {
425            0.0
426        }
427    }
428
429    /// Get the phase space volume
430    pub fn phase_space_volume(&self) -> f64 {
431        if self.trajectory.len() < 8 {
432            return 0.0;
433        }
434
435        // Calculate bounding box volume
436        let mut min_vals = self.trajectory[0];
437        let mut max_vals = self.trajectory[0];
438
439        for point in &self.trajectory {
440            for i in 0..3 {
441                min_vals[i] = min_vals[i].min(point[i]);
442                max_vals[i] = max_vals[i].max(point[i]);
443            }
444        }
445
446        (max_vals - min_vals).iter().product()
447    }
448
449    /// Calculate temporal correlation
450    pub fn temporal_correlation(&self, lag: usize) -> f64 {
451        if self.trajectory.len() <= lag {
452            return 0.0;
453        }
454
455        let n = self.trajectory.len() - lag;
456        let mut correlation = 0.0;
457
458        for i in 0..n {
459            let dot_product = self.trajectory[i].dot(&self.trajectory[i + lag]);
460            let norm_product = self.trajectory[i].norm() * self.trajectory[i + lag].norm();
461            if norm_product > 0.0 {
462                correlation += dot_product / norm_product;
463            }
464        }
465
466        correlation / n as f64
467    }
468
469    /// Reset attractor to initial state
470    pub fn reset(&mut self) {
471        self.state = Vector3D::new(1.0, 1.0, 1.0);
472        self.time_ns = 0;
473        self.trajectory.clear();
474    }
475
476    /// Reset with custom initial state
477    pub fn reset_with_state(&mut self, initial_state: Vector3D) {
478        self.state = initial_state;
479        self.time_ns = 0;
480        self.trajectory.clear();
481    }
482
483    /// Create a perturbation for sensitivity analysis
484    pub fn perturb(&mut self, perturbation: Vector3D) {
485        self.state = self.state + perturbation;
486    }
487
488    /// Get configuration
489    pub fn config(&self) -> &AttractorConfig {
490        &self.config
491    }
492
493    /// Update configuration
494    pub fn update_config(&mut self, config: AttractorConfig) -> Result<()> {
495        config.validate()?;
496        self.config = config;
497        Ok(())
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use super::*;
504    use approx::assert_relative_eq;
505
506    #[test]
507    fn test_attractor_type_validation() {
508        let lorenz = AttractorType::Lorenz { sigma: 10.0, rho: 28.0, beta: 8.0/3.0 };
509        assert!(lorenz.validate().is_ok());
510
511        let bad_lorenz = AttractorType::Lorenz { sigma: -1.0, rho: 28.0, beta: 8.0/3.0 };
512        assert!(bad_lorenz.validate().is_err());
513    }
514
515    #[test]
516    fn test_attractor_creation() {
517        let config = AttractorConfig::default();
518        let attractor = TemporalAttractor::new(config);
519        assert!(attractor.is_ok());
520    }
521
522    #[test]
523    fn test_attractor_step() {
524        let config = AttractorConfig::default();
525        let mut attractor = TemporalAttractor::new(config).unwrap();
526
527        let initial_state = attractor.state();
528        let new_state = attractor.step().unwrap();
529
530        assert_ne!(initial_state, new_state);
531        assert_eq!(attractor.trajectory().len(), 1);
532    }
533
534    #[test]
535    fn test_multiple_steps() {
536        let config = AttractorConfig::default();
537        let mut attractor = TemporalAttractor::new(config).unwrap();
538
539        let steps = 10;
540        let results = attractor.step_multiple(steps).unwrap();
541
542        assert_eq!(results.len(), steps);
543        assert_eq!(attractor.trajectory().len(), steps);
544    }
545
546    #[test]
547    fn test_lorenz_attractor() {
548        let config = AttractorConfig {
549            attractor_type: AttractorType::Lorenz { sigma: 10.0, rho: 28.0, beta: 8.0/3.0 },
550            dt_ns: 1000,
551            steps_per_frame: 1,
552            adaptive_stepping: false,
553            tolerance: 1e-6,
554            max_deviation: 50.0,
555        };
556
557        let mut attractor = TemporalAttractor::new(config).unwrap();
558
559        // Step and verify the trajectory changes
560        for _ in 0..100 {
561            attractor.step().unwrap();
562        }
563
564        assert_eq!(attractor.trajectory().len(), 100);
565
566        // Lorenz attractor should exhibit chaotic behavior
567        let correlation_dim = attractor.correlation_dimension(3);
568        assert!(correlation_dim >= 0.0);
569    }
570
571    #[test]
572    fn test_rossler_attractor() {
573        let config = AttractorConfig {
574            attractor_type: AttractorType::Rossler { a: 0.2, b: 0.2, c: 5.7 },
575            dt_ns: 1000,
576            steps_per_frame: 1,
577            adaptive_stepping: false,
578            tolerance: 1e-6,
579            max_deviation: 50.0,
580        };
581
582        let mut attractor = TemporalAttractor::new(config).unwrap();
583
584        for _ in 0..50 {
585            attractor.step().unwrap();
586        }
587
588        let volume = attractor.phase_space_volume();
589        assert!(volume > 0.0);
590    }
591
592    #[test]
593    fn test_temporal_correlation() {
594        let config = AttractorConfig::default();
595        let mut attractor = TemporalAttractor::new(config).unwrap();
596
597        // Generate trajectory
598        for _ in 0..100 {
599            attractor.step().unwrap();
600        }
601
602        let correlation = attractor.temporal_correlation(1);
603        assert!(correlation.is_finite());
604    }
605
606    #[test]
607    fn test_adaptive_stepping() {
608        let config = AttractorConfig {
609            attractor_type: AttractorType::default(),
610            dt_ns: 1000,
611            steps_per_frame: 1,
612            adaptive_stepping: true,
613            tolerance: 1e-9,
614            max_deviation: 10.0,
615        };
616
617        let mut attractor = TemporalAttractor::new(config).unwrap();
618
619        // Should not fail with adaptive stepping
620        for _ in 0..10 {
621            attractor.step().unwrap();
622        }
623    }
624
625    #[test]
626    fn test_perturbation() {
627        let config = AttractorConfig::default();
628        let mut attractor = TemporalAttractor::new(config).unwrap();
629
630        let initial_state = attractor.state();
631        let perturbation = Vector3D::new(0.01, 0.01, 0.01);
632        attractor.perturb(perturbation);
633
634        let perturbed_state = attractor.state();
635        assert_relative_eq!((perturbed_state - initial_state).norm(), perturbation.norm(), epsilon = 1e-10);
636    }
637
638    #[test]
639    fn test_consciousness_mode_config() {
640        let config = AttractorConfig::consciousness_mode();
641        assert_eq!(config.dt_ns, 100);
642        assert_eq!(config.steps_per_frame, 1000);
643        assert!(config.adaptive_stepping);
644    }
645
646    #[test]
647    fn test_lyapunov_exponent() {
648        let lorenz = AttractorType::Lorenz { sigma: 10.0, rho: 28.0, beta: 8.0/3.0 };
649        let lyapunov = lorenz.lyapunov_exponent();
650        assert!(lyapunov > 0.0); // Should be positive for chaotic system
651
652        let stable_lorenz = AttractorType::Lorenz { sigma: 10.0, rho: 0.5, beta: 8.0/3.0 };
653        let stable_lyapunov = stable_lorenz.lyapunov_exponent();
654        assert!(stable_lyapunov < 0.0); // Should be negative for stable system
655    }
656}