DDE

Mackey-Glass DDE

Classic chaotic delay differential equation solved by method of steps.

  • intermediate

The Mackey-Glass equation models the regulation of red blood cells and becomes chaotic at the right delay. With β = 2, γ = 1, n = 9.65, τ = 2 it produces a classic chaotic time series that’s been used as a benchmark in everything from neural networks to time-series forecasting.

y'(t) = β y(t-τ) / (1 + y(t-τ)^n) - γ y(t)

This example uses numra::dde::MethodOfSteps and prints the trajectory at fixed intervals so you can plot it.

Run it:

cargo run --release --example mackey_glass

Source

numra/examples/mackey_glass.rs on GitHub · Related book chapter

use numra::dde::{DdeOptions, DdeSolver, DdeSystem, MethodOfSteps};

/// Mackey-Glass equation
struct MackeyGlass {
    /// Production rate
    beta: f64,
    /// Decay rate
    gamma: f64,
    /// Hill coefficient (nonlinearity)
    n: f64,
    /// Time delay
    tau: f64,
}

impl DdeSystem<f64> for MackeyGlass {
    fn dim(&self) -> usize {
        1
    }

    fn delays(&self) -> Vec<f64> {
        vec![self.tau]
    }

    fn rhs(&self, _t: f64, y: &[f64], y_delayed: &[&[f64]], dydt: &mut [f64]) {
        let y_tau = y_delayed[0][0]; // y(t - tau)
                                     // dy/dt = β * y(t-τ) / (1 + y(t-τ)^n) - γ * y(t)
        dydt[0] = self.beta * y_tau / (1.0 + y_tau.powf(self.n)) - self.gamma * y[0];
    }
}

fn main() {
    println!("=== Mackey-Glass Delay Differential Equation ===\n");

    // Standard chaotic parameters
    let system = MackeyGlass {
        beta: 2.0,
        gamma: 1.0,
        n: 9.65,
        tau: 2.0,
    };

    println!("Parameters:");
    println!("  β (production rate): {}", system.beta);
    println!("  γ (decay rate): {}", system.gamma);
    println!("  n (Hill coefficient): {}", system.n);
    println!("  τ (delay): {}", system.tau);
    println!();

    // Constant history function
    let history = |_t: f64| vec![0.5];

    // Solver options
    let options = DdeOptions::default()
        .rtol(1e-6)
        .atol(1e-9)
        .max_steps(100000);

    println!("Solving from t=0 to t=500...\n");

    let result = MethodOfSteps::solve(&system, 0.0, 500.0, &history, &options)
        .expect("Failed to solve Mackey-Glass equation");

    println!("Solution computed successfully!");
    println!("  Number of time points: {}", result.len());
    println!("  Accepted steps: {}", result.stats.n_accept);
    println!("  Rejected steps: {}", result.stats.n_reject);
    println!();

    // Find min, max, and final value
    let mut y_min = f64::INFINITY;
    let mut y_max = f64::NEG_INFINITY;
    for y in result.y.iter() {
        if *y < y_min {
            y_min = *y;
        }
        if *y > y_max {
            y_max = *y;
        }
    }

    let y_final = result.y_final().unwrap()[0];

    println!("Solution statistics:");
    println!("  Minimum y: {:.6}", y_min);
    println!("  Maximum y: {:.6}", y_max);
    println!("  Range: {:.6}", y_max - y_min);
    println!("  Final y(500): {:.6}", y_final);
    println!();

    // Sample some values for the trajectory
    println!("Sample trajectory points:");
    println!("  {:>10} | {:>12}", "t", "y(t)");
    println!("  {:-<10}|{:-<13}", "", "");

    let sample_times = [0.0, 50.0, 100.0, 200.0, 300.0, 400.0, 500.0];
    let mut sample_idx = 0;

    for (i, &t) in result.t.iter().enumerate() {
        if sample_idx < sample_times.len() && t >= sample_times[sample_idx] {
            let y = result.y_at(i)[0];
            println!("  {:>10.1} | {:>12.6}", t, y);
            sample_idx += 1;
        }
    }

    println!();
    println!("The Mackey-Glass equation with τ=2 exhibits chaotic oscillations.");
    println!(
        "The solution oscillates irregularly between approximately {} and {}.",
        y_min.round() as i32,
        y_max.round() as i32
    );
}