1use scirs2_core::ndarray::{Array1, Array2, Axis};
25use scirs2_core::random::essentials::{Normal as RandNormal, Uniform as RandUniform};
26use scirs2_core::random::rngs::StdRng as RealStdRng;
27use scirs2_core::random::seq::SliceRandom;
28use scirs2_core::random::RngExt;
29use scirs2_core::random::{thread_rng, SeedableRng};
30use scirs2_core::StandardNormal;
31use scirs2_linalg::compat::{Eig, Inverse, SVD};
32use sklears_core::error::{Result, SklearsError};
33use sklears_core::traits::{Fit, Transform};
34use std::f64::consts::PI;
35
36#[derive(Debug, Clone)]
38pub struct AnisotropicRBFSampler {
40 n_components: usize,
42 length_scales: Vec<f64>,
44 signal_variance: f64,
46 learn_length_scales: bool,
48 max_iter: usize,
50 tol: f64,
52 random_state: Option<u64>,
54}
55
56#[derive(Debug, Clone)]
58pub struct FittedAnisotropicRBF {
60 random_weights: Array2<f64>,
62 random_biases: Array1<f64>,
64 length_scales: Array1<f64>,
66 signal_variance: f64,
68 n_features: usize,
70}
71
72impl AnisotropicRBFSampler {
73 pub fn new(n_components: usize) -> Self {
75 Self {
76 n_components,
77 length_scales: vec![1.0],
78 signal_variance: 1.0,
79 learn_length_scales: true,
80 max_iter: 100,
81 tol: 1e-6,
82 random_state: None,
83 }
84 }
85
86 pub fn length_scales(mut self, length_scales: Vec<f64>) -> Self {
88 self.length_scales = length_scales;
89 self.learn_length_scales = false;
90 self
91 }
92
93 pub fn signal_variance(mut self, signal_variance: f64) -> Self {
95 self.signal_variance = signal_variance;
96 self
97 }
98
99 pub fn learn_length_scales(mut self, learn: bool) -> Self {
101 self.learn_length_scales = learn;
102 self
103 }
104
105 pub fn optimization_params(mut self, max_iter: usize, tol: f64) -> Self {
107 self.max_iter = max_iter;
108 self.tol = tol;
109 self
110 }
111
112 pub fn random_state(mut self, seed: u64) -> Self {
114 self.random_state = Some(seed);
115 self
116 }
117
118 fn learn_length_scales_ml(&self, x: &Array2<f64>) -> Result<Array1<f64>> {
120 let n_features = x.ncols();
121 let mut length_scales = Array1::ones(n_features);
122
123 for j in 0..n_features {
125 let col = x.column(j);
126 let mean = col.mean().unwrap_or(0.0);
127 let var = col.mapv(|v| (v - mean).powi(2)).mean().unwrap_or(1.0);
128 length_scales[j] = var.sqrt();
129 }
130
131 for _iter in 0..self.max_iter {
133 let mut improved = false;
134
135 for j in 0..n_features {
136 let old_scale = length_scales[j];
137 let mut best_scale = old_scale;
138 let mut best_ll = self.compute_log_likelihood(x, &length_scales)?;
139
140 for factor in [0.5, 0.8, 1.2, 2.0] {
142 length_scales[j] = old_scale * factor;
143 let ll = self.compute_log_likelihood(x, &length_scales)?;
144 if ll > best_ll {
145 best_ll = ll;
146 best_scale = length_scales[j];
147 improved = true;
148 }
149 }
150
151 length_scales[j] = best_scale;
152 }
153
154 if !improved {
155 break;
156 }
157 }
158
159 Ok(length_scales)
160 }
161
162 fn compute_log_likelihood(&self, x: &Array2<f64>, length_scales: &Array1<f64>) -> Result<f64> {
164 let n = x.nrows();
165 let mut k = Array2::zeros((n, n));
166
167 for i in 0..n {
169 for j in 0..n {
170 let mut dist_sq = 0.0;
171 for d in 0..x.ncols() {
172 let diff = x[(i, d)] - x[(j, d)];
173 dist_sq += diff * diff / (length_scales[d] * length_scales[d]);
174 }
175 k[(i, j)] = self.signal_variance * (-0.5 * dist_sq).exp();
176 }
177 }
178
179 for i in 0..n {
181 k[(i, i)] += 1e-6;
182 }
183
184 let (u, s, vt) = k.svd(true).map_err(|e| {
187 SklearsError::NumericalError(format!("SVD decomposition failed: {:?}", e))
188 })?;
189 let s_inv = s.mapv(|x| if x > 1e-10 { 1.0 / x.sqrt() } else { 0.0 });
190 let s_inv_diag = Array2::from_diag(&s_inv);
191 let _k_inv_sqrt = u.dot(&s_inv_diag).dot(&vt);
192 let log_det = s.mapv(|x| if x > 1e-10 { x.ln() } else { -23.0 }).sum(); let log_likelihood = -0.5 * (log_det + n as f64 * (2.0 * PI).ln());
194
195 Ok(log_likelihood)
196 }
197}
198
199impl Fit<Array2<f64>, ()> for AnisotropicRBFSampler {
200 type Fitted = FittedAnisotropicRBF;
201 fn fit(self, x: &Array2<f64>, _y: &()) -> Result<Self::Fitted> {
202 let n_features = x.ncols();
203
204 let length_scales = if self.learn_length_scales {
206 self.learn_length_scales_ml(x)?
207 } else if self.length_scales.len() == 1 {
208 Array1::from_elem(n_features, self.length_scales[0])
209 } else if self.length_scales.len() == n_features {
210 Array1::from_vec(self.length_scales.clone())
211 } else {
212 return Err(SklearsError::InvalidInput(
213 "Length scales must be either scalar or match number of features".to_string(),
214 ));
215 };
216
217 let mut rng = if let Some(seed) = self.random_state {
219 RealStdRng::seed_from_u64(seed)
220 } else {
221 RealStdRng::from_seed(thread_rng().random())
222 };
223
224 let mut random_weights = Array2::zeros((self.n_components, n_features));
225 for j in 0..n_features {
226 let normal =
227 RandNormal::new(0.0, 1.0 / length_scales[j]).expect("operation should succeed");
228 for i in 0..self.n_components {
229 random_weights[(i, j)] = rng.sample(normal);
230 }
231 }
232
233 let uniform = RandUniform::new(0.0, 2.0 * PI).expect("operation should succeed");
235 let random_biases = Array1::from_shape_fn(self.n_components, |_| rng.sample(uniform));
236
237 Ok(FittedAnisotropicRBF {
238 random_weights,
239 random_biases,
240 length_scales,
241 signal_variance: self.signal_variance,
242 n_features,
243 })
244 }
245}
246
247impl Transform<Array2<f64>, Array2<f64>> for FittedAnisotropicRBF {
248 fn transform(&self, x: &Array2<f64>) -> Result<Array2<f64>> {
249 if x.ncols() != self.n_features {
250 return Err(SklearsError::InvalidInput(
251 "Feature dimension mismatch".to_string(),
252 ));
253 }
254
255 let n_samples = x.nrows();
256 let n_components = self.random_weights.nrows();
257 let normalization = (2.0 * self.signal_variance / n_components as f64).sqrt();
258
259 let mut features = Array2::zeros((n_samples, n_components));
260
261 for i in 0..n_samples {
262 for j in 0..n_components {
263 let mut dot_product = 0.0;
264 for k in 0..self.n_features {
265 dot_product += x[(i, k)] * self.random_weights[(j, k)];
266 }
267 features[(i, j)] = normalization * (dot_product + self.random_biases[j]).cos();
268 }
269 }
270
271 Ok(features)
272 }
273}
274
275#[derive(Debug, Clone)]
277pub struct MahalanobisRBFSampler {
279 n_components: usize,
281 signal_variance: f64,
283 reg_param: f64,
285 random_state: Option<u64>,
287}
288
289#[derive(Debug, Clone)]
291pub struct FittedMahalanobisRBF {
293 random_weights: Array2<f64>,
295 random_biases: Array1<f64>,
297 whitening_matrix: Array2<f64>,
299 mean: Array1<f64>,
301 signal_variance: f64,
303 n_features: usize,
305}
306
307impl MahalanobisRBFSampler {
308 pub fn new(n_components: usize) -> Self {
310 Self {
311 n_components,
312 signal_variance: 1.0,
313 reg_param: 1e-6,
314 random_state: None,
315 }
316 }
317
318 pub fn signal_variance(mut self, signal_variance: f64) -> Self {
320 self.signal_variance = signal_variance;
321 self
322 }
323
324 pub fn reg_param(mut self, reg_param: f64) -> Self {
326 self.reg_param = reg_param;
327 self
328 }
329
330 pub fn random_state(mut self, seed: u64) -> Self {
332 self.random_state = Some(seed);
333 self
334 }
335}
336
337impl Fit<Array2<f64>, ()> for MahalanobisRBFSampler {
338 type Fitted = FittedMahalanobisRBF;
339 fn fit(self, x: &Array2<f64>, _y: &()) -> Result<Self::Fitted> {
340 let n_features = x.ncols();
341 let n_samples = x.nrows();
342
343 let mean = x.mean_axis(Axis(0)).expect("operation should succeed");
345
346 let x_centered = x - &mean.clone().insert_axis(Axis(0));
348
349 let cov = x_centered.t().dot(&x_centered) / (n_samples - 1) as f64;
351
352 let mut cov_reg = cov;
354 for i in 0..n_features {
355 cov_reg[(i, i)] += self.reg_param;
356 }
357
358 let (eigenvals_complex, eigenvecs_complex) = cov_reg.eig().map_err(|e| {
360 SklearsError::NumericalError(format!("Eigendecomposition failed: {:?}", e))
361 })?;
362
363 let eigenvals = eigenvals_complex.mapv(|x| x.re);
365 let eigenvecs = eigenvecs_complex.mapv(|x| x.re);
366
367 let mut inv_sqrt_eigenvals = Array1::zeros(n_features);
369 for i in 0..n_features {
370 inv_sqrt_eigenvals[i] = 1.0 / eigenvals[i].sqrt();
371 }
372
373 let whitening_matrix = eigenvecs
374 .dot(&Array2::from_diag(&inv_sqrt_eigenvals))
375 .dot(&eigenvecs.t());
376
377 let mut rng = if let Some(seed) = self.random_state {
379 RealStdRng::seed_from_u64(seed)
380 } else {
381 RealStdRng::from_seed(thread_rng().random())
382 };
383
384 let mut random_weights = Array2::zeros((self.n_components, n_features));
385 for i in 0..self.n_components {
386 for j in 0..n_features {
387 random_weights[(i, j)] = rng.sample(StandardNormal);
388 }
389 }
390
391 let uniform = RandUniform::new(0.0, 2.0 * PI).expect("operation should succeed");
393 let random_biases = Array1::from_shape_fn(self.n_components, |_| rng.sample(uniform));
394
395 Ok(FittedMahalanobisRBF {
396 random_weights,
397 random_biases,
398 whitening_matrix,
399 mean,
400 signal_variance: self.signal_variance,
401 n_features,
402 })
403 }
404}
405
406impl Transform<Array2<f64>, Array2<f64>> for FittedMahalanobisRBF {
407 fn transform(&self, x: &Array2<f64>) -> Result<Array2<f64>> {
408 if x.ncols() != self.n_features {
409 return Err(SklearsError::InvalidInput(
410 "Feature dimension mismatch".to_string(),
411 ));
412 }
413
414 let n_samples = x.nrows();
415 let n_components = self.random_weights.nrows();
416 let normalization = (2.0 * self.signal_variance / n_components as f64).sqrt();
417
418 let x_centered = x - &self.mean.clone().insert_axis(Axis(0));
420 let x_whitened = x_centered.dot(&self.whitening_matrix);
421
422 let mut features = Array2::zeros((n_samples, n_components));
423
424 for i in 0..n_samples {
425 for j in 0..n_components {
426 let dot_product = x_whitened.row(i).dot(&self.random_weights.row(j));
427 features[(i, j)] = normalization * (dot_product + self.random_biases[j]).cos();
428 }
429 }
430
431 Ok(features)
432 }
433}
434
435#[derive(Debug, Clone)]
437pub struct RobustAnisotropicRBFSampler {
439 n_components: usize,
441 robust_estimator: RobustEstimator,
443 signal_variance: f64,
445 random_state: Option<u64>,
447}
448
449#[derive(Debug, Clone)]
451pub enum RobustEstimator {
453 MCD { support_fraction: f64 },
455 MVE,
457 Huber { c: f64 },
459}
460
461#[derive(Debug, Clone)]
463pub struct FittedRobustAnisotropicRBF {
465 random_weights: Array2<f64>,
467 random_biases: Array1<f64>,
469 robust_cov: Array2<f64>,
471 robust_mean: Array1<f64>,
473 signal_variance: f64,
475 n_features: usize,
477}
478
479impl RobustAnisotropicRBFSampler {
480 pub fn new(n_components: usize) -> Self {
482 Self {
483 n_components,
484 robust_estimator: RobustEstimator::MCD {
485 support_fraction: 0.8,
486 },
487 signal_variance: 1.0,
488 random_state: None,
489 }
490 }
491
492 pub fn robust_estimator(mut self, estimator: RobustEstimator) -> Self {
494 self.robust_estimator = estimator;
495 self
496 }
497
498 pub fn signal_variance(mut self, signal_variance: f64) -> Self {
500 self.signal_variance = signal_variance;
501 self
502 }
503
504 pub fn random_state(mut self, seed: u64) -> Self {
506 self.random_state = Some(seed);
507 self
508 }
509
510 fn compute_mcd_covariance(
512 &self,
513 x: &Array2<f64>,
514 support_fraction: f64,
515 ) -> Result<(Array1<f64>, Array2<f64>)> {
516 let n_samples = x.nrows();
517 let n_features = x.ncols();
518 let h = ((n_samples as f64 * support_fraction).floor() as usize).max(n_features + 1);
519
520 let mut best_det = f64::INFINITY;
521 let mut best_mean = Array1::zeros(n_features);
522 let mut best_cov = Array2::eye(n_features);
523
524 let mut rng = thread_rng();
525
526 for _ in 0..50 {
528 let mut indices: Vec<usize> = (0..n_samples).collect();
530 indices.shuffle(&mut rng);
531 indices.truncate(h);
532
533 let subset = x.select(Axis(0), &indices);
534
535 let mean = subset.mean_axis(Axis(0)).expect("operation should succeed");
537 let centered = &subset - &mean.clone().insert_axis(Axis(0));
538 let cov = centered.t().dot(¢ered) / (h - 1) as f64;
539
540 let mut cov_reg = cov;
542 let trace = cov_reg.diag().sum();
543 let reg = trace * 1e-6 / n_features as f64;
544 for i in 0..n_features {
545 cov_reg[(i, i)] += reg;
546 }
547
548 if let Ok((_, s, _)) = cov_reg.svd(false) {
550 let log_det: f64 = s.mapv(|x| if x > 1e-10 { x.ln() } else { -23.0 }).sum();
551 let det = log_det.exp();
552
553 if det < best_det {
554 best_det = det;
555 best_mean = mean;
556 best_cov = cov_reg;
557 }
558 }
559 }
560
561 Ok((best_mean, best_cov))
562 }
563
564 fn compute_huber_covariance(
566 &self,
567 x: &Array2<f64>,
568 c: f64,
569 ) -> Result<(Array1<f64>, Array2<f64>)> {
570 let n_samples = x.nrows();
571 let n_features = x.ncols();
572
573 let mut mean = x.mean_axis(Axis(0)).expect("operation should succeed");
575 let centered = x - &mean.clone().insert_axis(Axis(0));
576 let mut cov = centered.t().dot(¢ered) / (n_samples - 1) as f64;
577
578 for _ in 0..20 {
580 let cov_inv = cov.inv().map_err(|_| {
582 SklearsError::InvalidInput("Covariance matrix not positive definite".to_string())
583 })?;
584
585 let mut weights = Array1::zeros(n_samples);
586 for i in 0..n_samples {
587 let diff = &x.row(i) - &mean;
588 let mahal_dist = diff.dot(&cov_inv).dot(&diff).sqrt();
589 weights[i] = if mahal_dist <= c { 1.0 } else { c / mahal_dist };
590 }
591
592 let weight_sum = weights.sum();
594 let mut new_mean = Array1::zeros(n_features);
595 for i in 0..n_samples {
596 for j in 0..n_features {
597 new_mean[j] += weights[i] * x[(i, j)];
598 }
599 }
600 new_mean /= weight_sum;
601
602 let mut new_cov = Array2::zeros((n_features, n_features));
604 for i in 0..n_samples {
605 let diff = &x.row(i) - &new_mean;
606 let outer =
607 Array2::from_shape_fn((n_features, n_features), |(j, k)| diff[j] * diff[k]);
608 new_cov = new_cov + weights[i] * outer;
609 }
610 new_cov /= weight_sum;
611
612 let mean_diff = (&new_mean - &mean).mapv(|x| x.abs()).sum();
614 if mean_diff < 1e-6 {
615 break;
616 }
617
618 mean = new_mean;
619 cov = new_cov;
620 }
621
622 Ok((mean, cov))
623 }
624}
625
626impl Fit<Array2<f64>, ()> for RobustAnisotropicRBFSampler {
627 type Fitted = FittedRobustAnisotropicRBF;
628 fn fit(self, x: &Array2<f64>, _y: &()) -> Result<Self::Fitted> {
629 let n_features = x.ncols();
630
631 let (robust_mean, robust_cov) = match &self.robust_estimator {
633 RobustEstimator::MCD { support_fraction } => {
634 self.compute_mcd_covariance(x, *support_fraction)?
635 }
636 RobustEstimator::MVE => {
637 self.compute_mcd_covariance(x, 0.5)?
639 }
640 RobustEstimator::Huber { c } => self.compute_huber_covariance(x, *c)?,
641 };
642
643 let precision = robust_cov.inv().map_err(|_| {
645 SklearsError::InvalidInput("Robust covariance matrix not positive definite".to_string())
646 })?;
647
648 let mut rng = if let Some(seed) = self.random_state {
650 RealStdRng::seed_from_u64(seed)
651 } else {
652 RealStdRng::from_seed(thread_rng().random())
653 };
654
655 let (u, s, _vt) = precision.svd(true).map_err(|e| {
658 SklearsError::NumericalError(format!("SVD decomposition failed: {:?}", e))
659 })?;
660 let s_sqrt = s.mapv(|x| x.sqrt());
661 let precision_sqrt = u.dot(&Array2::from_diag(&s_sqrt));
662
663 let mut random_weights = Array2::zeros((self.n_components, n_features));
664 for i in 0..self.n_components {
665 let mut z = Array1::zeros(n_features);
666 for j in 0..n_features {
667 z[j] = rng.sample(StandardNormal);
668 }
669 let w = precision_sqrt.t().dot(&z);
670 random_weights.row_mut(i).assign(&w);
671 }
672
673 let uniform = RandUniform::new(0.0, 2.0 * PI).expect("operation should succeed");
675 let random_biases = Array1::from_shape_fn(self.n_components, |_| rng.sample(uniform));
676
677 Ok(FittedRobustAnisotropicRBF {
678 random_weights,
679 random_biases,
680 robust_cov,
681 robust_mean,
682 signal_variance: self.signal_variance,
683 n_features,
684 })
685 }
686}
687
688impl Transform<Array2<f64>, Array2<f64>> for FittedRobustAnisotropicRBF {
689 fn transform(&self, x: &Array2<f64>) -> Result<Array2<f64>> {
690 if x.ncols() != self.n_features {
691 return Err(SklearsError::InvalidInput(
692 "Feature dimension mismatch".to_string(),
693 ));
694 }
695
696 let n_samples = x.nrows();
697 let n_components = self.random_weights.nrows();
698 let normalization = (2.0 * self.signal_variance / n_components as f64).sqrt();
699
700 let x_centered = x - &self.robust_mean.clone().insert_axis(Axis(0));
702
703 let mut features = Array2::zeros((n_samples, n_components));
704
705 for i in 0..n_samples {
706 for j in 0..n_components {
707 let dot_product = x_centered.row(i).dot(&self.random_weights.row(j));
708 features[(i, j)] = normalization * (dot_product + self.random_biases[j]).cos();
709 }
710 }
711
712 Ok(features)
713 }
714}
715
716#[allow(non_snake_case)]
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use approx::assert_abs_diff_eq;
721 use scirs2_core::ndarray::array;
722
723 #[test]
724 fn test_anisotropic_rbf_sampler() {
725 let sampler = AnisotropicRBFSampler::new(100)
726 .length_scales(vec![1.0, 2.0])
727 .signal_variance(1.5);
728
729 let x = array![[0.0, 0.0], [1.0, 1.0], [2.0, 2.0], [-1.0, -1.0]];
730 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
731 let features = fitted.transform(&x).expect("operation should succeed");
732
733 assert_eq!(features.shape(), &[4, 100]);
734 assert!(features.iter().all(|&x| x.is_finite()));
735
736 let mean = features
738 .mean_axis(Axis(0))
739 .expect("operation should succeed");
740 for &m in mean.iter() {
741 assert!(m.abs() < 0.5);
742 }
743 }
744
745 #[test]
746 fn test_anisotropic_rbf_learned_scales() {
747 let sampler = AnisotropicRBFSampler::new(50)
748 .learn_length_scales(true)
749 .signal_variance(1.0);
750
751 let x = array![
753 [0.0, 0.0],
754 [1.0, 0.1],
755 [2.0, 0.2],
756 [3.0, 0.3],
757 [4.0, 0.4],
758 [5.0, 0.5],
759 [-1.0, -0.1],
760 [-2.0, -0.2]
761 ];
762
763 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
764 let features = fitted.transform(&x).expect("operation should succeed");
765
766 assert_eq!(features.shape(), &[8, 50]);
767 assert!(features.iter().all(|&x| x.is_finite()));
768
769 assert!(fitted.length_scales[0] > fitted.length_scales[1]);
771 }
772
773 #[test]
774 fn test_mahalanobis_rbf_sampler() {
775 let sampler = MahalanobisRBFSampler::new(80)
776 .signal_variance(2.0)
777 .reg_param(1e-4);
778
779 let x = array![
780 [1.0, 2.0],
781 [2.0, 4.0],
782 [3.0, 6.0],
783 [1.5, 3.0],
784 [2.5, 5.0],
785 [0.5, 1.0],
786 [3.5, 7.0],
787 [4.0, 8.0]
788 ];
789
790 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
791 let features = fitted.transform(&x).expect("operation should succeed");
792
793 assert_eq!(features.shape(), &[8, 80]);
794 assert!(features.iter().all(|&x| x.is_finite()));
795
796 let x_test = array![[2.0, 4.0], [1.0, 2.0]];
798 let features_test = fitted.transform(&x_test).expect("operation should succeed");
799 assert_eq!(features_test.shape(), &[2, 80]);
800 assert!(features_test.iter().all(|&x| x.is_finite()));
801 }
802
803 #[test]
804 fn test_robust_anisotropic_rbf_mcd() {
805 let sampler = RobustAnisotropicRBFSampler::new(60)
806 .robust_estimator(RobustEstimator::MCD {
807 support_fraction: 0.7,
808 })
809 .signal_variance(1.0);
810
811 let x = array![
813 [1.0, 1.0],
814 [1.1, 1.1],
815 [0.9, 0.9],
816 [1.2, 1.2],
817 [0.8, 0.8],
818 [1.3, 1.3],
819 [10.0, 10.0], [1.05, 1.05],
821 [0.95, 0.95],
822 [1.15, 1.15]
823 ];
824
825 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
826 let features = fitted.transform(&x).expect("operation should succeed");
827
828 assert_eq!(features.shape(), &[10, 60]);
829 assert!(features.iter().all(|&x| x.is_finite()));
830 }
831
832 #[test]
833 fn test_robust_anisotropic_rbf_huber() {
834 let sampler = RobustAnisotropicRBFSampler::new(40)
835 .robust_estimator(RobustEstimator::Huber { c: 1.345 })
836 .signal_variance(0.5);
837
838 let x = array![
839 [0.0, 0.0],
840 [1.0, 0.0],
841 [0.0, 1.0],
842 [1.0, 1.0],
843 [0.5, 0.5],
844 [1.5, 0.5],
845 [0.5, 1.5],
846 [5.0, 5.0] ];
848
849 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
850 let features = fitted.transform(&x).expect("operation should succeed");
851
852 assert_eq!(features.shape(), &[8, 40]);
853 assert!(features.iter().all(|&x| x.is_finite()));
854 }
855
856 #[test]
857 fn test_anisotropic_rbf_reproducibility() {
858 let sampler1 = AnisotropicRBFSampler::new(30)
859 .random_state(42)
860 .length_scales(vec![1.0, 2.0]);
861
862 let sampler2 = AnisotropicRBFSampler::new(30)
863 .random_state(42)
864 .length_scales(vec![1.0, 2.0]);
865
866 let x = array![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
867
868 let features1 = sampler1
869 .fit(&x, &())
870 .expect("operation should succeed")
871 .transform(&x)
872 .expect("operation should succeed");
873 let features2 = sampler2
874 .fit(&x, &())
875 .expect("operation should succeed")
876 .transform(&x)
877 .expect("operation should succeed");
878
879 for i in 0..features1.nrows() {
880 for j in 0..features1.ncols() {
881 assert_abs_diff_eq!(features1[(i, j)], features2[(i, j)], epsilon = 1e-10);
882 }
883 }
884 }
885
886 #[test]
887 fn test_length_scale_learning() {
888 let sampler = AnisotropicRBFSampler::new(20)
889 .learn_length_scales(true)
890 .optimization_params(10, 1e-4);
891
892 let x = array![
894 [0.0, 0.0],
895 [10.0, 0.1],
896 [20.0, 0.2],
897 [30.0, 0.3],
898 [-10.0, -0.1],
899 [-20.0, -0.2],
900 [15.0, 0.15],
901 [25.0, 0.25]
902 ];
903
904 let fitted = sampler.fit(&x, &()).expect("operation should succeed");
905
906 assert!(fitted.length_scales[0] > 5.0 * fitted.length_scales[1]);
908 }
909}