use super::*;
use crate::{
diffusion::DiffusionChernoff,
diffusion4::Diffusion4thChernoff,
grid::Grid1D,
grid_fn::GridFn1D,
truncated_exp4::TruncatedExp4thDiffusionChernoff,
};
fn make_diffusion(n: usize) -> DiffusionChernoff<f64> {
let grid = Grid1D::new(0.0_f64, 1.0, n).unwrap();
DiffusionChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid)
}
fn make_d4(n: usize) -> Diffusion4thChernoff<f64> {
let grid = Grid1D::new(0.0_f64, 1.0, n).unwrap();
Diffusion4thChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid)
}
fn gridfn(n: usize, f: impl Fn(f64) -> f64) -> GridFn1D<f64> {
let grid = Grid1D::new(0.0_f64, 1.0, n).unwrap();
GridFn1D::from_fn(grid, f)
}
#[test]
fn diffusion_in_subspace_true_for_5pts() {
let dc = make_diffusion(16);
let f = gridfn(16, f64::sin);
assert!(
ApproximationSubspace::<2, f64>::in_subspace(&dc, &f),
"expected true for 16-pt grid"
);
}
#[test]
fn diffusion_in_subspace_false_for_4pts() {
let dc = make_diffusion(4);
let f = gridfn(4, |x| x);
assert!(
!ApproximationSubspace::<2, f64>::in_subspace(&dc, &f),
"expected false for 4-pt grid"
);
}
#[test]
fn diffusion_in_subspace_false_for_nan_value() {
let dc = make_diffusion(16);
let mut f = gridfn(16, |x| x);
f.values[3] = f64::NAN;
assert!(
!ApproximationSubspace::<2, f64>::in_subspace(&dc, &f),
"expected false for NaN value"
);
}
#[test]
fn diffusion_jet_k2_returns_3_slots() {
let dc = make_diffusion(16);
let f = gridfn(16, f64::sin);
let zero = gridfn(16, |_| 0.0);
let mut out = [zero.clone(), zero.clone(), zero];
ApproximationSubspace::<2, f64>::jet(&dc, &f, &mut out).unwrap();
for (v, e) in out[0].values.iter().zip(f.values.iter()) {
assert!((v - e).abs() < 1e-14, "out[0] != f");
}
assert!(out[1].values.iter().all(|v| v.is_finite()), "out[1] non-finite");
}
#[test]
fn diffusion_jet_k2_wrong_out_len_errors() {
let dc = make_diffusion(16);
let f = gridfn(16, |x| x);
let zero = gridfn(16, |_| 0.0);
let mut out = [zero.clone(), zero];
let result = ApproximationSubspace::<2, f64>::jet(&dc, &f, &mut out);
assert!(result.is_err(), "expected DomainViolation for wrong out.len()");
}
#[test]
fn d4_in_subspace_k2_true_for_9pts() {
let dc = make_d4(16);
let f = gridfn(16, f64::sin);
assert!(ApproximationSubspace::<2, f64>::in_subspace(&dc, &f));
}
#[test]
fn d4_in_subspace_k4_true_for_9pts() {
let dc = make_d4(16);
let f = gridfn(16, f64::sin);
assert!(ApproximationSubspace::<4, f64>::in_subspace(&dc, &f));
}
#[test]
fn d4_jet_k4_writes_5_slots() {
let dc = make_d4(16);
let f = gridfn(16, f64::sin);
let zero = gridfn(16, |_| 0.0);
let mut out = vec![zero; 5];
ApproximationSubspace::<4, f64>::jet(&dc, &f, &mut out).unwrap();
assert!(out[0].values.iter().all(|v| v.is_finite()));
assert!(out[4].values.iter().all(|v| v.is_finite()));
}
#[test]
fn assert_in_subspace_ok_for_valid_datum() {
let dc = make_diffusion(16);
let f = gridfn(16, f64::sin);
assert_in_subspace::<_, f64, 2>(&dc, &f).unwrap();
}
#[test]
fn assert_in_subspace_err_for_invalid_datum() {
let dc = make_diffusion(4); let f = gridfn(4, |x| x);
assert!(assert_in_subspace::<_, f64, 2>(&dc, &f).is_err());
}
#[test]
fn ladder_rung_k2_has_no_predecessor() {
use crate::LadderRung;
assert_eq!(
<Diffusion4thChernoff<f64> as LadderRung<2, f64>>::PREDECESSOR_K,
None
);
}
#[test]
fn ladder_rung_k4_predecessor_is_2() {
use crate::{diffusion4_zeta4::Diffusion4thZeta4Chernoff, LadderRung};
assert_eq!(
<Diffusion4thZeta4Chernoff<f64> as LadderRung<4, f64>>::PREDECESSOR_K,
Some(2)
);
}
#[test]
fn kolmogorov_jet_returns_unsupported() {
use crate::{
grid2d::Grid2D,
grid_fn2d::GridFn2D,
hormander::{KolmogorovHypoelliptic, KolmogorovPhaseSpace},
};
let x0 = alloc::boxed::Box::new(KolmogorovPhaseSpace::<f64>::x0_drift());
let x1 = alloc::boxed::Box::new(KolmogorovPhaseSpace::<f64>::x1_diffusion());
let kernel = KolmogorovHypoelliptic::<f64>::new(x0, [x1]).unwrap();
let gx = Grid1D::new(0.0_f64, 1.0, 6).unwrap();
let gy = Grid1D::new(0.0_f64, 1.0, 6).unwrap();
let grid = Grid2D::new(gx, gy);
let f = GridFn2D::from_fn(grid, |x, _y| x);
let zero = GridFn2D::from_fn(grid, |_, _| 0.0);
let mut out = [zero.clone(), zero.clone(), zero];
let result = ApproximationSubspace::<2, f64>::jet(&kernel, &f, &mut out);
assert!(result.is_err(), "expected Unsupported from Kolmogorov jet");
}
#[test]
fn d4_k2_jet_writes_3_slots() {
let kernel = make_d4(16);
let grid = Grid1D::new(0.0_f64, 1.0, 16).unwrap();
let f = GridFn1D::from_fn(grid, |x| x * (1.0 - x));
let zero = GridFn1D::from_fn(grid, |_| 0.0_f64);
let mut out = [zero.clone(), zero.clone(), zero];
ApproximationSubspace::<2, f64>::jet(&kernel, &f, &mut out).unwrap();
assert_eq!(out[0].values, f.values, "out[0] must equal f");
for v in &out[1].values {
assert!(v.is_finite(), "out[1] non-finite: {v}");
}
}
#[test]
fn d4_k2_jet_wrong_out_len_errors() {
let kernel = make_d4(16);
let grid = Grid1D::new(0.0_f64, 1.0, 16).unwrap();
let f = GridFn1D::from_fn(grid, |x| x);
let zero = GridFn1D::from_fn(grid, |_| 0.0_f64);
let mut out = [zero.clone(), zero];
let result = ApproximationSubspace::<2, f64>::jet(&kernel, &f, &mut out);
assert!(result.is_err(), "expected Err for out.len() != 3");
}
#[test]
fn te4_k6_in_subspace_true_for_16pts() {
let grid = Grid1D::new(0.0_f64, 1.0, 16).unwrap();
let kernel = TruncatedExp4thDiffusionChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid);
let f = GridFn1D::from_fn(grid, f64::sin);
assert!(
ApproximationSubspace::<6, f64>::in_subspace(&kernel, &f),
"in_subspace should be true for 16 pts"
);
}
#[test]
fn te4_k6_in_subspace_false_for_10pts() {
let grid = Grid1D::new(0.0_f64, 1.0, 10).unwrap();
let kernel = TruncatedExp4thDiffusionChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid);
let f = GridFn1D::from_fn(grid, f64::sin);
assert!(
!ApproximationSubspace::<6, f64>::in_subspace(&kernel, &f),
"in_subspace should be false for 10 pts (need >= 13)"
);
}
#[test]
fn te4_k6_jet_writes_7_slots() {
let grid = Grid1D::new(0.0_f64, 1.0, 16).unwrap();
let kernel = TruncatedExp4thDiffusionChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid);
let f = GridFn1D::from_fn(grid, f64::sin);
let zero = GridFn1D::from_fn(grid, |_| 0.0_f64);
let mut out: alloc::vec::Vec<_> = (0..7).map(|_| zero.clone()).collect();
ApproximationSubspace::<6, f64>::jet(&kernel, &f, &mut out).unwrap();
assert_eq!(out[0].values, f.values, "out[0] must equal f");
for (i, slot) in out.iter().enumerate() {
for v in &slot.values {
assert!(v.is_finite(), "out[{i}] has non-finite value: {v}");
}
}
}
#[test]
fn te4_k6_jet_wrong_out_len_errors() {
let grid = Grid1D::new(0.0_f64, 1.0, 16).unwrap();
let kernel = TruncatedExp4thDiffusionChernoff::new(|_| 1.0_f64, |_| 0.0, |_| 0.0, 1.0, grid);
let f = GridFn1D::from_fn(grid, |x| x);
let zero = GridFn1D::from_fn(grid, |_| 0.0_f64);
let mut out = [zero.clone(), zero.clone(), zero];
let result = ApproximationSubspace::<6, f64>::jet(&kernel, &f, &mut out);
assert!(result.is_err(), "expected Err for out.len() != 7");
}
#[test]
fn ladder_rung_k6_predecessor_is_4() {
use crate::diffusion6_zeta6::Diffusion6thZeta6Chernoff;
assert_eq!(
<Diffusion6thZeta6Chernoff<f64> as LadderRung<6, f64>>::PREDECESSOR_K,
Some(4)
);
}
#[test]
fn ladder_rung_k8_predecessor_is_6() {
use crate::diffusion8_zeta8::Diffusion8thZeta8Chernoff;
assert_eq!(
<Diffusion8thZeta8Chernoff<f64> as LadderRung<8, f64>>::PREDECESSOR_K,
Some(6)
);
}