use numra_core::Scalar;
pub trait HistoryFunction<S: Scalar>: Fn(S) -> Vec<S> {}
impl<S: Scalar, F: Fn(S) -> Vec<S>> HistoryFunction<S> for F {}
#[derive(Clone, Debug)]
pub struct HistoryStep<S: Scalar> {
pub t: S,
pub y: Vec<S>,
pub f: Vec<S>,
pub t_next: S,
pub y_next: Vec<S>,
pub f_next: Vec<S>,
}
impl<S: Scalar> HistoryStep<S> {
pub fn new(t: S, y: Vec<S>, f: Vec<S>, t_next: S, y_next: Vec<S>, f_next: Vec<S>) -> Self {
Self {
t,
y,
f,
t_next,
y_next,
f_next,
}
}
pub fn h(&self) -> S {
self.t_next - self.t
}
pub fn contains(&self, t: S) -> bool {
t >= self.t && t <= self.t_next
}
}
pub struct HermiteInterpolator;
impl HermiteInterpolator {
pub fn interpolate<S: Scalar>(step: &HistoryStep<S>, t: S) -> Vec<S> {
let h = step.h();
if h.abs() < S::from_f64(1e-15) {
return step.y.clone();
}
let theta = (t - step.t) / h;
let theta2 = theta * theta;
let theta3 = theta2 * theta;
let h00 = S::ONE - S::from_f64(3.0) * theta2 + S::from_f64(2.0) * theta3; let h10 = theta - S::from_f64(2.0) * theta2 + theta3; let h01 = S::from_f64(3.0) * theta2 - S::from_f64(2.0) * theta3; let h11 = -theta2 + theta3;
let dim = step.y.len();
let mut result = vec![S::ZERO; dim];
for i in 0..dim {
result[i] = h00 * step.y[i]
+ h10 * h * step.f[i]
+ h01 * step.y_next[i]
+ h11 * h * step.f_next[i];
}
result
}
pub fn interpolate_derivative<S: Scalar>(step: &HistoryStep<S>, t: S) -> Vec<S> {
let h = step.h();
if h.abs() < S::from_f64(1e-15) {
return step.f.clone();
}
let theta = (t - step.t) / h;
let theta2 = theta * theta;
let dh00 = -S::from_f64(6.0) * theta + S::from_f64(6.0) * theta2;
let dh10 = S::ONE - S::from_f64(4.0) * theta + S::from_f64(3.0) * theta2;
let dh01 = S::from_f64(6.0) * theta - S::from_f64(6.0) * theta2;
let dh11 = -S::from_f64(2.0) * theta + S::from_f64(3.0) * theta2;
let dim = step.y.len();
let mut result = vec![S::ZERO; dim];
for i in 0..dim {
result[i] = (dh00 * step.y[i]
+ dh10 * h * step.f[i]
+ dh01 * step.y_next[i]
+ dh11 * h * step.f_next[i])
/ h;
}
result
}
}
pub struct History<S: Scalar, H: Fn(S) -> Vec<S>> {
initial_history: H,
t0: S,
steps: Vec<HistoryStep<S>>,
dim: usize,
}
impl<S: Scalar, H: Fn(S) -> Vec<S>> History<S, H> {
pub fn new(initial_history: H, t0: S, dim: usize) -> Self {
Self {
initial_history,
t0,
steps: Vec::new(),
dim,
}
}
pub fn add_step(&mut self, step: HistoryStep<S>) {
self.steps.push(step);
}
pub fn evaluate(&self, t: S) -> Vec<S> {
if t <= self.t0 {
return (self.initial_history)(t);
}
match self.find_step(t) {
Some(idx) => HermiteInterpolator::interpolate(&self.steps[idx], t),
None => {
if let Some(last) = self.steps.last() {
if t <= last.t_next {
HermiteInterpolator::interpolate(last, t)
} else {
last.y_next.clone()
}
} else {
(self.initial_history)(self.t0)
}
}
}
}
pub fn evaluate_derivative(&self, t: S) -> Vec<S> {
if t <= self.t0 {
let eps = S::from_f64(1e-8);
let y1 = (self.initial_history)(t - eps);
let y2 = (self.initial_history)(t + eps);
let mut deriv = vec![S::ZERO; self.dim];
for i in 0..self.dim {
deriv[i] = (y2[i] - y1[i]) / (S::from_f64(2.0) * eps);
}
return deriv;
}
match self.find_step(t) {
Some(idx) => HermiteInterpolator::interpolate_derivative(&self.steps[idx], t),
None => {
if let Some(last) = self.steps.last() {
last.f_next.clone()
} else {
vec![S::ZERO; self.dim]
}
}
}
}
fn find_step(&self, t: S) -> Option<usize> {
if self.steps.is_empty() {
return None;
}
let mut lo = 0;
let mut hi = self.steps.len();
while lo < hi {
let mid = (lo + hi) / 2;
if t < self.steps[mid].t {
hi = mid;
} else if t > self.steps[mid].t_next {
lo = mid + 1;
} else {
return Some(mid);
}
}
if lo < self.steps.len() && self.steps[lo].contains(t) {
Some(lo)
} else if lo > 0 && self.steps[lo - 1].contains(t) {
Some(lo - 1)
} else {
None
}
}
pub fn current_time(&self) -> S {
self.steps.last().map(|s| s.t_next).unwrap_or(self.t0)
}
pub fn n_steps(&self) -> usize {
self.steps.len()
}
pub fn clear(&mut self) {
self.steps.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hermite_interpolation() {
let step = HistoryStep {
t: 0.0,
y: vec![0.0],
f: vec![0.0],
t_next: 1.0,
y_next: vec![1.0],
f_next: vec![2.0],
};
let y_mid = HermiteInterpolator::interpolate(&step, 0.5);
assert!((y_mid[0] - 0.25).abs() < 1e-10);
let y_quarter = HermiteInterpolator::interpolate(&step, 0.25);
assert!((y_quarter[0] - 0.0625).abs() < 1e-10);
}
#[test]
fn test_hermite_derivative() {
let step = HistoryStep {
t: 0.0,
y: vec![0.0],
f: vec![0.0],
t_next: 1.0,
y_next: vec![1.0],
f_next: vec![2.0],
};
let f_mid = HermiteInterpolator::interpolate_derivative(&step, 0.5);
assert!((f_mid[0] - 1.0).abs() < 1e-10);
}
#[test]
fn test_history_initial() {
let history_fn = |t: f64| vec![t.sin()];
let history = History::new(history_fn, 0.0, 1);
let y = history.evaluate(-1.0);
assert!((y[0] - (-1.0_f64).sin()).abs() < 1e-10);
}
#[test]
fn test_history_with_steps() {
let history_fn = |_t: f64| vec![1.0];
let mut history = History::new(history_fn, 0.0, 1);
history.add_step(HistoryStep {
t: 0.0,
y: vec![1.0],
f: vec![1.0],
t_next: 1.0,
y_next: vec![2.0],
f_next: vec![1.0],
});
let y = history.evaluate(0.5);
assert!((y[0] - 1.5).abs() < 0.1);
let y_pre = history.evaluate(-0.5);
assert!((y_pre[0] - 1.0).abs() < 1e-10);
let y_end = history.evaluate(1.0);
assert!((y_end[0] - 2.0).abs() < 1e-10);
}
#[test]
fn test_history_multiple_steps() {
let history_fn = |_t: f64| vec![0.0];
let mut history = History::new(history_fn, 0.0, 1);
for i in 0..5 {
let t = i as f64;
let t_next = (i + 1) as f64;
history.add_step(HistoryStep {
t,
y: vec![t],
f: vec![1.0],
t_next,
y_next: vec![t_next],
f_next: vec![1.0],
});
}
assert!((history.evaluate(2.5)[0] - 2.5).abs() < 0.1);
assert!((history.evaluate(4.0)[0] - 4.0).abs() < 1e-10);
}
}