use crate::gauss::{Rule, legendre_vandermonde};
use crate::numeric::CustomNumeric;
use mdarray::DTensor;
use std::fmt::Debug;
#[derive(Debug, Clone)]
pub struct Interpolate1D<T> {
pub x_min: T,
pub x_max: T,
pub coeffs: Vec<T>,
pub gauss_rule: Rule<T>,
}
impl<T: CustomNumeric + Debug + Clone + 'static> Interpolate1D<T> {
pub fn new(values: &[T], gauss_rule: &Rule<T>) -> Self {
assert!(
!values.is_empty(),
"Cannot create interpolation from empty values"
);
assert_eq!(
values.len(),
gauss_rule.x.len(),
"Values length must match Gauss points"
);
let coeffs = interpolate_1d_legendre(values, gauss_rule);
Interpolate1D {
x_min: gauss_rule.a,
x_max: gauss_rule.b,
coeffs,
gauss_rule: gauss_rule.clone(),
}
}
pub fn evaluate(&self, x: T) -> T {
assert!(
x >= self.x_min && x <= self.x_max,
"Point x={:?} is outside interpolation domain [{:?}, {:?}]",
x,
self.x_min,
self.x_max
);
evaluate_interpolated_polynomial(x, &self.coeffs)
}
pub fn domain(&self) -> (T, T) {
(self.x_min, self.x_max)
}
pub fn n_points(&self) -> usize {
self.coeffs.len()
}
}
pub fn legendre_collocation_matrix<T: CustomNumeric>(gauss_rule: &Rule<T>) -> DTensor<T, 2> {
let n = gauss_rule.x.len();
let v = legendre_vandermonde(&gauss_rule.x, n - 1);
let invnorm: Vec<T> = (0..n)
.map(|i| T::from_f64_unchecked(0.5 + i as f64))
.collect();
DTensor::<T, 2>::from_fn([n, n], |idx| {
let (i, j) = (idx[0], idx[1]);
v[[j, i]] * gauss_rule.w[j] * invnorm[i]
})
}
pub fn interpolate_1d_legendre<T: CustomNumeric>(values: &[T], gauss_rule: &Rule<T>) -> Vec<T> {
let n = values.len();
assert_eq!(
n,
gauss_rule.x.len(),
"Values length must match grid points"
);
let collocation_matrix = legendre_collocation_matrix(gauss_rule);
let mut coeffs = vec![T::zero(); n];
for i in 0..n {
for j in 0..n {
coeffs[i] = coeffs[i] + collocation_matrix[[i, j]] * values[j];
}
}
coeffs
}
pub fn evaluate_interpolated_polynomial<T: CustomNumeric>(x: T, coeffs: &[T]) -> T {
let n = coeffs.len();
let mut result = T::zero();
for i in 0..n {
result = result + coeffs[i] * evaluate_legendre_polynomial(x, i);
}
result
}
fn evaluate_legendre_polynomial<T: CustomNumeric>(x: T, n: usize) -> T {
evaluate_legendre_basis(x, n + 1)[n]
}
pub fn evaluate_legendre_basis<T: CustomNumeric>(x: T, n: usize) -> Vec<T> {
if n == 0 {
return Vec::new();
}
let mut p = Vec::with_capacity(n);
p.push(T::from_f64_unchecked(1.0));
if n > 1 {
p.push(x);
}
for i in 1..n - 1 {
let i_f64 = i as f64;
let next_p = (T::from_f64_unchecked(2.0 * i_f64 + 1.0) * x * p[i]
- T::from_f64_unchecked(i_f64) * p[i - 1])
/ T::from_f64_unchecked(i_f64 + 1.0);
p.push(next_p);
}
p
}
#[cfg(test)]
#[path = "interpolation1d_tests.rs"]
mod tests;