use std::iter::{repeat, Product};
use na::{
ArrayStorage, Const, DefaultAllocator, DimMin, DimName, Matrix, RealField, Scalar,
SquareMatrix, Storage, StorageMut, U1, Vector, allocator::Allocator,
};
use crate::{powers::monomial_exponents, rbf::RBFInterpolator};
pub enum RBFInterpolatorBuilder<
T,
const DEGREE: usize,
const MONOMIALS: usize,
const POINTS: usize,
const DIM: usize,
> {
Linear,
ThinPlateSpline,
Cubic,
Quintic,
Multiquadratic { epsilon: T },
InverseMultiquadratic { epsilon: T },
InverseQuadratic { epsilon: T },
Gaussian { epsilon: T },
}
impl<T, const DEGREE: usize, const MONOMIALS: usize, const POINTS: usize, const DIM: usize>
RBFInterpolatorBuilder<T, DEGREE, MONOMIALS, POINTS, DIM>
where
T: Scalar + RealField + Copy + Product,
{
fn construct_phi<SP>(
&self,
points: &Matrix<T, Const<DIM>, Const<POINTS>, SP>,
) -> SquareMatrix<
T,
Const<POINTS>,
<DefaultAllocator as Allocator<Const<POINTS>, Const<POINTS>>>::Buffer<T>,
>
where
SP: Storage<T, Const<DIM>, Const<POINTS>>,
{
let iter = points
.column_iter()
.flat_map(|point_a| points.column_iter().zip(repeat(point_a)))
.map(|(point_a, point_b)| {
self.kernel((point_a - point_b).map(|e| e.powi(2)).sum().sqrt())
});
SquareMatrix::<
T,
Const<POINTS>,
<DefaultAllocator as Allocator<Const<POINTS>, Const<POINTS>>>::Buffer<T>,
>::from_iterator(iter)
}
fn embelish_phi<SP, SH>(
&self,
points: &Matrix<T, Const<DIM>, Const<POINTS>, SP>,
phi: SquareMatrix<T, Const<POINTS>, SH>,
) -> SquareMatrix<
T,
Const<{ POINTS + MONOMIALS }>,
<DefaultAllocator as Allocator<
Const<{ POINTS + MONOMIALS }>,
Const<{ POINTS + MONOMIALS }>,
>>::Buffer<T>,
>
where
SP: Storage<T, Const<DIM>, Const<POINTS>>,
SH: StorageMut<T, Const<POINTS>, Const<POINTS>>,
Const<{ POINTS + MONOMIALS }>: DimName,
DefaultAllocator: Allocator<Const<POINTS>, Const<POINTS>>
+ Allocator<Const<{ POINTS + MONOMIALS }>, Const<{ POINTS + MONOMIALS }>>,
{
let mut phi = phi.resize_generic(
Const::<{ POINTS + MONOMIALS }>,
Const::<{ POINTS + MONOMIALS }>,
na::zero(),
);
let exponents = monomial_exponents::<DIM, DEGREE>();
assert_eq!(
exponents.len(),
MONOMIALS,
"The choices of generics polynomial DEGREE and number of variables DIM did not match the number of MONONOMIALS."
);
for (i, point) in points.column_iter().enumerate() {
for (j, exponent) in exponents.iter().enumerate() {
debug_assert_eq!(exponent.len(), point.len());
let value: T = exponent
.iter()
.zip(point.row_iter())
.map(|(&exponent, ordinate)| ordinate[(0, 0)].powi(exponent))
.product();
phi[(i, POINTS + j)] = value;
phi[(POINTS + j, i)] = value;
}
}
phi
}
fn embelish_values<SV>(
&self,
values: Vector<T, Const<POINTS>, SV>,
) -> Vector<
T,
Const<{ POINTS + MONOMIALS }>,
<DefaultAllocator as Allocator<Const<{ POINTS + MONOMIALS }>, U1>>::Buffer<T>,
>
where
SV: StorageMut<T, Const<POINTS>, U1>,
Const<{ POINTS + MONOMIALS }>: DimName,
{
values.resize_generic(Const::<{ POINTS + MONOMIALS }>, U1, na::zero())
}
fn solve_phi<const M: usize, SH, SV>(
&self,
phi: SquareMatrix<T, Const<M>, SH>,
values: &mut Vector<T, Const<M>, SV>,
) -> bool
where
SH: Storage<T, Const<M>, Const<M>>,
SV: StorageMut<T, Const<M>, U1>,
Const<M>: DimMin<Const<M>, Output = Const<M>>,
DefaultAllocator: Allocator<<Const<M> as DimMin<Const<M>>>::Output>,
{
phi.lu().solve_mut(values)
}
pub fn build<SP, SV>(
self,
points: Matrix<T, Const<DIM>, Const<POINTS>, SP>,
values: Vector<T, Const<POINTS>, SV>,
) -> Option<
RBFInterpolator<
T,
DEGREE,
MONOMIALS,
POINTS,
DIM,
SP,
ArrayStorage<T, { POINTS + MONOMIALS }, 1>,
>,
>
where
SP: Storage<T, Const<DIM>, Const<POINTS>>,
SV: StorageMut<T, Const<POINTS>, U1>,
Const<{ POINTS + MONOMIALS }>:
DimMin<Const<{ POINTS + MONOMIALS }>, Output = Const<{ POINTS + MONOMIALS }>>,
{
let phi = self.construct_phi::<SP>(&points);
let phi = self.embelish_phi::<SP, ArrayStorage<T, POINTS, POINTS>>(&points, phi);
let mut values = self.embelish_values(values);
if self.solve_phi(phi, &mut values) {
Some(
RBFInterpolator::<T, _, _, _, _, SP, ArrayStorage<T, { POINTS + MONOMIALS }, 1>> {
kernel: self,
points,
weights: values,
},
)
} else {
None
}
}
}