#![allow(clippy::cast_precision_loss)]
use crate::{
chernoff::ChernoffFunction, error::SemiflowError, float::SemiflowFloat, grid_fn::GridFn1D,
grid_fn2d::GridFn2D,
};
pub trait ApproximationSubspace<const K: usize, F: SemiflowFloat = f64>:
ChernoffFunction<F>
{
fn in_subspace(&self, f: &Self::S) -> bool;
fn jet(&self, f: &Self::S, out: &mut [Self::S]) -> Result<(), SemiflowError>;
}
pub fn assert_in_subspace<C, F, const K: usize>(chernoff: &C, f: &C::S) -> Result<(), SemiflowError>
where
C: ApproximationSubspace<K, F>,
F: SemiflowFloat,
{
if chernoff.in_subspace(f) {
Ok(())
} else {
Err(SemiflowError::DomainViolation {
what: "f is not in D(A^K) (ApproximationSubspace witness failed)",
value: K as f64,
})
}
}
mod sealed {
pub trait Sealed {}
impl<F: crate::float::SemiflowFloat> Sealed for crate::diffusion4::Diffusion4thChernoff<F> {}
impl<F: crate::float::SemiflowFloat> Sealed
for crate::diffusion4_zeta4::Diffusion4thZeta4Chernoff<F>
{
}
impl<F: crate::float::SemiflowFloat> Sealed
for crate::diffusion6_zeta6::Diffusion6thZeta6Chernoff<F>
{
}
impl<F: crate::float::SemiflowFloat> Sealed
for crate::diffusion8_zeta8::Diffusion8thZeta8Chernoff<F>
{
}
}
pub trait LadderRung<const K: usize, F: SemiflowFloat = f64>:
ApproximationSubspace<K, F> + sealed::Sealed
{
const PREDECESSOR_K: Option<usize>;
}
impl LadderRung<2, f64> for crate::diffusion4::Diffusion4thChernoff<f64> {
const PREDECESSOR_K: Option<usize> = None;
}
impl LadderRung<4, f64> for crate::diffusion4_zeta4::Diffusion4thZeta4Chernoff<f64> {
const PREDECESSOR_K: Option<usize> = Some(2);
}
impl LadderRung<6, f64> for crate::diffusion6_zeta6::Diffusion6thZeta6Chernoff<f64> {
const PREDECESSOR_K: Option<usize> = Some(4);
}
impl LadderRung<8, f64> for crate::diffusion8_zeta8::Diffusion8thZeta8Chernoff<f64> {
const PREDECESSOR_K: Option<usize> = Some(6);
}
impl ApproximationSubspace<2, f64> for crate::diffusion::DiffusionChernoff<f64> {
fn in_subspace(&self, f: &GridFn1D<f64>) -> bool {
f.values.len() >= 5 && f.values.iter().all(|v| v.is_finite())
}
fn jet(&self, f: &GridFn1D<f64>, out: &mut [GridFn1D<f64>]) -> Result<(), SemiflowError> {
if out.len() != 3 {
return Err(SemiflowError::DomainViolation {
what: "jet K=2 requires out.len() == 3",
value: out.len() as f64,
});
}
out[0].values.clone_from(&f.values);
apply_divergence_form_op(self, f, &mut out[1])?;
let af = out[1].clone();
apply_divergence_form_op(self, &af, &mut out[2])?;
Ok(())
}
}
impl ApproximationSubspace<2, f64> for crate::diffusion4::Diffusion4thChernoff<f64> {
fn in_subspace(&self, f: &GridFn1D<f64>) -> bool {
f.values.len() >= 9 && f.values.iter().all(|v| v.is_finite())
}
fn jet(&self, f: &GridFn1D<f64>, out: &mut [GridFn1D<f64>]) -> Result<(), SemiflowError> {
if out.len() != 3 {
return Err(SemiflowError::DomainViolation {
what: "jet K=2 (Diffusion4thChernoff) requires out.len() == 3",
value: out.len() as f64,
});
}
out[0].values.clone_from(&f.values);
apply_divergence_form_op_d4(self, f, &mut out[1])?;
let af = out[1].clone();
apply_divergence_form_op_d4(self, &af, &mut out[2])?;
Ok(())
}
}
impl ApproximationSubspace<4, f64> for crate::diffusion4::Diffusion4thChernoff<f64> {
fn in_subspace(&self, f: &GridFn1D<f64>) -> bool {
f.values.len() >= 9 && f.values.iter().all(|v| v.is_finite())
}
fn jet(&self, f: &GridFn1D<f64>, out: &mut [GridFn1D<f64>]) -> Result<(), SemiflowError> {
if out.len() != 5 {
return Err(SemiflowError::DomainViolation {
what: "jet K=4 requires out.len() == 5",
value: out.len() as f64,
});
}
out[0].values.clone_from(&f.values);
apply_divergence_form_op_d4(self, f, &mut out[1])?;
for k in 1..4usize {
let prev = out[k].clone();
apply_divergence_form_op_d4(self, &prev, &mut out[k + 1])?;
}
Ok(())
}
}
impl ApproximationSubspace<6, f64>
for crate::truncated_exp4::TruncatedExp4thDiffusionChernoff<f64>
{
fn in_subspace(&self, f: &GridFn1D<f64>) -> bool {
f.values.len() >= 13 && f.values.iter().all(|v| v.is_finite())
}
fn jet(&self, f: &GridFn1D<f64>, out: &mut [GridFn1D<f64>]) -> Result<(), SemiflowError> {
if out.len() != 7 {
return Err(SemiflowError::DomainViolation {
what: "jet K=6 requires out.len() == 7",
value: out.len() as f64,
});
}
out[0].values.clone_from(&f.values);
apply_divergence_form_op_te4(self, f, &mut out[1])?;
for k in 1..6usize {
let prev = out[k].clone();
apply_divergence_form_op_te4(self, &prev, &mut out[k + 1])?;
}
Ok(())
}
}
impl ApproximationSubspace<2, f64> for crate::hormander::KolmogorovHypoelliptic<f64> {
fn in_subspace(&self, f: &GridFn2D<f64>) -> bool {
let nx = f.grid.nx();
let ny = f.grid.ny();
nx >= 4 && ny >= 4 && f.values.iter().all(|v| v.is_finite())
}
fn jet(&self, _f: &GridFn2D<f64>, _out: &mut [GridFn2D<f64>]) -> Result<(), SemiflowError> {
Err(SemiflowError::Unsupported {
feature: "hypoelliptic-jet-K2",
})
}
}
fn apply_div_form_fn<F: SemiflowFloat>(
a_fn: &impl Fn(F) -> F,
grid: crate::grid::Grid1D<F>,
f: &GridFn1D<F>,
out: &mut GridFn1D<F>,
) -> Result<(), SemiflowError> {
let n = f.values.len();
if n < 3 {
return Err(SemiflowError::DomainViolation {
what: "divergence-form jet op requires >= 3 grid points",
value: n as f64,
});
}
let dx = grid.dx();
let dx2 = dx * dx;
let half = crate::float::half::<F>();
out.values.resize(n, F::zero());
for i in 0..n {
let x_i = grid.x_at(i);
let a_pos = a_fn(x_i + dx * half);
let a_neg = a_fn(x_i - dx * half);
let f_pos = if i + 1 < n {
f.values[i + 1]
} else {
f.values[n - 1]
};
let f_neg = if i > 0 { f.values[i - 1] } else { f.values[0] };
let f_i = f.values[i];
out.values[i] = (a_pos * (f_pos - f_i) - a_neg * (f_i - f_neg)) / dx2;
}
Ok(())
}
fn apply_divergence_form_op<F: SemiflowFloat>(
dc: &crate::diffusion::DiffusionChernoff<F>,
f: &GridFn1D<F>,
out: &mut GridFn1D<F>,
) -> Result<(), SemiflowError> {
let grid = dc.grid;
apply_div_form_fn(&|x| dc.call_a(x), grid, f, out)
}
fn apply_divergence_form_op_d4<F: SemiflowFloat>(
dc: &crate::diffusion4::Diffusion4thChernoff<F>,
f: &GridFn1D<F>,
out: &mut GridFn1D<F>,
) -> Result<(), SemiflowError> {
let grid = dc.grid;
let a_fn = dc.a;
apply_div_form_fn(&|x| a_fn(x), grid, f, out)
}
fn apply_divergence_form_op_te4<F: SemiflowFloat>(
dc: &crate::truncated_exp4::TruncatedExp4thDiffusionChernoff<F>,
f: &GridFn1D<F>,
out: &mut GridFn1D<F>,
) -> Result<(), SemiflowError> {
let grid = dc.grid;
let a_fn = dc.a;
apply_div_form_fn(&|x| a_fn(x), grid, f, out)
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
include!("approximation_tests_mod.rs");
}