use crate::approximation::ApproximationSubspace;
use crate::chernoff::ApplyChernoffExt;
use crate::grid::Grid1D;
use crate::state::State;
use crate::ChernoffFunction;
use num_complex::Complex;
type C64 = Complex<f64>;
fn c(re: f64, im: f64) -> C64 {
Complex::new(re, im)
}
fn make_grid(n: usize) -> Grid1D<f64> {
Grid1D::new(0.0_f64, 1.0, n).unwrap()
}
fn identity_a(_x: f64, a: &mut [[C64; 2]; 2]) {
a[0][0] = c(1.0, 0.0);
a[0][1] = c(0.0, 0.0);
a[1][0] = c(0.0, 0.0);
a[1][1] = c(1.0, 0.0);
}
fn zero_b(_x: f64, b: &mut [[C64; 2]; 2]) {
for row in b.iter_mut() {
for v in row.iter_mut() {
*v = c(0.0, 0.0);
}
}
}
#[test]
fn new_rejects_n_less_than_5() {
let grid = make_grid(4);
let result =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid);
assert!(result.is_err(), "expected Err for n=4");
}
#[test]
fn new_ok_for_n_ge_5() {
let grid = make_grid(8);
let result =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid);
assert!(result.is_ok(), "expected Ok for n=8");
}
#[test]
fn state_new_is_zero() {
let grid = make_grid(8);
let state = MatrixGridFnComplex1D::<C64, 2>::new(grid);
assert_eq!(state.values.len(), 8 * 2, "wrong len");
for v in &state.values {
assert_eq!(*v, c(0.0, 0.0), "initial value non-zero");
}
}
#[test]
fn state_from_fn_populates_values() {
let grid = make_grid(8);
let state =
MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| [c(x, 0.0), c(0.0, x)]);
assert_eq!(state.values.len(), 8 * 2);
let x0 = grid.x_at(0);
assert!(
(state.values[0].re - x0).abs() < 1e-14,
"component 0 wrong: {}",
state.values[0]
);
}
#[test]
fn point_view_set_point_roundtrip() {
let grid = make_grid(8);
let mut state = MatrixGridFnComplex1D::<C64, 2>::new(grid);
let val = [c(1.5, 2.3), c(-0.7, 0.1)];
state.set_point(3, &val);
let got = state.point_view(3);
assert!((got[0] - val[0]).norm() < 1e-15, "component 0 wrong");
assert!((got[1] - val[1]).norm() < 1e-15, "component 1 wrong");
}
#[test]
fn norm_sup_zero_state_is_zero() {
let grid = make_grid(8);
let state = MatrixGridFnComplex1D::<C64, 2>::new(grid);
assert!(state.norm_sup() < 1e-15, "norm_sup of zero state non-zero");
}
#[test]
fn norm_sup_nonzero_state_is_positive() {
let grid = make_grid(8);
let state = MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| {
[c(x, 0.0), c(0.0, 1.0)]
});
assert!(state.norm_sup() > 0.0, "norm_sup should be positive");
}
#[test]
fn order_is_2() {
let grid = make_grid(8);
let kernel =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid)
.unwrap();
assert_eq!(kernel.order(), 2);
}
#[test]
fn apply_negative_tau_errors() {
let grid = make_grid(8);
let kernel =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid)
.unwrap();
let src = MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| [c(x, 0.0), c(0.0, 0.0)]);
let mut dst = MatrixGridFnComplex1D::<C64, 2>::new(grid);
let mut scratch = crate::scratch::ScratchPool::new();
assert!(kernel.apply_into(-0.01, &src, &mut dst, &mut scratch).is_err());
}
#[test]
fn apply_finite_output_for_small_tau() {
let grid = make_grid(8);
let kernel =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid)
.unwrap();
let src = MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| {
[c((x * core::f64::consts::PI).sin(), 0.0), c(0.0, (x * core::f64::consts::PI).cos())]
});
let dst = kernel.apply_chernoff(0.01, &src).unwrap();
for v in &dst.values {
assert!(v.is_finite(), "non-finite output: {v}");
}
}
#[test]
fn in_subspace_true_for_valid_state() {
let grid = make_grid(8);
let kernel =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid)
.unwrap();
let f = MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| [c(x, 0.0), c(0.0, 0.0)]);
assert!(ApproximationSubspace::<2, f64>::in_subspace(&kernel, &f));
}
#[test]
fn in_subspace_false_for_nan_value() {
let grid = make_grid(8);
let kernel =
MatrixDiffusionChernoffComplex::<C64, 2>::new(identity_a, zero_b, zero_b, grid)
.unwrap();
let mut f = MatrixGridFnComplex1D::<C64, 2>::from_fn(grid, |x| [c(x, 0.0), c(0.0, 0.0)]);
f.values[2] = c(f64::NAN, 0.0);
assert!(!ApproximationSubspace::<2, f64>::in_subspace(&kernel, &f));
}