1#![allow(clippy::too_many_arguments)]
12#![allow(dead_code)]
13
14use crate::error::{InterpolateError, InterpolateResult};
15use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
16use scirs2_core::numeric::{Float, FromPrimitive};
17use scirs2_core::random::{rngs::StdRng, Rng, RngExt, SeedableRng};
18use scirs2_core::random::{Distribution, Normal, StandardNormal};
19use statrs::statistics::Statistics;
20use std::fmt::{Debug, Display};
21
22#[derive(Debug, Clone)]
24pub struct BootstrapConfig {
25 pub n_samples: usize,
27 pub confidence_level: f64,
29 pub seed: Option<u64>,
31}
32
33impl Default for BootstrapConfig {
34 fn default() -> Self {
35 Self {
36 n_samples: 1000,
37 confidence_level: 0.95,
38 seed: None,
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct BootstrapResult<T: Float> {
46 pub estimate: Array1<T>,
48 pub lower_bound: Array1<T>,
50 pub upper_bound: Array1<T>,
52 pub std_error: Array1<T>,
54}
55
56pub struct BootstrapInterpolator<T: Float> {
61 config: BootstrapConfig,
63 interpolator_factory:
65 Box<dyn Fn(&ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Box<dyn Fn(T) -> T>>>,
66}
67
68impl<T: Float + FromPrimitive + Debug + Display + std::iter::Sum> BootstrapInterpolator<T> {
69 pub fn new<F>(config: BootstrapConfig, interpolator_factory: F) -> Self
71 where
72 F: Fn(&ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Box<dyn Fn(T) -> T>> + 'static,
73 {
74 Self {
75 config,
76 interpolator_factory: Box::new(interpolator_factory),
77 }
78 }
79
80 pub fn interpolate(
82 &self,
83 x: &ArrayView1<T>,
84 y: &ArrayView1<T>,
85 xnew: &ArrayView1<T>,
86 ) -> InterpolateResult<BootstrapResult<T>> {
87 if x.len() != y.len() {
88 return Err(InterpolateError::DimensionMismatch(
89 "x and y must have the same length".to_string(),
90 ));
91 }
92
93 let n = x.len();
94 let m = xnew.len();
95 let mut rng = match self.config.seed {
96 Some(seed) => StdRng::seed_from_u64(seed),
97 None => {
98 let mut rng = scirs2_core::random::rng();
99 StdRng::from_rng(&mut rng)
100 }
101 };
102
103 let mut bootstrap_results = Array2::<T>::zeros((self.config.n_samples, m));
105
106 for i in 0..self.config.n_samples {
108 let indices: Vec<usize> = (0..n).map(|_| rng.random_range(0..n)).collect();
110
111 let x_resampled = indices.iter().map(|&idx| x[idx]).collect::<Array1<_>>();
113 let y_resampled = indices.iter().map(|&idx| y[idx]).collect::<Array1<_>>();
114
115 let interpolator =
117 (self.interpolator_factory)(&x_resampled.view(), &y_resampled.view())?;
118
119 for (j, &x_val) in xnew.iter().enumerate() {
121 bootstrap_results[[i, j]] = interpolator(x_val);
122 }
123 }
124
125 let alpha = T::from(1.0 - self.config.confidence_level).expect("Operation failed");
127 let lower_percentile = alpha / T::from(2.0).expect("Operation failed");
128 let upper_percentile = T::one() - alpha / T::from(2.0).expect("Operation failed");
129
130 let mut estimate = Array1::zeros(m);
131 let mut lower_bound = Array1::zeros(m);
132 let mut upper_bound = Array1::zeros(m);
133 let mut std_error = Array1::zeros(m);
134
135 for j in 0..m {
136 let column = bootstrap_results.index_axis(Axis(1), j);
137 let mut sorted_col = column.to_vec();
138 sorted_col.sort_by(|a, b| a.partial_cmp(b).expect("Operation failed"));
139
140 let median_idx = self.config.n_samples / 2;
142 estimate[j] = sorted_col[median_idx];
143
144 let lower_idx = (lower_percentile
146 * T::from(self.config.n_samples).expect("Operation failed"))
147 .to_usize()
148 .expect("Operation failed");
149 let upper_idx = (upper_percentile
150 * T::from(self.config.n_samples).expect("Operation failed"))
151 .to_usize()
152 .expect("Operation failed");
153 lower_bound[j] = sorted_col[lower_idx];
154 upper_bound[j] = sorted_col[upper_idx];
155
156 let mean = column.mean().expect("Operation failed");
158 let variance = column
159 .iter()
160 .map(|&val| (val - mean) * (val - mean))
161 .sum::<T>()
162 / T::from(self.config.n_samples - 1).expect("Operation failed");
163 std_error[j] = variance.sqrt();
164 }
165
166 Ok(BootstrapResult {
167 estimate,
168 lower_bound,
169 upper_bound,
170 std_error,
171 })
172 }
173}
174
175pub struct BayesianConfig<T: Float> {
177 pub prior_mean: Box<dyn Fn(T) -> T>,
179 pub prior_variance: T,
181 pub noise_variance: T,
183 pub length_scale: T,
185 pub n_posterior_samples: usize,
187}
188
189impl<T: Float + FromPrimitive> Default for BayesianConfig<T> {
190 fn default() -> Self {
191 Self {
192 prior_mean: Box::new(|_| T::zero()),
193 prior_variance: T::one(),
194 noise_variance: T::from(0.01).expect("Operation failed"),
195 length_scale: T::one(),
196 n_posterior_samples: 100,
197 }
198 }
199}
200
201impl<T: Float + FromPrimitive> BayesianConfig<T> {
202 pub fn with_length_scale(mut self, lengthscale: T) -> Self {
204 self.length_scale = lengthscale;
205 self
206 }
207
208 pub fn with_prior_variance(mut self, variance: T) -> Self {
210 self.prior_variance = variance;
211 self
212 }
213
214 pub fn with_noise_variance(mut self, variance: T) -> Self {
216 self.noise_variance = variance;
217 self
218 }
219
220 pub fn with_n_posterior_samples(mut self, nsamples: usize) -> Self {
222 self.n_posterior_samples = nsamples;
223 self
224 }
225}
226
227pub struct BayesianInterpolator<T: Float> {
232 config: BayesianConfig<T>,
233 x_obs: Array1<T>,
234 y_obs: Array1<T>,
235}
236
237impl<
238 T: Float
239 + FromPrimitive
240 + Debug
241 + Display
242 + std::ops::AddAssign
243 + std::ops::SubAssign
244 + std::ops::MulAssign
245 + std::ops::DivAssign
246 + std::ops::RemAssign,
247 > BayesianInterpolator<T>
248{
249 pub fn new(
251 x: &ArrayView1<T>,
252 y: &ArrayView1<T>,
253 config: BayesianConfig<T>,
254 ) -> InterpolateResult<Self> {
255 if x.len() != y.len() {
256 return Err(InterpolateError::DimensionMismatch(
257 "x and y must have the same length".to_string(),
258 ));
259 }
260
261 Ok(Self {
262 config,
263 x_obs: x.to_owned(),
264 y_obs: y.to_owned(),
265 })
266 }
267
268 pub fn posterior_mean(&self, xnew: &ArrayView1<T>) -> InterpolateResult<Array1<T>> {
270 let n = self.x_obs.len();
271 let m = xnew.len();
272
273 if n == 0 {
274 return Err(InterpolateError::invalid_input(
275 "No observed data points".to_string(),
276 ));
277 }
278
279 let mut k_xx = Array2::<T>::zeros((n, n));
281 let length_scale = self.config.length_scale;
282
283 for i in 0..n {
285 for j in 0..n {
286 let dist_sq = (self.x_obs[i] - self.x_obs[j]).powi(2);
287 k_xx[[i, j]] = self.config.prior_variance
288 * (-dist_sq / (T::from(2.0).expect("Operation failed") * length_scale.powi(2)))
289 .exp();
290
291 if i == j {
293 k_xx[[i, j]] += self.config.noise_variance;
294 }
295 }
296 }
297
298 let weights = match self.solve_gp_system(&k_xx.view(), &self.y_obs.view()) {
301 Ok(w) => w,
302 Err(_) => {
303 let regularization = T::from(1e-6).expect("Operation failed");
305 for i in 0..n {
306 k_xx[[i, i]] += regularization;
307 }
308 self.solve_gp_system(&k_xx.view(), &self.y_obs.view())?
309 }
310 };
311
312 let mut k_star_x = Array2::<T>::zeros((m, n));
314 for i in 0..m {
315 for j in 0..n {
316 let dist_sq = (xnew[i] - self.x_obs[j]).powi(2);
317 k_star_x[[i, j]] = self.config.prior_variance
318 * (-dist_sq / (T::from(2.0).expect("Operation failed") * length_scale.powi(2)))
319 .exp();
320 }
321 }
322
323 let mut mean = Array1::zeros(m);
325 for i in 0..m {
326 let mut sum = T::zero();
327 for j in 0..n {
328 sum += k_star_x[[i, j]] * weights[j];
329 }
330 mean[i] = (self.config.prior_mean)(xnew[i]) + sum;
332 }
333
334 Ok(mean)
335 }
336
337 fn solve_gp_system(
339 &self,
340 k_matrix: &ArrayView2<T>,
341 y_obs: &ArrayView1<T>,
342 ) -> InterpolateResult<Array1<T>> {
343 use crate::structured_matrix::solve_dense_system;
344
345 match solve_dense_system(k_matrix, y_obs) {
347 Ok(solution) => Ok(solution),
348 Err(_) => {
349 let n = y_obs.len();
351 let weights =
352 Array1::from_elem(n, T::one() / T::from(n).expect("Operation failed"));
353 Ok(weights)
354 }
355 }
356 }
357
358 pub fn posterior_samples(
360 &self,
361 xnew: &ArrayView1<T>,
362 n_samples: usize,
363 ) -> InterpolateResult<Array2<T>> {
364 let mean = self.posterior_mean(xnew)?;
365 let m = xnew.len();
366
367 let mut samples = Array2::zeros((n_samples, m));
373 let mut rng = scirs2_core::random::rng();
374
375 let length_scale = T::one();
377 for j in 0..m {
378 let mut reduction_factor = T::zero();
380 let mut total_influence = T::zero();
381
382 for i in 0..self.x_obs.len() {
383 let dist_sq = (xnew[j] - self.x_obs[i]).powi(2);
384 let influence = (-dist_sq
385 / (T::from(2.0).expect("Operation failed") * length_scale.powi(2)))
386 .exp();
387 total_influence += influence;
388 reduction_factor += influence * influence;
389 }
390
391 let noise_ratio = self.config.noise_variance / self.config.prior_variance;
393 let posterior_var = self.config.prior_variance
394 * (T::one()
395 - reduction_factor
396 / (total_influence
397 + noise_ratio
398 + T::from(1e-8).expect("Operation failed")));
399
400 let std_dev = posterior_var
402 .max(T::from(1e-12).expect("Operation failed"))
403 .sqrt();
404
405 for i in 0..n_samples {
407 if let Ok(normal) = Normal::new(
408 mean[j].to_f64().expect("Operation failed"),
409 std_dev.to_f64().expect("Operation failed"),
410 ) {
411 samples[[i, j]] = T::from(normal.sample(&mut rng)).expect("Operation failed");
412 } else {
413 samples[[i, j]] = mean[j];
414 }
415 }
416 }
417
418 Ok(samples)
419 }
420}
421
422pub struct QuantileInterpolator<T: Float> {
426 quantile: T,
428 bandwidth: T,
430}
431
432impl<T: Float + FromPrimitive + Debug + Display> QuantileInterpolator<T>
433where
434 T: std::iter::Sum<T> + for<'a> std::iter::Sum<&'a T>,
435{
436 pub fn new(quantile: T, bandwidth: T) -> InterpolateResult<Self> {
438 if quantile <= T::zero() || quantile >= T::one() {
439 return Err(InterpolateError::InvalidValue(
440 "Quantile must be between 0 and 1".to_string(),
441 ));
442 }
443
444 Ok(Self {
445 quantile,
446 bandwidth,
447 })
448 }
449
450 pub fn interpolate(
452 &self,
453 x: &ArrayView1<T>,
454 y: &ArrayView1<T>,
455 xnew: &ArrayView1<T>,
456 ) -> InterpolateResult<Array1<T>> {
457 if x.len() != y.len() {
458 return Err(InterpolateError::DimensionMismatch(
459 "x and y must have the same length".to_string(),
460 ));
461 }
462
463 let n = x.len();
464 let m = xnew.len();
465 let mut result = Array1::zeros(m);
466
467 for j in 0..m {
469 let x_target = xnew[j];
470
471 let mut weights = Vec::with_capacity(n);
473 let mut weighted_values = Vec::with_capacity(n);
474
475 for i in 0..n {
476 let dist = (x[i] - x_target).abs() / self.bandwidth;
477 let weight = if dist < T::one() {
478 (T::one() - dist * dist * dist).powi(3) } else {
480 T::zero()
481 };
482
483 if weight > T::epsilon() {
484 weights.push(weight);
485 weighted_values.push((y[i], weight));
486 }
487 }
488
489 if weighted_values.is_empty() {
490 let nearest_idx = x
492 .iter()
493 .enumerate()
494 .min_by_key(|(_, &xi)| {
495 ((xi - x_target).abs().to_f64().expect("Operation failed") * 1e6) as i64
496 })
497 .map(|(i_, _)| i_)
498 .expect("Operation failed");
499 result[j] = y[nearest_idx];
500 } else {
501 weighted_values.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("Operation failed"));
503
504 let total_weight: T = weights.iter().sum();
506 let target_weight = self.quantile * total_weight;
507
508 let mut cumulative_weight = T::zero();
509 for (val, weight) in weighted_values {
510 cumulative_weight = cumulative_weight + weight;
511 if cumulative_weight >= target_weight {
512 result[j] = val;
513 break;
514 }
515 }
516 }
517 }
518
519 Ok(result)
520 }
521}
522
523pub struct RobustInterpolator<T: Float> {
525 tuning_constant: T,
527 max_iterations: usize,
529 tolerance: T,
531}
532
533impl<T: Float + FromPrimitive + Debug + Display> RobustInterpolator<T> {
534 pub fn new(_tuningconstant: T) -> Self {
536 Self {
537 tuning_constant: _tuningconstant,
538 max_iterations: 100,
539 tolerance: T::from(1e-6).expect("Operation failed"),
540 }
541 }
542
543 pub fn interpolate(
545 &self,
546 x: &ArrayView1<T>,
547 y: &ArrayView1<T>,
548 xnew: &ArrayView1<T>,
549 ) -> InterpolateResult<Array1<T>> {
550 let n = x.len();
552 let m = xnew.len();
553 let mut result = Array1::zeros(m);
554
555 for j in 0..m {
556 let x_target = xnew[j];
557
558 let mut weights = vec![T::one(); n];
560 let mut prev_estimate = T::zero();
561
562 for _iter in 0..self.max_iterations {
564 let mut sum_w = T::zero();
566 let mut sum_wx = T::zero();
567 let mut sum_wy = T::zero();
568 let mut sum_wxx = T::zero();
569 let mut sum_wxy = T::zero();
570
571 for i in 0..n {
572 let w = weights[i];
573 let dx = x[i] - x_target;
574 sum_w = sum_w + w;
575 sum_wx = sum_wx + w * dx;
576 sum_wy = sum_wy + w * y[i];
577 sum_wxx = sum_wxx + w * dx * dx;
578 sum_wxy = sum_wxy + w * dx * y[i];
579 }
580
581 let det = sum_w * sum_wxx - sum_wx * sum_wx;
583 let estimate = if det.abs() > T::epsilon() {
584 (sum_wxx * sum_wy - sum_wx * sum_wxy) / det
585 } else {
586 sum_wy / sum_w
587 };
588
589 if (estimate - prev_estimate).abs() < self.tolerance {
591 result[j] = estimate;
592 break;
593 }
594 prev_estimate = estimate;
595
596 for i in 0..n {
598 let residual = y[i] - estimate;
599 let scaled_residual = residual / self.tuning_constant;
600
601 weights[i] = if scaled_residual.abs() <= T::one() {
602 T::one()
603 } else {
604 T::one() / scaled_residual.abs()
605 };
606 }
607 }
608
609 result[j] = prev_estimate;
610 }
611
612 Ok(result)
613 }
614}
615
616pub struct StochasticInterpolator<T: Float> {
621 correlation_length: T,
623 field_variance: T,
625 n_realizations: usize,
627}
628
629impl<T: Float + FromPrimitive + Debug + Display> StochasticInterpolator<T> {
630 pub fn new(correlation_length: T, field_variance: T, n_realizations: usize) -> Self {
632 Self {
633 correlation_length,
634 field_variance,
635 n_realizations,
636 }
637 }
638
639 pub fn interpolate_realizations(
641 &self,
642 x: &ArrayView1<T>,
643 y: &ArrayView1<T>,
644 xnew: &ArrayView1<T>,
645 ) -> InterpolateResult<Array2<T>> {
646 let n = x.len();
647 let m = xnew.len();
648 let mut realizations = Array2::zeros((self.n_realizations, m));
649
650 let mut rng = scirs2_core::random::rng();
651
652 for r in 0..self.n_realizations {
653 for j in 0..m {
655 let x_target = xnew[j];
656
657 let mut weighted_sum = T::zero();
659 let mut weight_sum = T::zero();
660
661 for i in 0..n {
662 let dist = (x[i] - x_target).abs() / self.correlation_length;
663 let weight = (-dist * dist).exp();
664 weighted_sum = weighted_sum + weight * y[i];
665 weight_sum = weight_sum + weight;
666 }
667
668 let mean = if weight_sum > T::epsilon() {
669 weighted_sum / weight_sum
670 } else {
671 T::zero()
672 };
673
674 let std_dev = (self.field_variance
676 * (T::one() - weight_sum / T::from(n).expect("Operation failed")))
677 .sqrt();
678 let normal_sample: f64 = StandardNormal.sample(&mut rng);
679 let noise: T = T::from(normal_sample).expect("Operation failed") * std_dev;
680
681 realizations[[r, j]] = mean + noise;
682 }
683 }
684
685 Ok(realizations)
686 }
687
688 pub fn interpolate_statistics(
690 &self,
691 x: &ArrayView1<T>,
692 y: &ArrayView1<T>,
693 xnew: &ArrayView1<T>,
694 ) -> InterpolateResult<(Array1<T>, Array1<T>)> {
695 let realizations = self.interpolate_realizations(x, y, xnew)?;
696
697 let mean = realizations.mean_axis(Axis(0)).expect("Operation failed");
698 let variance = realizations.var_axis(Axis(0), T::from(1.0).expect("Operation failed"));
699
700 Ok((mean, variance))
701 }
702}
703
704#[allow(dead_code)]
707pub fn make_bootstrap_linear_interpolator<
708 T: Float + FromPrimitive + Debug + Display + 'static + std::iter::Sum,
709>(
710 config: BootstrapConfig,
711) -> BootstrapInterpolator<T> {
712 BootstrapInterpolator::new(config, |x, y| {
713 let x_owned = x.to_owned();
715 let y_owned = y.to_owned();
716 Ok(Box::new(move |xnew| {
717 if xnew <= x_owned[0] {
719 y_owned[0]
720 } else if xnew >= x_owned[x_owned.len() - 1] {
721 y_owned[y_owned.len() - 1]
722 } else {
723 let mut i = 0;
725 for j in 1..x_owned.len() {
726 if xnew <= x_owned[j] {
727 i = j - 1;
728 break;
729 }
730 }
731
732 let alpha = (xnew - x_owned[i]) / (x_owned[i + 1] - x_owned[i]);
733 y_owned[i] * (T::one() - alpha) + y_owned[i + 1] * alpha
734 }
735 }))
736 })
737}
738
739#[allow(dead_code)]
741pub fn make_bayesian_interpolator<T: crate::traits::InterpolationFloat>(
742 x: &ArrayView1<T>,
743 y: &ArrayView1<T>,
744) -> InterpolateResult<BayesianInterpolator<T>> {
745 BayesianInterpolator::new(x, y, BayesianConfig::default())
746}
747
748#[allow(dead_code)]
750pub fn make_median_interpolator<T>(bandwidth: T) -> InterpolateResult<QuantileInterpolator<T>>
751where
752 T: Float + FromPrimitive + Debug + Display + std::iter::Sum<T> + for<'a> std::iter::Sum<&'a T>,
753{
754 QuantileInterpolator::new(T::from(0.5).expect("Operation failed"), bandwidth)
755}
756
757#[allow(dead_code)]
759pub fn make_robust_interpolator<T: crate::traits::InterpolationFloat>() -> RobustInterpolator<T> {
760 RobustInterpolator::new(T::from(1.345).expect("Operation failed")) }
762
763#[allow(dead_code)]
765pub fn make_stochastic_interpolator<T: crate::traits::InterpolationFloat>(
766 correlation_length: T,
767) -> StochasticInterpolator<T> {
768 StochasticInterpolator::new(correlation_length, T::one(), 100)
769}
770
771pub struct EnsembleInterpolator<T: Float> {
776 weights: Array1<T>,
778 methods: Vec<
780 Box<dyn Fn(&ArrayView1<T>, &ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Array1<T>>>,
781 >,
782 normalize_weights: bool,
784}
785
786impl<T: crate::traits::InterpolationFloat> EnsembleInterpolator<T> {
787 pub fn new() -> Self {
789 Self {
790 weights: Array1::zeros(0),
791 methods: Vec::new(),
792 normalize_weights: true,
793 }
794 }
795
796 pub fn add_linear_method(mut self, weight: T) -> Self {
798 self.weights = if self.weights.is_empty() {
799 Array1::from_vec(vec![weight])
800 } else {
801 let mut new_weights = self.weights.to_vec();
802 new_weights.push(weight);
803 Array1::from_vec(new_weights)
804 };
805
806 self.methods.push(Box::new(|x, y, xnew| {
807 let mut result = Array1::zeros(xnew.len());
808 for (i, &x_val) in xnew.iter().enumerate() {
809 if x_val <= x[0] {
811 result[i] = y[0];
812 } else if x_val >= x[x.len() - 1] {
813 result[i] = y[y.len() - 1];
814 } else {
815 for j in 1..x.len() {
817 if x_val <= x[j] {
818 let alpha = (x_val - x[j - 1]) / (x[j] - x[j - 1]);
819 result[i] = y[j - 1] * (T::one() - alpha) + y[j] * alpha;
820 break;
821 }
822 }
823 }
824 }
825 Ok(result)
826 }));
827 self
828 }
829
830 pub fn add_cubic_method(mut self, weight: T) -> Self {
832 self.weights = if self.weights.is_empty() {
833 Array1::from_vec(vec![weight])
834 } else {
835 let mut new_weights = self.weights.to_vec();
836 new_weights.push(weight);
837 Array1::from_vec(new_weights)
838 };
839
840 self.methods.push(Box::new(|x, y, xnew| {
841 use crate::spline::CubicSpline;
843
844 if x.len() < 3 {
846 return Err(InterpolateError::invalid_input(
847 "Cubic spline requires at least 3 data points".to_string(),
848 ));
849 }
850
851 let spline = CubicSpline::new(x, y)?;
853
854 let mut result = Array1::zeros(xnew.len());
856 for (i, &x_val) in xnew.iter().enumerate() {
857 if x_val < x[0] {
859 result[i] = y[0];
860 } else if x_val > x[x.len() - 1] {
861 result[i] = y[y.len() - 1];
862 } else {
863 result[i] = spline.evaluate(x_val)?;
865 }
866 }
867 Ok(result)
868 }));
869 self
870 }
871
872 pub fn interpolate(
874 &self,
875 x: &ArrayView1<T>,
876 y: &ArrayView1<T>,
877 xnew: &ArrayView1<T>,
878 ) -> InterpolateResult<Array1<T>> {
879 if self.methods.is_empty() {
880 return Err(InterpolateError::InvalidState(
881 "No interpolation methods in ensemble".to_string(),
882 ));
883 }
884
885 let mut weighted_results = Array1::zeros(xnew.len());
886 let mut total_weight = T::zero();
887
888 for (i, method) in self.methods.iter().enumerate() {
889 let result = method(x, y, xnew)?;
890 let weight = self.weights[i];
891
892 for j in 0..xnew.len() {
893 weighted_results[j] += weight * result[j];
894 }
895 total_weight += weight;
896 }
897
898 if self.normalize_weights && total_weight > T::zero() {
900 for val in weighted_results.iter_mut() {
901 *val /= total_weight;
902 }
903 }
904
905 Ok(weighted_results)
906 }
907
908 pub fn interpolate_with_variance(
910 &self,
911 x: &ArrayView1<T>,
912 y: &ArrayView1<T>,
913 xnew: &ArrayView1<T>,
914 ) -> InterpolateResult<(Array1<T>, Array1<T>)> {
915 if self.methods.is_empty() {
916 return Err(InterpolateError::InvalidState(
917 "No interpolation methods in ensemble".to_string(),
918 ));
919 }
920
921 let mut all_results = Vec::new();
922
923 for method in self.methods.iter() {
925 let result = method(x, y, xnew)?;
926 all_results.push(result);
927 }
928
929 let mut weighted_mean = Array1::zeros(xnew.len());
931 let mut total_weight = T::zero();
932
933 for (i, result) in all_results.iter().enumerate() {
934 let weight = self.weights[i];
935 for j in 0..xnew.len() {
936 weighted_mean[j] += weight * result[j];
937 }
938 total_weight += weight;
939 }
940
941 if total_weight > T::zero() {
942 for val in weighted_mean.iter_mut() {
943 *val /= total_weight;
944 }
945 }
946
947 let mut variance = Array1::zeros(xnew.len());
949 if all_results.len() > 1 {
950 for (i, result) in all_results.iter().enumerate() {
951 let weight = self.weights[i];
952 for j in 0..xnew.len() {
953 let diff = result[j] - weighted_mean[j];
954 variance[j] += weight * diff * diff;
955 }
956 }
957
958 if total_weight > T::zero() {
959 for val in variance.iter_mut() {
960 *val /= total_weight;
961 }
962 }
963 }
964
965 Ok((weighted_mean, variance))
966 }
967}
968
969impl<T: crate::traits::InterpolationFloat> Default for EnsembleInterpolator<T> {
970 fn default() -> Self {
971 Self::new()
972 }
973}
974
975pub struct CrossValidationUncertainty {
980 k_folds: usize,
982 seed: Option<u64>,
984}
985
986impl CrossValidationUncertainty {
987 pub fn new(_kfolds: usize) -> Self {
989 Self {
990 k_folds: _kfolds,
991 seed: None,
992 }
993 }
994
995 pub fn with_seed(mut self, seed: u64) -> Self {
997 self.seed = Some(seed);
998 self
999 }
1000
1001 pub fn estimate_uncertainty<T, F>(
1003 &self,
1004 x: &ArrayView1<T>,
1005 y: &ArrayView1<T>,
1006 xnew: &ArrayView1<T>,
1007 interpolator_factory: F,
1008 ) -> InterpolateResult<(Array1<T>, Array1<T>)>
1009 where
1010 T: Clone
1011 + Copy
1012 + scirs2_core::numeric::Float
1013 + scirs2_core::numeric::FromPrimitive
1014 + std::iter::Sum,
1015 F: Fn(&ArrayView1<T>, &ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Array1<T>>,
1016 {
1017 let n = x.len();
1018 let _m = xnew.len();
1019
1020 if self.k_folds == 0 || self.k_folds >= n {
1021 self.leave_one_out_uncertainty(x, y, xnew, interpolator_factory)
1023 } else {
1024 self.k_fold_uncertainty(x, y, xnew, interpolator_factory)
1026 }
1027 }
1028
1029 fn leave_one_out_uncertainty<T, F>(
1030 &self,
1031 x: &ArrayView1<T>,
1032 y: &ArrayView1<T>,
1033 xnew: &ArrayView1<T>,
1034 interpolator_factory: F,
1035 ) -> InterpolateResult<(Array1<T>, Array1<T>)>
1036 where
1037 T: Clone
1038 + Copy
1039 + scirs2_core::numeric::Float
1040 + scirs2_core::numeric::FromPrimitive
1041 + std::iter::Sum,
1042 F: Fn(&ArrayView1<T>, &ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Array1<T>>,
1043 {
1044 let n = x.len();
1045 let m = xnew.len();
1046 let mut predictions = Array2::zeros((n, m));
1047
1048 for i in 0..n {
1050 let mut x_train = Vec::new();
1052 let mut y_train = Vec::new();
1053
1054 for j in 0..n {
1055 if j != i {
1056 x_train.push(x[j]);
1057 y_train.push(y[j]);
1058 }
1059 }
1060
1061 let x_train_array = Array1::from_vec(x_train);
1062 let y_train_array = Array1::from_vec(y_train);
1063
1064 let pred = interpolator_factory(&x_train_array.view(), &y_train_array.view(), xnew)?;
1066 for j in 0..m {
1067 predictions[[i, j]] = pred[j];
1068 }
1069 }
1070
1071 let mut mean = Array1::zeros(m);
1073 let mut variance = Array1::zeros(m);
1074
1075 for j in 0..m {
1076 let col = predictions.column(j);
1077 let sum: T = col.iter().copied().sum();
1078 mean[j] = sum / T::from(n).expect("Operation failed");
1079
1080 let var_sum: T = col
1081 .iter()
1082 .map(|&val| (val - mean[j]) * (val - mean[j]))
1083 .sum();
1084 variance[j] = var_sum / T::from(n - 1).expect("Operation failed");
1085 }
1086
1087 Ok((mean, variance))
1088 }
1089
1090 fn k_fold_uncertainty<T, F>(
1091 &self,
1092 x: &ArrayView1<T>,
1093 y: &ArrayView1<T>,
1094 xnew: &ArrayView1<T>,
1095 interpolator_factory: F,
1096 ) -> InterpolateResult<(Array1<T>, Array1<T>)>
1097 where
1098 T: Clone
1099 + Copy
1100 + scirs2_core::numeric::Float
1101 + scirs2_core::numeric::FromPrimitive
1102 + std::iter::Sum,
1103 F: Fn(&ArrayView1<T>, &ArrayView1<T>, &ArrayView1<T>) -> InterpolateResult<Array1<T>>,
1104 {
1105 let n = x.len();
1106 let m = xnew.len();
1107 let fold_size = n / self.k_folds;
1108 let mut predictions = Vec::new();
1109
1110 let mut rng = match self.seed {
1111 Some(seed) => StdRng::seed_from_u64(seed),
1112 None => {
1113 let mut rng = scirs2_core::random::rng();
1114 StdRng::from_rng(&mut rng)
1115 }
1116 };
1117
1118 let mut indices: Vec<usize> = (0..n).collect();
1120 use scirs2_core::random::seq::SliceRandom;
1121 indices.shuffle(&mut rng);
1122
1123 for fold in 0..self.k_folds {
1125 let start_idx = fold * fold_size;
1126 let end_idx = if fold == self.k_folds - 1 {
1127 n
1128 } else {
1129 (fold + 1) * fold_size
1130 };
1131
1132 let mut x_train = Vec::new();
1134 let mut y_train = Vec::new();
1135
1136 for &idx in &indices[..start_idx] {
1137 x_train.push(x[idx]);
1138 y_train.push(y[idx]);
1139 }
1140 for &idx in &indices[end_idx..] {
1141 x_train.push(x[idx]);
1142 y_train.push(y[idx]);
1143 }
1144
1145 let x_train_array = Array1::from_vec(x_train);
1146 let y_train_array = Array1::from_vec(y_train);
1147
1148 let pred = interpolator_factory(&x_train_array.view(), &y_train_array.view(), xnew)?;
1150 predictions.push(pred);
1151 }
1152
1153 let mut mean = Array1::zeros(m);
1155 let mut variance = Array1::zeros(m);
1156
1157 for j in 0..m {
1158 let values: Vec<T> = predictions.iter().map(|pred| pred[j]).collect();
1159 let sum: T = values.iter().copied().sum();
1160 mean[j] = sum / T::from(self.k_folds).expect("Operation failed");
1161
1162 let var_sum: T = values
1163 .iter()
1164 .map(|&val| (val - mean[j]) * (val - mean[j]))
1165 .sum();
1166 variance[j] = var_sum / T::from(self.k_folds - 1).expect("Operation failed");
1167 }
1168
1169 Ok((mean, variance))
1170 }
1171}
1172
1173#[allow(dead_code)]
1175pub fn make_ensemble_interpolator<
1176 T: Float
1177 + FromPrimitive
1178 + Debug
1179 + Display
1180 + Copy
1181 + std::iter::Sum
1182 + crate::traits::InterpolationFloat,
1183>() -> EnsembleInterpolator<T> {
1184 EnsembleInterpolator::new()
1185 .add_linear_method(T::from(0.6).expect("Operation failed"))
1186 .add_cubic_method(T::from(0.4).expect("Operation failed"))
1187}
1188
1189#[allow(dead_code)]
1191pub fn make_loocv_uncertainty() -> CrossValidationUncertainty {
1192 CrossValidationUncertainty::new(0) }
1194
1195#[allow(dead_code)]
1197pub fn make_kfold_uncertainty(k: usize) -> CrossValidationUncertainty {
1198 CrossValidationUncertainty::new(k)
1199}
1200
1201#[derive(Debug, Clone)]
1207pub struct IsotonicInterpolator<T: Float> {
1208 fitted_values: Array1<T>,
1210 x_data: Array1<T>,
1212 increasing: bool,
1214}
1215
1216impl<T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum> IsotonicInterpolator<T> {
1217 pub fn new(x: &ArrayView1<T>, y: &ArrayView1<T>, increasing: bool) -> InterpolateResult<Self> {
1219 if x.len() != y.len() {
1220 return Err(InterpolateError::DimensionMismatch(
1221 "x and y must have the same length".to_string(),
1222 ));
1223 }
1224
1225 if x.len() < 2 {
1226 return Err(InterpolateError::invalid_input(
1227 "Need at least 2 points for isotonic regression".to_string(),
1228 ));
1229 }
1230
1231 let mut indices: Vec<usize> = (0..x.len()).collect();
1233 indices.sort_by(|&i, &j| x[i].partial_cmp(&x[j]).expect("Operation failed"));
1234
1235 let x_sorted: Array1<T> = indices.iter().map(|&i| x[i]).collect();
1236 let y_sorted: Array1<T> = indices.iter().map(|&i| y[i]).collect();
1237
1238 let fitted_values = Self::pool_adjacent_violators(&y_sorted.view(), increasing)?;
1240
1241 Ok(Self {
1242 fitted_values,
1243 x_data: x_sorted,
1244 increasing,
1245 })
1246 }
1247
1248 fn pool_adjacent_violators(
1250 y: &ArrayView1<T>,
1251 increasing: bool,
1252 ) -> InterpolateResult<Array1<T>> {
1253 let n = y.len();
1254 let mut fitted = y.to_owned();
1255 let mut weights = Array1::<T>::ones(n);
1256
1257 loop {
1258 let mut changed = false;
1259
1260 for i in 0..n - 1 {
1261 let violates = if increasing {
1262 fitted[i] > fitted[i + 1]
1263 } else {
1264 fitted[i] < fitted[i + 1]
1265 };
1266
1267 if violates {
1268 let total_weight = weights[i] + weights[i + 1];
1270 let weighted_mean =
1271 (fitted[i] * weights[i] + fitted[i + 1] * weights[i + 1]) / total_weight;
1272
1273 fitted[i] = weighted_mean;
1274 fitted[i + 1] = weighted_mean;
1275 weights[i] = total_weight;
1276 weights[i + 1] = total_weight;
1277
1278 changed = true;
1279 }
1280 }
1281
1282 if !changed {
1283 break;
1284 }
1285 }
1286
1287 Ok(fitted)
1288 }
1289
1290 pub fn interpolate(&self, xnew: &ArrayView1<T>) -> InterpolateResult<Array1<T>> {
1292 let mut result = Array1::zeros(xnew.len());
1293
1294 for (i, &x) in xnew.iter().enumerate() {
1295 let idx = match self
1297 .x_data
1298 .as_slice()
1299 .expect("Operation failed")
1300 .binary_search_by(|&probe| probe.partial_cmp(&x).expect("Operation failed"))
1301 {
1302 Ok(exact_idx) => {
1303 result[i] = self.fitted_values[exact_idx];
1304 continue;
1305 }
1306 Err(insert_idx) => insert_idx,
1307 };
1308
1309 if idx == 0 {
1311 result[i] = self.fitted_values[0];
1312 } else if idx >= self.x_data.len() {
1313 result[i] = self.fitted_values[self.x_data.len() - 1];
1314 } else {
1315 let x0 = self.x_data[idx - 1];
1316 let x1 = self.x_data[idx];
1317 let y0 = self.fitted_values[idx - 1];
1318 let y1 = self.fitted_values[idx];
1319
1320 let t = (x - x0) / (x1 - x0);
1321 result[i] = y0 + t * (y1 - y0);
1322 }
1323 }
1324
1325 Ok(result)
1326 }
1327}
1328
1329#[derive(Debug, Clone)]
1334pub struct KDEInterpolator<T: Float> {
1335 x_data: Array1<T>,
1337 y_data: Array1<T>,
1338 bandwidth: T,
1340 kernel_type: KDEKernel,
1342}
1343
1344#[derive(Debug, Clone, Copy, PartialEq)]
1346pub enum KDEKernel {
1347 Gaussian,
1349 Epanechnikov,
1351 Triangular,
1353 Uniform,
1355}
1356
1357impl<T: Float + FromPrimitive + Debug + Display + Copy> KDEInterpolator<T> {
1358 pub fn new(
1360 x: &ArrayView1<T>,
1361 y: &ArrayView1<T>,
1362 bandwidth: T,
1363 kernel_type: KDEKernel,
1364 ) -> InterpolateResult<Self> {
1365 if x.len() != y.len() {
1366 return Err(InterpolateError::DimensionMismatch(
1367 "x and y must have the same length".to_string(),
1368 ));
1369 }
1370
1371 if bandwidth <= T::zero() {
1372 return Err(InterpolateError::invalid_input(
1373 "Bandwidth must be positive".to_string(),
1374 ));
1375 }
1376
1377 Ok(Self {
1378 x_data: x.to_owned(),
1379 y_data: y.to_owned(),
1380 bandwidth,
1381 kernel_type,
1382 })
1383 }
1384
1385 fn kernel(&self, u: T) -> T {
1387 match self.kernel_type {
1388 KDEKernel::Gaussian => {
1389 let pi = T::from(std::f64::consts::PI).expect("Operation failed");
1390 let two = T::from(2.0).expect("Operation failed");
1391 let exp_arg = -u * u / two;
1392 exp_arg.exp() / (two * pi).sqrt()
1393 }
1394 KDEKernel::Epanechnikov => {
1395 if u.abs() <= T::one() {
1396 let three_fourths = T::from(0.75).expect("Operation failed");
1397 three_fourths * (T::one() - u * u)
1398 } else {
1399 T::zero()
1400 }
1401 }
1402 KDEKernel::Triangular => {
1403 if u.abs() <= T::one() {
1404 T::one() - u.abs()
1405 } else {
1406 T::zero()
1407 }
1408 }
1409 KDEKernel::Uniform => {
1410 if u.abs() <= T::one() {
1411 T::from(0.5).expect("Operation failed")
1412 } else {
1413 T::zero()
1414 }
1415 }
1416 }
1417 }
1418
1419 pub fn interpolate(&self, xnew: &ArrayView1<T>) -> InterpolateResult<Array1<T>> {
1421 let mut result = Array1::zeros(xnew.len());
1422
1423 for (i, &x) in xnew.iter().enumerate() {
1424 let mut weighted_sum = T::zero();
1425 let mut weight_sum = T::zero();
1426
1427 for j in 0..self.x_data.len() {
1428 let u = (x - self.x_data[j]) / self.bandwidth;
1429 let kernel_weight = self.kernel(u);
1430
1431 weighted_sum = weighted_sum + kernel_weight * self.y_data[j];
1432 weight_sum = weight_sum + kernel_weight;
1433 }
1434
1435 if weight_sum > T::zero() {
1436 result[i] = weighted_sum / weight_sum;
1437 } else {
1438 let mut min_dist = T::infinity();
1440 let mut nearest_y = self.y_data[0];
1441
1442 for j in 0..self.x_data.len() {
1443 let dist = (x - self.x_data[j]).abs();
1444 if dist < min_dist {
1445 min_dist = dist;
1446 nearest_y = self.y_data[j];
1447 }
1448 }
1449
1450 result[i] = nearest_y;
1451 }
1452 }
1453
1454 Ok(result)
1455 }
1456}
1457
1458#[derive(Debug, Clone)]
1464pub struct EmpiricalBayesInterpolator<T: Float> {
1465 x_data: Array1<T>,
1467 y_data: Array1<T>,
1468 shrinkage_factor: T,
1470 prior_mean: T,
1472 noise_variance: T,
1474}
1475
1476impl<T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum>
1477 EmpiricalBayesInterpolator<T>
1478{
1479 pub fn new(x: &ArrayView1<T>, y: &ArrayView1<T>) -> InterpolateResult<Self> {
1481 if x.len() != y.len() {
1482 return Err(InterpolateError::DimensionMismatch(
1483 "x and y must have the same length".to_string(),
1484 ));
1485 }
1486
1487 if x.len() < 3 {
1488 return Err(InterpolateError::invalid_input(
1489 "Need at least 3 points for empirical Bayes".to_string(),
1490 ));
1491 }
1492
1493 let prior_mean = y.iter().copied().sum::<T>() / T::from(y.len()).expect("Operation failed");
1495
1496 let residuals: Array1<T> = y.iter().map(|&yi| yi - prior_mean).collect();
1498 let noise_variance = residuals.iter().map(|&r| r * r).sum::<T>()
1499 / T::from(residuals.len() - 1).expect("Operation failed");
1500
1501 let signal_variance = noise_variance.max(T::from(1e-10).expect("Operation failed"));
1503 let shrinkage_factor = noise_variance / (noise_variance + signal_variance);
1504
1505 Ok(Self {
1506 x_data: x.to_owned(),
1507 y_data: y.to_owned(),
1508 shrinkage_factor,
1509 prior_mean,
1510 noise_variance,
1511 })
1512 }
1513
1514 pub fn with_prior(
1516 x: &ArrayView1<T>,
1517 y: &ArrayView1<T>,
1518 prior_mean: T,
1519 shrinkage_factor: T,
1520 ) -> InterpolateResult<Self> {
1521 if x.len() != y.len() {
1522 return Err(InterpolateError::DimensionMismatch(
1523 "x and y must have the same length".to_string(),
1524 ));
1525 }
1526
1527 let residuals: Array1<T> = y.iter().map(|&yi| yi - prior_mean).collect();
1528 let noise_variance = residuals.iter().map(|&r| r * r).sum::<T>()
1529 / T::from(residuals.len().max(1)).expect("Operation failed");
1530
1531 Ok(Self {
1532 x_data: x.to_owned(),
1533 y_data: y.to_owned(),
1534 shrinkage_factor,
1535 prior_mean,
1536 noise_variance,
1537 })
1538 }
1539
1540 pub fn interpolate(&self, xnew: &ArrayView1<T>) -> InterpolateResult<Array1<T>> {
1542 let mut result = Array1::zeros(xnew.len());
1543
1544 for (i, &x) in xnew.iter().enumerate() {
1545 let mut distances: Vec<(T, usize)> = self
1547 .x_data
1548 .iter()
1549 .enumerate()
1550 .map(|(j, &xi)| ((x - xi).abs(), j))
1551 .collect();
1552 distances.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("Operation failed"));
1553
1554 let k = (3_usize).min(self.x_data.len() / 2).max(1);
1556 let mut local_mean = T::zero();
1557 let mut total_weight = T::zero();
1558
1559 for &(dist, j) in distances.iter().take(k) {
1560 let weight = if dist == T::zero() {
1561 T::one()
1562 } else {
1563 T::one() / (T::one() + dist)
1564 };
1565 local_mean = local_mean + weight * self.y_data[j];
1566 total_weight = total_weight + weight;
1567 }
1568
1569 if total_weight > T::zero() {
1570 local_mean = local_mean / total_weight;
1571 } else {
1572 local_mean = self.prior_mean;
1573 }
1574
1575 let shrunk_estimate = (T::one() - self.shrinkage_factor) * local_mean
1577 + self.shrinkage_factor * self.prior_mean;
1578
1579 result[i] = shrunk_estimate;
1580 }
1581
1582 Ok(result)
1583 }
1584
1585 pub fn get_shrinkage_factor(&self) -> T {
1587 self.shrinkage_factor
1588 }
1589
1590 pub fn get_prior_mean(&self) -> T {
1592 self.prior_mean
1593 }
1594
1595 pub fn get_noise_variance(&self) -> T {
1597 self.noise_variance
1598 }
1599}
1600
1601#[allow(dead_code)]
1603pub fn make_isotonic_interpolator<
1604 T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum,
1605>(
1606 x: &ArrayView1<T>,
1607 y: &ArrayView1<T>,
1608) -> InterpolateResult<IsotonicInterpolator<T>> {
1609 IsotonicInterpolator::new(x, y, true)
1610}
1611
1612#[allow(dead_code)]
1614pub fn make_decreasing_isotonic_interpolator<
1615 T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum,
1616>(
1617 x: &ArrayView1<T>,
1618 y: &ArrayView1<T>,
1619) -> InterpolateResult<IsotonicInterpolator<T>> {
1620 IsotonicInterpolator::new(x, y, false)
1621}
1622
1623#[allow(dead_code)]
1625pub fn make_kde_interpolator<T: crate::traits::InterpolationFloat + Copy>(
1626 x: &ArrayView1<T>,
1627 y: &ArrayView1<T>,
1628 bandwidth: T,
1629) -> InterpolateResult<KDEInterpolator<T>> {
1630 KDEInterpolator::new(x, y, bandwidth, KDEKernel::Gaussian)
1631}
1632
1633#[allow(dead_code)]
1635pub fn make_auto_kde_interpolator<
1636 T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum,
1637>(
1638 x: &ArrayView1<T>,
1639 y: &ArrayView1<T>,
1640) -> InterpolateResult<KDEInterpolator<T>> {
1641 let n = T::from(x.len()).expect("Operation failed");
1643 let x_std = {
1644 let mean = x.iter().copied().sum::<T>() / n;
1645 let variance = x.iter().map(|&xi| (xi - mean) * (xi - mean)).sum::<T>() / (n - T::one());
1646 variance.sqrt()
1647 };
1648
1649 let bandwidth = x_std * n.powf(-T::from(0.2).expect("Operation failed")); KDEInterpolator::new(x, y, bandwidth, KDEKernel::Gaussian)
1651}
1652
1653#[allow(dead_code)]
1655pub fn make_empirical_bayes_interpolator<
1656 T: Float + FromPrimitive + Debug + Display + Copy + std::iter::Sum,
1657>(
1658 x: &ArrayView1<T>,
1659 y: &ArrayView1<T>,
1660) -> InterpolateResult<EmpiricalBayesInterpolator<T>> {
1661 EmpiricalBayesInterpolator::new(x, y)
1662}