use crate::advanced::enhanced_kriging::AnisotropicCovariance;
use crate::advanced::kriging::CovarianceFunction;
use crate::error::InterpolateResult;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::{Debug, Display};
use std::ops::{Add, Div, Mul, Sub};
type SparseComponents<F> = (Vec<(usize, usize)>, Vec<F>);
#[allow(dead_code)]
pub fn find_nearest_neighbors<F: Float + FromPrimitive>(
query_point: &ArrayView1<F>,
points: &Array2<F>,
max_neighbors: usize,
radius_multiplier: F,
) -> InterpolateResult<(Vec<usize>, Vec<F>)> {
let n_points = points.shape()[0];
let n_dims = points.shape()[1];
let mut distances = Vec::with_capacity(n_points);
for i in 0..n_points {
let mut dist_sq = F::zero();
for j in 0..n_dims {
let diff = query_point[j] - points[[i, j]];
dist_sq = dist_sq + diff * diff;
}
distances.push((i, dist_sq.sqrt()));
}
distances.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
let radius = if distances.len() > max_neighbors {
distances[max_neighbors - 1].1 * radius_multiplier
} else {
F::infinity()
};
let mut indices = Vec::with_capacity(max_neighbors);
let mut selected_distances = Vec::with_capacity(max_neighbors);
for (idx, dist) in distances {
if dist <= radius && indices.len() < max_neighbors {
indices.push(idx);
selected_distances.push(dist);
}
}
Ok((indices, selected_distances))
}
#[allow(dead_code)]
pub fn compute_anisotropic_distance<
F: Float
+ FromPrimitive
+ Debug
+ Display
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign,
>(
p1: &ArrayView1<F>,
p2: &ArrayView1<F>,
anisotropic_cov: &AnisotropicCovariance<F>,
) -> InterpolateResult<F> {
let n_dims = p1.len();
if n_dims != anisotropic_cov.length_scales.len() {
return Err(crate::error::InterpolateError::DimensionMismatch(
"Number of length scales must match dimension of points".to_string(),
));
}
let mut sum_sq = F::zero();
for i in 0..n_dims {
let diff = (p1[i] - p2[i]) / anisotropic_cov.length_scales[i];
sum_sq += diff * diff;
}
Ok(sum_sq.sqrt())
}
#[allow(dead_code)]
pub fn compute_covariance<
F: Float
+ FromPrimitive
+ Debug
+ Display
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign,
>(
r: F,
anisotropic_cov: &AnisotropicCovariance<F>,
) -> F {
match anisotropic_cov.cov_fn {
CovarianceFunction::SquaredExponential => {
anisotropic_cov.sigma_sq * (-r * r).exp()
}
CovarianceFunction::Exponential => {
anisotropic_cov.sigma_sq * (-r).exp()
}
CovarianceFunction::Matern32 => {
let sqrt3_r = F::from_f64(3.0).expect("Operation failed").sqrt() * r;
anisotropic_cov.sigma_sq * (F::one() + sqrt3_r) * (-sqrt3_r).exp()
}
CovarianceFunction::Matern52 => {
let sqrt5_r = F::from_f64(5.0).expect("Operation failed").sqrt() * r;
let factor =
F::one() + sqrt5_r + F::from_f64(5.0).expect("Operation failed") * r * r / F::from_f64(3.0).expect("Operation failed");
anisotropic_cov.sigma_sq * factor * (-sqrt5_r).exp()
}
CovarianceFunction::RationalQuadratic => {
let alpha = anisotropic_cov.extra_params;
let r_sq_div_2a = r * r / (F::from_f64(2.0).expect("Operation failed") * alpha);
anisotropic_cov.sigma_sq * (F::one() + r_sq_div_2a).powf(-alpha)
}
}
}
#[allow(dead_code)]
pub fn compute_low_rank_approximation<
F: Float
+ FromPrimitive
+ Debug
+ Display
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign,
>(
points: &Array2<F>,
anisotropic_cov: &AnisotropicCovariance<F>,
rank: usize,
) -> InterpolateResult<(Array2<F>, Array1<F>, Array2<F>)> {
let n_points = points.shape()[0];
let max_sample = std::cmp::min(rank * 2, n_points);
let mut sample_indices = Vec::with_capacity(max_sample);
let step = n_points / max_sample;
for i in 0..max_sample {
sample_indices.push(i * step);
}
let mut sample_cov = Array2::zeros((max_sample, max_sample));
for i in 0..max_sample {
for j in 0..max_sample {
let idx_i = sample_indices[i];
let idx_j = sample_indices[j];
if i == j {
sample_cov[[i, j]] = anisotropic_cov.sigma_sq + anisotropic_cov.nugget;
} else {
let dist = compute_anisotropic_distance(
&points.slice(scirs2_core::ndarray::s![idx_i, ..]),
&points.slice(scirs2_core::ndarray::s![idx_j, ..]),
anisotropic_cov,
)?;
sample_cov[[i, j]] = compute_covariance(dist, anisotropic_cov);
}
}
}
#[cfg(feature = "linalg")]
let (u, s, vt) = {
use ndarray_linalg::SVD;
let sample_cov_f64 = sample_cov.mapv(|x| x.to_f64().expect("Operation failed"));
match sample_cov_f64.svd(true, true) {
Ok((u_val, s_val, vt_val)) => {
let u = u_val.map_or_else(
|| Array2::eye(s_val.len()),
|u| u.mapv(|x| F::from_f64(x).expect("Operation failed")),
);
let s = s_val.mapv(|x| F::from_f64(x).expect("Operation failed"));
let vt = vt_val.map_or_else(
|| Array2::eye(s_val.len()),
|vt| vt.mapv(|x| F::from_f64(x).expect("Operation failed")),
);
(u, s, vt)
}
Err(_) => {
return Err(crate::error::InterpolateError::ComputationError(
"SVD computation failed for low-rank approximation".to_string(),
));
}
}
};
#[cfg(not(feature = "linalg"))]
let (u, s, vt) = {
let identity = Array2::eye(max_sample);
let values = Array1::from_elem(max_sample, anisotropic_cov.sigma_sq);
(identity.clone(), values, identity)
};
let actual_rank = std::cmp::min(rank, s.len());
let u_r = u.slice(scirs2_core::ndarray::s![.., 0..actual_rank]).to_owned();
let s_r = s.slice(scirs2_core::ndarray::s![0..actual_rank]).to_owned();
let v_r = vt.slice(scirs2_core::ndarray::s![0..actual_rank, ..]).t().to_owned();
Ok((u_r, s_r, v_r))
}
#[allow(dead_code)]
pub fn compute_tapered_covariance<
F: Float
+ FromPrimitive
+ Debug
+ Display
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign,
>(
points: &Array2<F>,
anisotropic_cov: &AnisotropicCovariance<F>,
taper_range: F,
) -> InterpolateResult<SparseComponents<F>> {
let n_points = points.shape()[0];
let mut indices = Vec::new();
let mut values = Vec::new();
for i in 0..n_points {
for j in 0..=i {
if i == j {
let value = anisotropic_cov.sigma_sq + anisotropic_cov.nugget;
indices.push((i, j));
values.push(value);
} else {
let dist = compute_anisotropic_distance(
&points.slice(scirs2_core::ndarray::s![i, ..]),
&points.slice(scirs2_core::ndarray::s![j, ..]),
anisotropic_cov,
)?;
if dist <= taper_range {
let value = compute_covariance(dist, anisotropic_cov);
indices.push((i, j));
values.push(value);
indices.push((j, i));
values.push(value);
}
}
}
}
Ok((indices, values))
}
#[allow(dead_code)]
pub fn project_to_feature<
F: Float
+ FromPrimitive
+ Debug
+ Display
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign,
>(
query_point: &ArrayView1<F>,
points: &Array2<F>,
feature_idx: usize,
anisotropic_cov: &AnisotropicCovariance<F>,
) -> InterpolateResult<F> {
let n_points = points.shape()[0];
let landmark_idx = feature_idx % n_points;
let landmark = points.slice(scirs2_core::ndarray::s![landmark_idx, ..]);
let dist = compute_anisotropic_distance(query_point, &landmark, anisotropic_cov)?;
let projection = compute_covariance(dist, anisotropic_cov);
Ok(projection)
}