#[cfg(feature = "cpu")]
use rayon::prelude::*;
use num_traits::Float;
use std::cmp::Ordering::Equal;
use std::fmt::Debug;
use std::vec::Vec;
use loess_rs::internals::algorithms::regression::{
PolynomialDegree, RegressionContext, SolverLinalg, ZeroWeightFallback,
};
use loess_rs::internals::evaluation::intervals::IntervalMethod;
use loess_rs::internals::math::distance::{DistanceLinalg, DistanceMetric};
use loess_rs::internals::math::kernel::WeightFunction;
use loess_rs::internals::math::linalg::FloatLinalg;
use loess_rs::internals::math::neighborhood::{KDTree, Neighborhood, NodeDistance};
use loess_rs::internals::primitives::buffer::{FittingBuffer, NeighborhoodSearchBuffer};
use crate::engine::executor::LoessDistanceCalculator;
#[allow(clippy::too_many_arguments)]
#[cfg(feature = "cpu")]
pub fn interval_pass_parallel<T>(
x: &[T],
y: &[T],
x_search: &[T], y_search: &[T], y_smooth: &[T],
dims: usize,
window_size: usize,
robustness_weights: &[T],
weight_function: WeightFunction,
_interval_method: &IntervalMethod<T>,
polynomial_degree: PolynomialDegree,
distance_metric: &DistanceMetric<T>,
scales: &[T],
) -> Vec<T>
where
T: FloatLinalg + DistanceLinalg + SolverLinalg + Float + Debug + Send + Sync + 'static,
{
let n = y.len();
if n == 0 {
return Vec::new();
}
let kdtree = KDTree::new(x_search, dims);
let mut residuals: Vec<T> = y
.iter()
.zip(y_smooth.iter())
.map(|(&yi, &si)| (yi - si).abs())
.collect();
let median_idx = n / 2;
if median_idx < residuals.len() {
residuals.select_nth_unstable_by(median_idx, |a, b| a.partial_cmp(b).unwrap_or(Equal));
}
let median_residual = residuals.get(median_idx).copied().unwrap_or(T::zero());
let sigma = median_residual * T::from(1.4826).unwrap_or(T::one());
let leverages: Vec<T> = (0..n)
.into_par_iter()
.map_init(
|| {
(
NeighborhoodSearchBuffer::<NodeDistance<T>>::new(window_size),
Neighborhood::<T>::new(),
FittingBuffer::new(window_size, dims),
)
},
|(search_buffer, neighborhood, fitting_buffer), i| {
let dist_calc = LoessDistanceCalculator {
metric: distance_metric,
scales,
};
let query_offset = i * dims;
let query_point = &x[query_offset..query_offset + dims];
kdtree.find_k_nearest(
query_point,
window_size,
&dist_calc,
None,
search_buffer,
neighborhood,
);
let mut context = RegressionContext::new(
x_search,
dims,
y_search,
i,
Some(query_point),
neighborhood,
!robustness_weights.is_empty(),
robustness_weights,
weight_function,
ZeroWeightFallback::UseLocalMean,
polynomial_degree,
true, Some(fitting_buffer),
);
if let Some((_, leverage)) = context.fit() {
leverage
} else {
T::zero()
}
},
)
.collect();
leverages
.iter()
.map(|&lev| {
if lev > T::zero() {
sigma * lev.sqrt()
} else {
sigma * T::from(0.1).unwrap_or(T::zero())
}
})
.collect()
}
#[cfg(not(feature = "cpu"))]
pub fn interval_pass_parallel<T>(
_x: &[T],
y: &[T],
y_smooth: &[T],
_dims: usize,
_window_size: usize,
_robustness_weights: &[T],
_weight_function: WeightFunction,
_interval_method: &IntervalMethod<T>,
_polynomial_degree: PolynomialDegree,
_distance_metric: &DistanceMetric<T>,
_scales: &[T],
) -> Vec<T>
where
T: Float + Send + Sync,
{
let n = y.len();
let mut residuals: Vec<T> = y
.iter()
.zip(y_smooth.iter())
.map(|(&yi, &si)| (yi - si).abs())
.collect();
if n == 0 {
return Vec::new();
}
residuals.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let median = residuals.get(n / 2).copied().unwrap_or(T::zero());
let sigma = median * T::from(1.4826).unwrap_or(T::one());
vec![sigma; n]
}