#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use core::fmt::Debug;
use crate::adapters::batch::BatchLoessBuilder;
use crate::adapters::online::OnlineLoessBuilder;
use crate::adapters::streaming::StreamingLoessBuilder;
use crate::algorithms::regression::SolverLinalg;
use crate::engine::executor::{CVPassFn, IntervalPassFn, SmoothPassFn};
use crate::evaluation::cv::{CVConfig, CVKind};
use crate::evaluation::intervals::IntervalMethod;
use crate::math::distance::DistanceLinalg;
use crate::math::linalg::FloatLinalg;
use crate::primitives::backend::Backend;
pub use crate::adapters::online::UpdateMode;
pub use crate::adapters::streaming::MergeStrategy;
pub use crate::algorithms::regression::{PolynomialDegree, ZeroWeightFallback};
pub use crate::algorithms::robustness::RobustnessMethod;
pub use crate::engine::executor::SurfaceMode;
pub use crate::engine::output::LoessResult;
pub use crate::evaluation::cv::{KFold, LOOCV};
pub use crate::math::boundary::BoundaryPolicy;
pub use crate::math::distance::DistanceMetric;
pub use crate::math::kernel::WeightFunction;
pub use crate::math::scaling::ScalingMethod;
pub use crate::primitives::errors::LoessError;
#[allow(non_snake_case)]
pub mod Adapter {
pub use super::{Batch, Online, Streaming};
}
#[derive(Debug, Clone)]
pub struct LoessBuilder<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> {
pub fraction: Option<T>,
pub iterations: Option<usize>,
pub weight_function: Option<WeightFunction>,
pub robustness_method: Option<RobustnessMethod>,
pub scaling_method: Option<ScalingMethod>,
pub interval_type: Option<IntervalMethod<T>>,
pub cv_fractions: Option<Vec<T>>,
pub(crate) cv_kind: Option<CVKind>,
pub(crate) cv_seed: Option<u64>,
pub auto_converge: Option<T>,
pub return_diagnostics: Option<bool>,
pub compute_residuals: Option<bool>,
pub return_robustness_weights: Option<bool>,
pub boundary_policy: Option<BoundaryPolicy>,
pub zero_weight_fallback: Option<ZeroWeightFallback>,
pub merge_strategy: Option<MergeStrategy>,
pub update_mode: Option<UpdateMode>,
pub chunk_size: Option<usize>,
pub overlap: Option<usize>,
pub window_capacity: Option<usize>,
pub min_points: Option<usize>,
pub polynomial_degree: Option<PolynomialDegree>,
pub dimensions: Option<usize>,
pub distance_metric: Option<DistanceMetric<T>>,
pub surface_mode: Option<SurfaceMode>,
pub cell: Option<T>,
pub interpolation_vertices: Option<usize>,
pub boundary_degree_fallback: Option<bool>,
#[doc(hidden)]
pub custom_smooth_pass: Option<SmoothPassFn<T>>,
#[doc(hidden)]
pub custom_cv_pass: Option<CVPassFn<T>>,
#[doc(hidden)]
pub custom_interval_pass: Option<IntervalPassFn<T>>,
#[doc(hidden)]
pub backend: Option<Backend>,
#[doc(hidden)]
pub parallel: Option<bool>,
#[doc(hidden)]
pub duplicate_param: Option<&'static str>,
}
impl<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> Default
for LoessBuilder<T>
{
fn default() -> Self {
Self::new()
}
}
impl<T: FloatLinalg + DistanceLinalg + Debug + Send + Sync + 'static + SolverLinalg>
LoessBuilder<T>
{
pub fn adapter<A>(self, _adapter: A) -> A::Output
where
A: LoessAdapter<T>,
{
A::convert(self)
}
pub fn new() -> Self {
Self {
fraction: None,
iterations: None,
weight_function: None,
robustness_method: None,
scaling_method: None,
interval_type: None,
cv_fractions: None,
cv_kind: None,
cv_seed: None,
auto_converge: None,
return_diagnostics: None,
compute_residuals: None,
return_robustness_weights: None,
boundary_policy: None,
zero_weight_fallback: None,
merge_strategy: None,
update_mode: None,
chunk_size: None,
overlap: None,
window_capacity: None,
min_points: None,
polynomial_degree: None,
dimensions: None,
distance_metric: None,
surface_mode: None,
cell: None,
interpolation_vertices: None,
boundary_degree_fallback: None,
custom_smooth_pass: None,
custom_cv_pass: None,
custom_interval_pass: None,
backend: None,
parallel: None,
duplicate_param: None,
}
}
pub fn zero_weight_fallback(mut self, policy: ZeroWeightFallback) -> Self {
if self.zero_weight_fallback.is_some() {
self.duplicate_param = Some("zero_weight_fallback");
}
self.zero_weight_fallback = Some(policy);
self
}
pub fn boundary_policy(mut self, policy: BoundaryPolicy) -> Self {
if self.boundary_policy.is_some() {
self.duplicate_param = Some("boundary_policy");
}
self.boundary_policy = Some(policy);
self
}
pub fn merge_strategy(mut self, strategy: MergeStrategy) -> Self {
if self.merge_strategy.is_some() {
self.duplicate_param = Some("merge_strategy");
}
self.merge_strategy = Some(strategy);
self
}
pub fn update_mode(mut self, mode: UpdateMode) -> Self {
if self.update_mode.is_some() {
self.duplicate_param = Some("update_mode");
}
self.update_mode = Some(mode);
self
}
pub fn chunk_size(mut self, size: usize) -> Self {
if self.chunk_size.is_some() {
self.duplicate_param = Some("chunk_size");
}
self.chunk_size = Some(size);
self
}
pub fn overlap(mut self, overlap: usize) -> Self {
if self.overlap.is_some() {
self.duplicate_param = Some("overlap");
}
self.overlap = Some(overlap);
self
}
pub fn window_capacity(mut self, capacity: usize) -> Self {
if self.window_capacity.is_some() {
self.duplicate_param = Some("window_capacity");
}
self.window_capacity = Some(capacity);
self
}
pub fn min_points(mut self, points: usize) -> Self {
if self.min_points.is_some() {
self.duplicate_param = Some("min_points");
}
self.min_points = Some(points);
self
}
pub fn fraction(mut self, fraction: T) -> Self {
if self.fraction.is_some() {
self.duplicate_param = Some("fraction");
}
self.fraction = Some(fraction);
self
}
pub fn iterations(mut self, iterations: usize) -> Self {
if self.iterations.is_some() {
self.duplicate_param = Some("iterations");
}
self.iterations = Some(iterations);
self
}
pub fn weight_function(mut self, wf: WeightFunction) -> Self {
if self.weight_function.is_some() {
self.duplicate_param = Some("weight_function");
}
self.weight_function = Some(wf);
self
}
pub fn robustness_method(mut self, rm: RobustnessMethod) -> Self {
if self.robustness_method.is_some() {
self.duplicate_param = Some("robustness_method");
}
self.robustness_method = Some(rm);
self
}
pub fn scaling_method(mut self, sm: ScalingMethod) -> Self {
if self.scaling_method.is_some() {
self.duplicate_param = Some("scaling_method");
}
self.scaling_method = Some(sm);
self
}
pub fn return_se(mut self) -> Self {
if self.interval_type.is_none() {
self.interval_type = Some(IntervalMethod::se());
}
self
}
pub fn confidence_intervals(mut self, level: T) -> Self {
if self.interval_type.as_ref().is_some_and(|it| it.confidence) {
self.duplicate_param = Some("confidence_intervals");
}
self.interval_type = Some(match self.interval_type {
Some(existing) if existing.prediction => IntervalMethod {
level,
confidence: true,
prediction: true,
se: true,
},
_ => IntervalMethod::confidence(level),
});
self
}
pub fn prediction_intervals(mut self, level: T) -> Self {
if self.interval_type.as_ref().is_some_and(|it| it.prediction) {
self.duplicate_param = Some("prediction_intervals");
}
self.interval_type = Some(match self.interval_type {
Some(existing) if existing.confidence => IntervalMethod {
level,
confidence: true,
prediction: true,
se: true,
},
_ => IntervalMethod::prediction(level),
});
self
}
pub fn cross_validate(mut self, config: CVConfig<'_, T>) -> Self {
if self.cv_fractions.is_some() {
self.duplicate_param = Some("cross_validate");
}
self.cv_fractions = Some(config.fractions().to_vec());
self.cv_kind = Some(config.kind());
self.cv_seed = config.get_seed();
self
}
pub fn auto_converge(mut self, tolerance: T) -> Self {
if self.auto_converge.is_some() {
self.duplicate_param = Some("auto_converge");
}
self.auto_converge = Some(tolerance);
self
}
pub fn return_diagnostics(mut self) -> Self {
self.return_diagnostics = Some(true);
self
}
pub fn return_residuals(mut self) -> Self {
self.compute_residuals = Some(true);
self
}
pub fn return_robustness_weights(mut self) -> Self {
self.return_robustness_weights = Some(true);
self
}
pub fn degree(mut self, degree: PolynomialDegree) -> Self {
if self.polynomial_degree.is_some() {
self.duplicate_param = Some("degree");
}
self.polynomial_degree = Some(degree);
self
}
pub fn dimensions(mut self, dims: usize) -> Self {
if self.dimensions.is_some() {
self.duplicate_param = Some("dimensions");
}
self.dimensions = Some(dims);
self
}
pub fn distance_metric(mut self, metric: DistanceMetric<T>) -> Self {
if self.distance_metric.is_some() {
self.duplicate_param = Some("distance_metric");
}
self.distance_metric = Some(metric);
self
}
pub fn surface_mode(mut self, mode: SurfaceMode) -> Self {
if self.surface_mode.is_some() {
self.duplicate_param = Some("surface_mode");
}
self.surface_mode = Some(mode);
self
}
pub fn cell(mut self, cell: T) -> Self {
if self.cell.is_some() {
self.duplicate_param = Some("cell");
}
self.cell = Some(cell);
self
}
pub fn interpolation_vertices(mut self, vertices: usize) -> Self {
if self.interpolation_vertices.is_some() {
self.duplicate_param = Some("interpolation_vertices");
}
self.interpolation_vertices = Some(vertices);
self
}
pub fn boundary_degree_fallback(mut self, enabled: bool) -> Self {
if self.boundary_degree_fallback.is_some() {
self.duplicate_param = Some("boundary_degree_fallback");
}
self.boundary_degree_fallback = Some(enabled);
self
}
#[doc(hidden)]
pub fn custom_smooth_pass(mut self, pass: SmoothPassFn<T>) -> Self {
self.custom_smooth_pass = Some(pass);
self
}
#[doc(hidden)]
pub fn custom_cv_pass(mut self, pass: CVPassFn<T>) -> Self {
self.custom_cv_pass = Some(pass);
self
}
#[doc(hidden)]
pub fn custom_interval_pass(mut self, pass: IntervalPassFn<T>) -> Self {
self.custom_interval_pass = Some(pass);
self
}
#[doc(hidden)]
pub fn backend(mut self, backend: Backend) -> Self {
self.backend = Some(backend);
self
}
#[doc(hidden)]
pub fn parallel(mut self, parallel: bool) -> Self {
self.parallel = Some(parallel);
self
}
}
pub trait LoessAdapter<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> {
type Output;
fn convert(builder: LoessBuilder<T>) -> Self::Output;
}
#[derive(Debug, Clone, Copy)]
pub struct Batch;
impl<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> LoessAdapter<T>
for Batch
{
type Output = BatchLoessBuilder<T>;
fn convert(builder: LoessBuilder<T>) -> Self::Output {
let mut result = BatchLoessBuilder::default();
if let Some(fraction) = builder.fraction {
result.fraction = fraction;
}
if let Some(iterations) = builder.iterations {
result.iterations = iterations;
}
if let Some(wf) = builder.weight_function {
result.weight_function = wf;
}
if let Some(rm) = builder.robustness_method {
result.robustness_method = rm;
}
if let Some(sm) = builder.scaling_method {
result.scaling_method = sm;
}
if let Some(it) = builder.interval_type {
result.interval_type = Some(it);
}
if let Some(cvf) = builder.cv_fractions {
result.cv_fractions = Some(cvf);
}
if let Some(cvk) = builder.cv_kind {
result.cv_kind = Some(cvk);
}
result.cv_seed = builder.cv_seed;
if let Some(ac) = builder.auto_converge {
result.auto_converge = Some(ac);
}
if let Some(zwf) = builder.zero_weight_fallback {
result.zero_weight_fallback = zwf;
}
if let Some(bp) = builder.boundary_policy {
result.boundary_policy = bp;
}
if let Some(rw) = builder.return_robustness_weights {
result.return_robustness_weights = rw;
}
if let Some(rd) = builder.return_diagnostics {
result.return_diagnostics = rd;
}
if let Some(cr) = builder.compute_residuals {
result.compute_residuals = cr;
}
if let Some(pd) = builder.polynomial_degree {
result.polynomial_degree = pd;
}
if let Some(dims) = builder.dimensions {
result.dimensions = dims;
}
if let Some(dm) = builder.distance_metric {
result.distance_metric = dm;
}
if let Some(cell) = builder.cell {
result.cell = Some(cell.to_f64().unwrap());
}
if let Some(iv) = builder.interpolation_vertices {
result.interpolation_vertices = Some(iv);
}
if let Some(sm) = builder.surface_mode {
result.surface_mode = sm;
}
if let Some(bdf) = builder.boundary_degree_fallback {
result.boundary_degree_fallback = bdf;
}
if let Some(sp) = builder.custom_smooth_pass {
result.custom_smooth_pass = Some(sp);
}
if let Some(cp) = builder.custom_cv_pass {
result.custom_cv_pass = Some(cp);
}
if let Some(ip) = builder.custom_interval_pass {
result.custom_interval_pass = Some(ip);
}
if let Some(b) = builder.backend {
result.backend = Some(b);
}
if let Some(p) = builder.parallel {
result.parallel = Some(p);
}
result.duplicate_param = builder.duplicate_param;
result
}
}
#[derive(Debug, Clone, Copy)]
pub struct Streaming;
impl<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> LoessAdapter<T>
for Streaming
{
type Output = StreamingLoessBuilder<T>;
fn convert(builder: LoessBuilder<T>) -> Self::Output {
let mut result = StreamingLoessBuilder::default();
if let Some(chunk_size) = builder.chunk_size {
result.chunk_size = chunk_size;
}
if let Some(overlap) = builder.overlap {
result.overlap = overlap;
}
if let Some(fraction) = builder.fraction {
result.fraction = fraction;
}
if let Some(iterations) = builder.iterations {
result.iterations = iterations;
}
if let Some(wf) = builder.weight_function {
result.weight_function = wf;
}
if let Some(bp) = builder.boundary_policy {
result.boundary_policy = bp;
}
if let Some(rm) = builder.robustness_method {
result.robustness_method = rm;
}
if let Some(sm) = builder.scaling_method {
result.scaling_method = sm;
}
if let Some(zwf) = builder.zero_weight_fallback {
result.zero_weight_fallback = zwf;
}
if let Some(ms) = builder.merge_strategy {
result.merge_strategy = ms;
}
if let Some(rw) = builder.return_robustness_weights {
result.return_robustness_weights = rw;
}
if let Some(rd) = builder.return_diagnostics {
result.return_diagnostics = rd;
}
if let Some(cr) = builder.compute_residuals {
result.compute_residuals = cr;
}
if let Some(ac) = builder.auto_converge {
result.auto_converge = Some(ac);
}
if let Some(pd) = builder.polynomial_degree {
result.polynomial_degree = pd;
}
if let Some(dims) = builder.dimensions {
result.dimensions = dims;
}
if let Some(dm) = builder.distance_metric {
result.distance_metric = dm;
}
if let Some(cell) = builder.cell {
result.cell = Some(cell.to_f64().unwrap());
}
if let Some(iv) = builder.interpolation_vertices {
result.interpolation_vertices = Some(iv);
}
if let Some(sm) = builder.surface_mode {
result.surface_mode = sm;
}
if let Some(bdf) = builder.boundary_degree_fallback {
result.boundary_degree_fallback = bdf;
}
if let Some(sp) = builder.custom_smooth_pass {
result.custom_smooth_pass = Some(sp);
}
if let Some(cp) = builder.custom_cv_pass {
result.custom_cv_pass = Some(cp);
}
if let Some(ip) = builder.custom_interval_pass {
result.custom_interval_pass = Some(ip);
}
if let Some(b) = builder.backend {
result.backend = Some(b);
}
if let Some(p) = builder.parallel {
result.parallel = Some(p);
}
result.duplicate_param = builder.duplicate_param;
result
}
}
#[derive(Debug, Clone, Copy)]
pub struct Online;
impl<T: FloatLinalg + DistanceLinalg + SolverLinalg + Debug + Send + Sync> LoessAdapter<T>
for Online
{
type Output = OnlineLoessBuilder<T>;
fn convert(builder: LoessBuilder<T>) -> Self::Output {
let mut result = OnlineLoessBuilder::default();
if let Some(window_capacity) = builder.window_capacity {
result.window_capacity = window_capacity;
}
if let Some(min_points) = builder.min_points {
result.min_points = min_points;
}
if let Some(fraction) = builder.fraction {
result.fraction = fraction;
}
if let Some(iterations) = builder.iterations {
result.iterations = iterations;
}
if let Some(wf) = builder.weight_function {
result.weight_function = wf;
}
if let Some(um) = builder.update_mode {
result.update_mode = um;
}
if let Some(rm) = builder.robustness_method {
result.robustness_method = rm;
}
if let Some(sm) = builder.scaling_method {
result.scaling_method = sm;
}
if let Some(bp) = builder.boundary_policy {
result.boundary_policy = bp;
}
if let Some(zwf) = builder.zero_weight_fallback {
result.zero_weight_fallback = zwf;
}
if let Some(cr) = builder.compute_residuals {
result.compute_residuals = cr;
}
if let Some(rw) = builder.return_robustness_weights {
result.return_robustness_weights = rw;
}
if let Some(ac) = builder.auto_converge {
result.auto_converge = Some(ac);
}
if let Some(pd) = builder.polynomial_degree {
result.polynomial_degree = pd;
}
if let Some(dims) = builder.dimensions {
result.dimensions = dims;
}
if let Some(dm) = builder.distance_metric {
result.distance_metric = dm;
}
if let Some(cell) = builder.cell {
result.cell = Some(cell.to_f64().unwrap());
}
if let Some(iv) = builder.interpolation_vertices {
result.interpolation_vertices = Some(iv);
}
if let Some(sm) = builder.surface_mode {
result.surface_mode = sm;
}
if let Some(bdf) = builder.boundary_degree_fallback {
result.boundary_degree_fallback = bdf;
}
if let Some(sp) = builder.custom_smooth_pass {
result.custom_smooth_pass = Some(sp);
}
if let Some(cp) = builder.custom_cv_pass {
result.custom_cv_pass = Some(cp);
}
if let Some(ip) = builder.custom_interval_pass {
result.custom_interval_pass = Some(ip);
}
if let Some(b) = builder.backend {
result.backend = Some(b);
}
if let Some(p) = builder.parallel {
result.parallel = Some(p);
}
result.duplicate_param = builder.duplicate_param;
result
}
}