pub mod kernel;
pub use kernel::*;
use na::storage::Storage;
use na::{
DMatrix, DVector, Matrix3, Matrix3x4, Matrix4, Point3, RealField, Vector, Vector3, Vector4, U1,
U3, U4,
};
use num_traits::{Float, Zero};
pub trait Real: Float + RealField + std::fmt::Debug {}
impl<T> Real for T where T: Float + RealField + std::fmt::Debug {}
pub type Pow3HRBF<T> = HRBF<T, kernel::Pow3<T>>;
pub type Pow5HRBF<T> = HRBF<T, kernel::Pow5<T>>;
pub type GaussHRBF<T> = HRBF<T, kernel::Gauss<T>>;
pub type Csrbf31HRBF<T> = HRBF<T, kernel::Csrbf31<T>>;
pub type Csrbf42HRBF<T> = HRBF<T, kernel::Csrbf42<T>>;
#[derive(Clone, Debug)]
pub enum KernelType<K> {
Variable(Vec<K>),
Constant(K),
}
impl<K> ::std::ops::Index<usize> for KernelType<K> {
type Output = K;
fn index(&self, index: usize) -> &K {
match *self {
KernelType::Variable(ref ks) => &ks[index],
KernelType::Constant(ref k) => k,
}
}
}
#[derive(Clone, Debug)]
pub struct HRBF<T, K>
where
T: Real,
K: Kernel<T>,
{
sites: Vec<Point3<T>>,
betas: Vec<Vector4<T>>,
kernel: KernelType<K>,
}
pub trait HRBFTrait<T: Real> {
fn fit(&mut self, points: &[Point3<T>], normals: &[Vector3<T>]) -> bool;
fn fit_offset(&mut self, points: &[Point3<T>], offsets: &[T], normals: &[Vector3<T>]) -> bool;
fn fit_system(
&self,
points: &[Point3<T>],
potential: &[T],
normals: &[Vector3<T>],
) -> (DMatrix<T>, DVector<T>);
fn eval(&self, p: Point3<T>) -> T;
fn grad(&self, p: Point3<T>) -> Vector3<T>;
fn hess(&self, p: Point3<T>) -> Matrix3<T>;
}
impl<T, K> HRBFTrait<T> for HRBF<T, K>
where
T: Real,
K: Kernel<T> + Default,
{
fn fit(&mut self, points: &[Point3<T>], normals: &[Vector3<T>]) -> bool {
HRBF::fit(self, points, normals)
}
fn fit_offset(&mut self, points: &[Point3<T>], offsets: &[T], normals: &[Vector3<T>]) -> bool {
HRBF::fit_offset(self, points, offsets, normals)
}
fn fit_system(
&self,
points: &[Point3<T>],
potential: &[T],
normals: &[Vector3<T>],
) -> (DMatrix<T>, DVector<T>) {
HRBF::fit_system(self, points, potential, normals)
}
fn eval(&self, p: Point3<T>) -> T {
HRBF::eval(self, p)
}
fn grad(&self, p: Point3<T>) -> Vector3<T> {
HRBF::grad(self, p)
}
fn hess(&self, p: Point3<T>) -> Matrix3<T> {
HRBF::hess(self, p)
}
}
impl<T, K> Default for HRBF<T, K>
where
T: Real,
K: Kernel<T> + Default,
{
fn default() -> Self {
HRBF {
sites: Vec::new(),
betas: Vec::new(),
kernel: KernelType::Constant(K::default()),
}
}
}
impl<T, K> HRBF<T, K>
where
T: Real,
K: Kernel<T> + LocalKernel<T>,
{
pub fn radius(mut self, radius: T) -> Self {
self.kernel = KernelType::Constant(K::new(radius));
self
}
pub fn radii(mut self, radii: Vec<T>) -> Self {
self.kernel = KernelType::Variable(radii.into_iter().map(|r| K::new(r)).collect());
self
}
}
impl<T, K> HRBF<T, K>
where
T: Real,
K: Kernel<T> + Default,
{
pub fn new(sites: Vec<Point3<T>>) -> Self {
HRBF {
sites,
..HRBF::default()
}
}
pub fn with_kernel(self, kernel: KernelType<K>) -> Self {
HRBF { kernel, ..self }
}
pub fn sites(&self) -> &Vec<Point3<T>> {
&self.sites
}
pub fn betas(&self) -> &Vec<Vector4<T>> {
&self.betas
}
#[allow(non_snake_case)]
pub fn fit(&mut self, points: &[Point3<T>], normals: &[Vector3<T>]) -> bool {
assert!(normals.len() == points.len());
let num_sites = self.sites.len();
let mut potential = Vec::new();
potential.resize(num_sites, T::zero());
let (A, b) = self.fit_system(points, &potential, normals);
self.betas.clear();
if let Some(x) = A.lu().solve(&b) {
assert!(x.len() == 4 * num_sites);
self.betas.resize(num_sites, Vector4::zero());
for j in 0..num_sites {
self.betas[j].copy_from(&x.fixed_rows::<U4>(4 * j));
}
true
} else {
false
}
}
#[allow(non_snake_case)]
pub fn fit_offset(
&mut self,
points: &[Point3<T>],
offsets: &[T],
normals: &[Vector3<T>],
) -> bool {
assert!(normals.len() == points.len());
let num_sites = self.sites.len();
let (A, b) = self.fit_system(points, offsets, normals);
self.betas.clear();
if let Some(x) = A.lu().solve(&b) {
assert!(x.len() == 4 * num_sites);
self.betas.resize(num_sites, Vector4::zero());
for j in 0..num_sites {
self.betas[j].copy_from(&x.fixed_rows::<U4>(4 * j));
}
true
} else {
false
}
}
#[allow(non_snake_case)]
pub fn fit_system(
&self,
points: &[Point3<T>],
potential: &[T],
normals: &[Vector3<T>],
) -> (DMatrix<T>, DVector<T>) {
assert!(normals.len() == points.len());
assert!(potential.len() == points.len());
let rows = 4 * points.len();
let num_sites = self.sites.len();
let cols = 4 * num_sites;
let mut A = DMatrix::<T>::zeros(rows, cols);
let mut b = DVector::<T>::zeros(rows);
for (i, p) in points.iter().enumerate() {
b[4 * i] = potential[i];
b.fixed_rows_mut::<U3>(4 * i + 1).copy_from(&normals[i]);
for j in 0..num_sites {
A.fixed_slice_mut::<U4, U4>(4 * i, 4 * j)
.copy_from(&self.fit_block(*p, j));
}
}
(A, b)
}
fn grad_phi(&self, x: Vector3<T>, l: T, j: usize) -> Vector3<T> {
x * self.kernel[j].df_l(l)
}
fn hess_phi(&self, x: Vector3<T>, l: T, j: usize) -> Matrix3<T> {
let df_l = self.kernel[j].df_l(l);
let mut hess = Matrix3::identity();
if l <= T::zero() {
debug_assert!({
let g = self.kernel[j].ddf(l) - df_l;
g * g < T::from(1e-12).unwrap()
});
return hess * df_l;
}
let ddf = self.kernel[j].ddf(l);
let x_hat = x / l;
hess.ger(ddf - df_l, &x_hat, &x_hat, df_l);
hess
}
fn third_deriv_prod_phi<S>(
&self,
x: Vector3<T>,
l: T,
b: &Vector<T, U3, S>,
j: usize,
) -> Matrix3<T>
where
S: Storage<T, U3, U1>,
{
if l <= T::zero() {
debug_assert!({
let g = self.kernel[j].g(l);
let dddf = self.kernel[j].dddf(l);
dddf == T::zero() && g == T::zero()
});
return Matrix3::zero();
}
let g = self.kernel[j].g(l);
let dddf = self.kernel[j].dddf(l);
let x_hat = x / l;
let x_dot_b = b.dot(&x_hat);
let mut mtx = Matrix3::identity();
let _3 = T::from(3).unwrap();
let _1 = T::one();
mtx.ger(_1, b, &x_hat, x_dot_b);
mtx.ger(_1, &x_hat, b, _1);
mtx.ger((dddf - _3 * g) * x_dot_b, &x_hat, &x_hat, g);
mtx
}
#[inline]
fn fourth_deriv_prod_phi<S>(
&self,
x: Vector3<T>,
l: T,
b: &Vector<T, U3, S>,
c: &Vector<T, U3, S>,
j: usize,
) -> Matrix3<T>
where
S: Storage<T, U3, U1>,
{
let g_l = self.kernel[j].g_l(l);
let bc_tr = Matrix3::new(
b[0] * c[0],
b[1] * c[0],
b[2] * c[0],
b[0] * c[1],
b[1] * c[1],
b[2] * c[1],
b[0] * c[2],
b[1] * c[2],
b[2] * c[2],
);
let c_dot_b = bc_tr.trace();
let bc_tr_plus_cb_tr = bc_tr + bc_tr.transpose();
let mut res = Matrix3::identity();
if l <= T::zero() {
debug_assert!({
let h3 = self.kernel[j].h(l, T::from(3).unwrap());
let h52 = self.kernel[j].h(l, T::from(5.0 / 2.0).unwrap());
let ddddf = self.kernel[j].ddddf(l);
let a = ddddf - T::from(6.0).unwrap() * h52;
h3 == T::zero() && a * a < T::from(1e-12).unwrap()
});
return res * (g_l * c_dot_b) + (bc_tr_plus_cb_tr) * g_l;
}
let h3 = self.kernel[j].h(l, T::from(3).unwrap());
let h52 = self.kernel[j].h(l, T::from(5.0 / 2.0).unwrap());
let ddddf = self.kernel[j].ddddf(l);
let a = ddddf - T::from(6.0).unwrap() * h52;
let x_hat = x / l;
let x_dot_b = x_hat.dot(b);
let x_dot_c = x_hat.dot(c);
let cb_sum = c * x_dot_b + b * x_dot_c;
let _1 = T::one();
res.ger(
a * x_dot_b * x_dot_c + h3 * c_dot_b,
&x_hat,
&x_hat,
h3 * x_dot_c * x_dot_b + g_l * c_dot_b,
);
res.ger(h3, &cb_sum, &x_hat, _1);
res.ger(h3, &x_hat, &cb_sum, _1);
res + (bc_tr_plus_cb_tr) * g_l
}
pub fn eval(&self, p: Point3<T>) -> T {
self.betas
.iter()
.enumerate()
.fold(T::zero(), |sum, (j, b)| sum + self.eval_block(p, j).dot(b))
}
fn eval_block(&self, p: Point3<T>, j: usize) -> Vector4<T> {
let x = p - self.sites[j];
let l = x.norm();
let w = self.kernel[j].f(l);
let g = self.grad_phi(x, l, j);
Vector4::new(w, g[0], g[1], g[2])
}
pub fn grad(&self, p: Point3<T>) -> Vector3<T> {
self.betas
.iter()
.enumerate()
.fold(Vector3::zero(), |sum, (j, b)| {
sum + self.grad_block(p, j) * b
})
}
fn grad_block(&self, p: Point3<T>, j: usize) -> Matrix3x4<T> {
let x = p - self.sites[j];
let l = x.norm();
let h = self.hess_phi(x, l, j);
let mut grad = Matrix3x4::zero();
grad.column_mut(0).copy_from(&self.grad_phi(x, l, j));
grad.fixed_columns_mut::<U3>(1).copy_from(&h);
grad
}
pub fn hess(&self, p: Point3<T>) -> Matrix3<T> {
self.betas
.iter()
.enumerate()
.fold(Matrix3::zero(), |sum, (j, b)| {
sum + self.hess_block_prod(p, b, j)
})
}
#[inline]
fn hess_block_prod(&self, p: Point3<T>, b: &Vector4<T>, j: usize) -> Matrix3<T> {
let x = p - self.sites[j];
let l = x.norm();
let b3 = b.fixed_rows::<U3>(1);
let h = self.hess_phi(x, l, j);
h * b[0] + self.third_deriv_prod_phi(x, l, &b3, j)
}
pub fn fit_block(&self, p: Point3<T>, j: usize) -> Matrix4<T> {
let x = p - self.sites[j];
let l = x.norm();
let w = self.kernel[j].f(l);
let g = self.grad_phi(x, l, j);
let h = self.hess_phi(x, l, j);
Matrix4::new(
w,
g[0],
g[1],
g[2],
g[0],
h[(0, 0)],
h[(0, 1)],
h[(0, 2)],
g[1],
h[(1, 0)],
h[(1, 1)],
h[(1, 2)],
g[2],
h[(2, 0)],
h[(2, 1)],
h[(2, 2)],
)
}
pub fn grad_fit_block_prod(&self, p: Point3<T>, b: Vector4<T>, j: usize) -> Matrix3x4<T> {
let x = p - self.sites[j];
let l = x.norm();
let b3 = b.fixed_rows::<U3>(1);
let g = self.grad_phi(x, l, j);
let h = self.hess_phi(x, l, j);
let third = h * b[0] + self.third_deriv_prod_phi(x, l, &b3, j);
let mut grad = Matrix3x4::zero();
grad.column_mut(0).copy_from(&(g * b[0] + h * b3));
grad.fixed_columns_mut::<U3>(1).copy_from(&third);
grad
}
fn hess_fit_prod_block(&self, p: Point3<T>, c: Vector4<T>, j: usize) -> Matrix3<T> {
let x = p - self.sites[j];
let l = x.norm();
let c3 = c.fixed_rows::<U3>(1);
let a = self.betas[j][0];
let b = self.betas[j].fixed_rows::<U3>(1);
self.hess_phi(x, l, j) * c[0] * a
+ self.third_deriv_prod_phi(x, l, &b, j) * c[0]
+ self.third_deriv_prod_phi(x, l, &c3, j) * a
+ self.fourth_deriv_prod_phi(x, l, &b, &c3, j)
}
pub fn hess_fit_prod(&self, p: Point3<T>, c: Vector4<T>) -> Matrix3<T> {
(0..self.sites.len()).fold(Matrix3::zero(), |sum, j| {
sum + self.hess_fit_prod_block(p, c, j)
})
}
}