#[cfg(not(feature = "std"))]
use alloc::collections::BinaryHeap;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::collections::BinaryHeap;
#[cfg(feature = "std")]
use std::vec::Vec;
use core::fmt::Debug;
use core::ops::{Deref, DerefMut};
use num_traits::Float;
#[derive(Debug, Clone)]
pub struct Slot<T>(Vec<T>);
impl<T> Slot<T> {
#[inline]
pub fn new(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
}
#[inline]
pub fn ensure_capacity(&mut self, capacity: usize) {
if self.0.capacity() < capacity {
self.0.reserve(capacity - self.0.capacity());
}
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
pub fn as_vec(&self) -> &Vec<T> {
&self.0
}
#[inline]
pub fn as_vec_mut(&mut self) -> &mut Vec<T> {
&mut self.0
}
#[inline]
pub fn into_inner(self) -> Vec<T> {
self.0
}
}
impl<T> Default for Slot<T> {
fn default() -> Self {
Self(Vec::new())
}
}
impl<T> Deref for Slot<T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Slot<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<Vec<T>> for Slot<T> {
fn from(v: Vec<T>) -> Self {
Self(v)
}
}
pub struct LoessBuffer<T: Float, N, NH> {
pub search_buffer: NeighborhoodSearchBuffer<N>,
pub neighborhood: NH,
pub fitting_buffer: FittingBuffer<T>,
pub executor_buffer: ExecutorBuffer<T>,
pub cv_buffer: CVBuffer<T>,
}
impl<T: Float + Debug + Send + Sync + 'static, N, NH> LoessBuffer<T, N, NH>
where
N: Ord,
NH: NeighborhoodStorage,
{
pub fn new(n: usize, dims: usize, k: usize, n_coeffs: usize) -> Self {
Self {
search_buffer: NeighborhoodSearchBuffer::new(k),
neighborhood: NH::with_capacity(k),
fitting_buffer: FittingBuffer::<T>::new(k, n_coeffs),
executor_buffer: ExecutorBuffer::<T>::new(n, dims),
cv_buffer: CVBuffer::<T>::new(n, dims),
}
}
pub fn ensure_capacity(&mut self, n_total: usize, dims: usize, k: usize, n_coeffs: usize) {
if self.neighborhood.capacity() < k {
self.neighborhood = NH::with_capacity(k);
self.search_buffer = NeighborhoodSearchBuffer::new(k);
}
if self.fitting_buffer.weights.capacity() < k
|| self.fitting_buffer.xtw_x.capacity() < n_coeffs * n_coeffs
{
self.fitting_buffer = FittingBuffer::new(k, n_coeffs);
}
self.executor_buffer.ensure_capacity(n_total, dims);
self.cv_buffer.ensure_capacity(n_total, dims);
}
}
pub trait NeighborhoodStorage {
fn with_capacity(k: usize) -> Self;
fn capacity(&self) -> usize;
}
pub struct NeighborhoodSearchBuffer<N> {
pub(crate) heap: BinaryHeap<N>,
pub(crate) stack: Vec<usize>,
}
impl<N: Ord> NeighborhoodSearchBuffer<N> {
pub fn new(k: usize) -> Self {
Self {
heap: BinaryHeap::with_capacity(k),
stack: Vec::with_capacity(32),
}
}
pub fn clear(&mut self) {
self.heap.clear();
self.stack.clear();
}
}
pub struct FittingBuffer<T> {
pub weights: Slot<T>,
pub xtw_x: Slot<T>,
pub xtw_y: Slot<T>,
pub col_norms: Slot<T>,
}
impl<T> FittingBuffer<T> {
pub fn new(k: usize, n_coeffs: usize) -> Self {
Self {
weights: Slot::new(k),
xtw_x: Slot::new(n_coeffs * n_coeffs),
xtw_y: Slot::new(n_coeffs),
col_norms: Slot::new(n_coeffs),
}
}
}
#[derive(Debug, Clone)]
pub struct NeighborhoodCache<T> {
pub entries: Vec<CachedNeighborhood<T>>,
pub is_valid: bool,
}
#[derive(Debug, Clone)]
pub struct CachedNeighborhood<T> {
pub indices: Vec<usize>,
pub distances: Vec<T>,
pub max_distance: T,
}
impl<T: Float> NeighborhoodCache<T> {
pub fn new() -> Self {
Self {
entries: Vec::new(),
is_valid: false,
}
}
pub fn with_capacity(n: usize) -> Self {
Self {
entries: Vec::with_capacity(n),
is_valid: false,
}
}
}
impl<T: Float> Default for NeighborhoodCache<T> {
fn default() -> Self {
Self::new()
}
}
pub struct ExecutorBuffer<T> {
pub mins: Slot<T>,
pub maxs: Slot<T>,
pub scales: Slot<T>,
pub robustness_weights: Slot<T>,
pub residuals: Slot<T>,
pub sorted_residuals: Slot<T>,
pub neighborhood_cache: NeighborhoodCache<T>,
}
impl<T: Float> ExecutorBuffer<T> {
pub fn new(n: usize, dims: usize) -> Self {
Self {
mins: Slot::new(dims),
maxs: Slot::new(dims),
scales: Slot::new(dims),
robustness_weights: Slot::new(n),
residuals: Slot::new(n),
sorted_residuals: Slot::new(n),
neighborhood_cache: NeighborhoodCache::with_capacity(n),
}
}
pub fn ensure_capacity(&mut self, n: usize, dims: usize) {
self.mins.ensure_capacity(dims);
self.maxs.ensure_capacity(dims);
self.scales.ensure_capacity(dims);
self.robustness_weights.ensure_capacity(n);
self.residuals.ensure_capacity(n);
self.sorted_residuals.ensure_capacity(n);
}
}
pub struct CVBuffer<T> {
pub train_x: Slot<T>,
pub train_y: Slot<T>,
pub test_x: Slot<T>,
pub test_y: Slot<T>,
}
impl<T> CVBuffer<T> {
pub fn new(n: usize, dims: usize) -> Self {
Self {
train_x: Slot::new(n * dims),
train_y: Slot::new(n),
test_x: Slot::new(n * dims),
test_y: Slot::new(n),
}
}
pub fn ensure_capacity(&mut self, n: usize, dims: usize) {
self.train_x.ensure_capacity(n * dims);
self.train_y.ensure_capacity(n);
self.test_x.ensure_capacity(n * dims);
self.test_y.ensure_capacity(n);
}
}