1use crate::error::{LoopError, Result};
7use crate::types::Vector3D;
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12#[allow(missing_docs)]
13pub enum AttractorType {
14 Lorenz { sigma: f64, rho: f64, beta: f64 },
16 Rossler { a: f64, b: f64, c: f64 },
18 Chua { alpha: f64, beta: f64, gamma: f64 },
20 Custom {
22 name: String,
23 parameters: Vec<f64>,
24 equations: String, },
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 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 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"], }
57 }
58
59 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 pub fn lyapunov_exponent(&self) -> f64 {
94 match self {
95 Self::Lorenz { sigma, rho, beta } => {
96 let r = *rho;
98 if r > 1.0 {
99 0.9056 } else {
101 -(sigma + beta + 1.0) }
103 }
104 Self::Rossler { a, b, c: _ } => {
105 if *a > 0.0 && *b > 0.0 {
107 0.0714 } else {
109 -1.0 }
111 }
112 Self::Chua { alpha, .. } => {
113 if *alpha > 7.0 {
115 0.3 } else {
117 -0.5 }
119 }
120 Self::Custom { .. } => 0.0, }
122 }
123}
124
125#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct AttractorConfig {
128 pub attractor_type: AttractorType,
130 pub dt_ns: u64,
132 pub steps_per_frame: usize,
134 pub adaptive_stepping: bool,
136 pub tolerance: f64,
138 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, steps_per_frame: 100,
148 adaptive_stepping: true,
149 tolerance: 1e-6,
150 max_deviation: 10.0,
151 }
152 }
153}
154
155impl AttractorConfig {
156 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, steps_per_frame: 1000,
166 adaptive_stepping: true,
167 tolerance: 1e-9,
168 max_deviation: 5.0,
169 }
170 }
171
172 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, steps_per_frame: 10,
182 adaptive_stepping: false,
183 tolerance: 1e-3,
184 max_deviation: 20.0,
185 }
186 }
187
188 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
209pub 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 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), time_ns: 0,
227 trajectory: Vec::new(),
228 max_trajectory_length: 10_000,
229 })
230 }
231
232 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 pub fn state(&self) -> Vector3D {
247 self.state
248 }
249
250 pub fn time_ns(&self) -> u128 {
252 self.time_ns
253 }
254
255 pub fn trajectory(&self) -> &[Vector3D] {
257 &self.trajectory
258 }
259
260 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 pub fn step(&mut self) -> Result<Vector3D> {
270 let dt = self.config.dt_ns as f64 / 1_000_000_000.0; 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 self.trajectory.push(self.state);
282 if self.trajectory.len() > self.max_trajectory_length {
283 self.trajectory.remove(0);
284 }
285
286 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 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 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 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 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 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 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 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 #[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 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 pub fn correlation_dimension(&self, embedding_dim: usize) -> f64 {
402 if self.trajectory.len() < embedding_dim * 2 {
403 return 0.0;
404 }
405
406 let n = self.trajectory.len().min(1000); 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 { 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 pub fn phase_space_volume(&self) -> f64 {
431 if self.trajectory.len() < 8 {
432 return 0.0;
433 }
434
435 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 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 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 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 pub fn perturb(&mut self, perturbation: Vector3D) {
485 self.state = self.state + perturbation;
486 }
487
488 pub fn config(&self) -> &AttractorConfig {
490 &self.config
491 }
492
493 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 for _ in 0..100 {
561 attractor.step().unwrap();
562 }
563
564 assert_eq!(attractor.trajectory().len(), 100);
565
566 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 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 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); 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); }
656}