| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
ddeint is a small toolkit for numerically solving delay differential equations (DDEs), i.e. ODEs whose right-hand side depends on the state at past times t - delay. It wraps integrators from the ode_solvers crate, plus a piecewise-linear history interpolator (DdeVar/DdeVars) that is updated after every accepted integration step so that delayed lookups always reflect the actual solved trajectory rather than only the initial history.
Three integrators are available, all with the same shape (new/step/ integrate, a results: Vec<(f64, DVector<f64>)> field) so switching between them is close to a drop-in change:
| Type | Underlying method | Extra constructor args |
|---|---|---|
| DdeRK4 | fixed-step RK4 | -- |
| DdeDopri5 | adaptive Dormand-Prince 5(4) | rtol, atol |
| DdeDop853 | adaptive Dormand-Prince 8(5,3) | rtol, atol |
For all three, step_size controls how often the delayed history is updated and a result is recorded. For DdeRK4 that's also the literal integration step; for the two adaptive solvers, the actual number of internal steps between two recorded points is chosen by their own error control, and the state at exactly t + step_size is obtained from their dense-output interpolant rather than by counting steps. See examples/integrator_comparison.rs for all three solving the same DDE side by side (and a note on why the higher-order integrator isn't automatically the more accurate one here).
use ddeint::Interpolator;
let x = vec![0.0, 1.0, 2.0];
let y = vec![0.0, 1.0, 4.0];
let interpolator = Interpolator::new(x, y, /* fill_value = */ 0.0);
assert_eq!(interpolator.interpolate(0.5), Some(0.5));A System that needs delayed state has to implement DdeSystem in addition to ode_solvers's System trait; DdeSystem::record is called by DdeRK4 after every step and is where the system should push the newly solved state into its own history storage (typically an Rc<RefCell<DdeVars>> shared with the closure/struct that reads it back in system()):
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use nalgebra::DVector;
use ddeint::{DdeRK4, DdeSystem, DdeVars};
#[derive(Clone)]
struct DelayedPredatorPrey {
delay: f64,
history: Rc<RefCell<DdeVars>>,
}
impl ode_solvers::System<f64, DVector<f64>> for DelayedPredatorPrey {
fn system(&self, t: f64, y: &DVector<f64>, dy: &mut DVector<f64>) {
let history = self.history.borrow();
let prey_delayed = history[0].instance_value(t - self.delay);
let predator_delayed = history[1].instance_value(t - self.delay);
dy[0] = 0.5 * y[0] * (1.0 - predator_delayed);
dy[1] = -0.5 * y[1] * (1.0 - prey_delayed);
}
}
impl DdeSystem for DelayedPredatorPrey {
fn record(&self, t: f64, y: &DVector<f64>) {
let mut history = self.history.borrow_mut();
history[0].update(t, y[0]);
history[1].update(t, y[1]);
}
}See src/lotka_delay.rs for the full, runnable version of this example (ddeint::solve_lotka_volterra), also wired into main.rs.
Runnable examples live under examples/. Each one writes its result to a CSV file (in the directory cargo is run from) in addition to printing a short summary:
cargo run --release --example three_var_dde # 3-variable, 2-delay system (Willé & Baker's classic example) cargo run --release --example sine_delay # single delay, checked against its exact closed-form solution cargo run --release --example variable_delay # a delay that is itself a function of time, not a constant cargo run --release --example integrator_comparison # DdeRK4 vs DdeDopri5 vs DdeDop853 on the same DDE
The exact-solution and convergence checks these examples use for verification are also captured as cargo test-run regression tests in tests/analytic_verification.rs.
This project was inspired by Zulko's Python ddeint, a lightweight DDE solver built on top of scipy.integrate.ode. Many thanks to Zulko for the original design and the history-interpolation (ddeVar) idea this crate is built around.
One thing this crate does differently: in the original ddeint, the whole state is a single vector-valued history function, so every state variable is looked up with the same delay. This port's DdeVars instead keeps a separate history per state variable, so different variables can use different delay values within the same system -- see examples/three_var_dde.rs, where y1 is delayed by 1.0 and y2 by 0.2 in the same system.
| Back | FazBrowse Home | New Git URL |