use super::DynamicalSystem;
const N: usize = 16;
pub struct CoupledMapLattice {
state: Vec<f64>,
pub r: f64, pub eps: f64, speed: f64,
}
impl CoupledMapLattice {
pub fn new(r: f64, eps: f64) -> Self {
let state: Vec<f64> = (0..N)
.map(|i| {
let t = i as f64 / N as f64;
0.2 + 0.6 * (t * std::f64::consts::PI * 3.0).sin().abs()
})
.collect();
Self {
state,
r,
eps,
speed: 0.0,
}
}
fn logistic(x: f64, r: f64) -> f64 {
r * x * (1.0 - x)
}
}
impl DynamicalSystem for CoupledMapLattice {
fn state(&self) -> &[f64] {
&self.state
}
fn dimension(&self) -> usize {
N
}
fn name(&self) -> &str {
"Coupled Map Lattice"
}
fn speed(&self) -> f64 {
self.speed
}
fn deriv_at(&self, _state: &[f64]) -> Vec<f64> {
vec![0.0; N]
}
fn set_state(&mut self, s: &[f64]) {
let n = self.state.len().min(s.len());
for i in 0..n {
if s[i].is_finite() {
self.state[i] = s[i].clamp(0.001, 0.999);
}
}
}
fn step(&mut self, _dt: f64) {
let r = self.r;
let eps = self.eps;
let prev = self.state.clone();
for i in 0..N {
let left = (i + N - 1) % N;
let right = (i + 1) % N;
let center = Self::logistic(prev[i], r);
let fl = Self::logistic(prev[left], r);
let fr = Self::logistic(prev[right], r);
let val = (1.0 - eps) * center + (eps / 2.0) * (fl + fr);
self.state[i] = val.clamp(0.0001, 0.9999);
}
self.speed = self
.state
.iter()
.zip(prev.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f64, f64::max);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::systems::DynamicalSystem;
#[test]
fn test_cml_initial_state() {
let sys = CoupledMapLattice::new(3.7, 0.1);
let s = sys.state();
assert_eq!(s.len(), 16);
assert_eq!(sys.dimension(), 16);
assert_eq!(sys.name(), "Coupled Map Lattice");
assert!(s.iter().all(|v| v.is_finite()));
assert!(s.iter().all(|&v| v > 0.0 && v < 1.0), "Initial values should be in (0,1)");
}
#[test]
fn test_cml_step_changes_state() {
let mut sys = CoupledMapLattice::new(3.7, 0.1);
let before: Vec<f64> = sys.state().to_vec();
sys.step(0.01);
assert!(before.iter().zip(sys.state().iter()).any(|(a, b)| (a - b).abs() > 1e-15));
}
#[test]
fn test_cml_values_stay_in_unit_interval() {
let mut sys = CoupledMapLattice::new(3.9, 0.2);
for _ in 0..1000 {
sys.step(0.01);
for &v in sys.state().iter() {
assert!(v > 0.0 && v < 1.0, "Value out of (0,1): {}", v);
}
}
}
#[test]
fn test_cml_deterministic() {
let mut s1 = CoupledMapLattice::new(3.7, 0.1);
let mut s2 = CoupledMapLattice::new(3.7, 0.1);
for _ in 0..200 {
s1.step(0.01);
s2.step(0.01);
}
for (a, b) in s1.state().iter().zip(s2.state().iter()) {
assert!((a - b).abs() < 1e-12, "Non-deterministic: {} vs {}", a, b);
}
}
#[test]
fn test_cml_set_state_clamped() {
let mut sys = CoupledMapLattice::new(3.7, 0.1);
let new_s: Vec<f64> = (0..16).map(|i| i as f64 * 0.06 + 0.01).collect();
sys.set_state(&new_s);
for &v in sys.state().iter() {
assert!(v > 0.0 && v < 1.0, "set_state value out of (0,1): {}", v);
}
}
#[test]
fn test_cml_speed_positive_after_step() {
let mut sys = CoupledMapLattice::new(3.7, 0.1);
sys.step(0.01);
assert!(sys.speed() > 0.0, "speed should be positive: {}", sys.speed());
}
#[test]
fn test_cml_different_r_different_dynamics() {
let mut sys_low = CoupledMapLattice::new(2.0, 0.1);
let mut sys_high = CoupledMapLattice::new(3.9, 0.1);
for _ in 0..100 {
sys_low.step(0.01);
sys_high.step(0.01);
}
let d: f64 = sys_low.state().iter().zip(sys_high.state().iter())
.map(|(a, b)| (a - b).abs())
.sum();
assert!(d > 0.01, "Different r should give different CML dynamics: d={}", d);
}
}