1use crate::*;
7use scirs2_core::ndarray::{s, Array1, Array2, ArrayView1, ArrayView2, Axis};
9use sklears_core::prelude::{SklearsError, Transform};
10use sklears_core::types::Float;
11use std::collections::HashMap;
12use std::hash::Hash;
13
14#[derive(Debug, Clone)]
43pub struct CountMinSketch {
44 width: usize,
45 depth: usize,
46 include_frequency_statistics: bool,
47 include_sketch_properties: bool,
48 hash_seed: u64,
49}
50
51impl CountMinSketch {
52 pub fn new() -> Self {
54 Self {
55 width: 1000,
56 depth: 5,
57 include_frequency_statistics: true,
58 include_sketch_properties: true,
59 hash_seed: 42,
60 }
61 }
62
63 pub fn width(mut self, width: usize) -> Self {
65 self.width = width;
66 self
67 }
68
69 pub fn depth(mut self, depth: usize) -> Self {
71 self.depth = depth;
72 self
73 }
74
75 pub fn include_frequency_statistics(mut self, include: bool) -> Self {
77 self.include_frequency_statistics = include;
78 self
79 }
80
81 pub fn include_sketch_properties(mut self, include: bool) -> Self {
83 self.include_sketch_properties = include;
84 self
85 }
86
87 pub fn hash_seed(mut self, seed: u64) -> Self {
89 self.hash_seed = seed;
90 self
91 }
92
93 fn hash(&self, item: u64, hash_idx: usize) -> usize {
95 let combined = item.wrapping_add((hash_idx as u64).wrapping_mul(self.hash_seed));
97 let hash_val = combined.wrapping_mul(0x9e3779b97f4a7c15_u64);
98 (hash_val as usize) % self.width
99 }
100
101 fn build_sketch(&self, data: &Array2<f64>) -> Array2<u32> {
103 let mut sketch = Array2::zeros((self.depth, self.width));
104
105 for row in data.outer_iter() {
107 for &value in row.iter() {
108 let item = (value * 1000.0) as u64; for hash_idx in 0..self.depth {
113 let bucket = self.hash(item, hash_idx);
114 sketch[[hash_idx, bucket]] += 1;
115 }
116 }
117 }
118
119 sketch
120 }
121
122 #[allow(dead_code)] fn estimate_frequency(&self, sketch: &Array2<u32>, item: f64) -> u32 {
125 let item_hash = (item * 1000.0) as u64;
126 let mut min_count = u32::MAX;
127
128 for hash_idx in 0..self.depth {
129 let bucket = self.hash(item_hash, hash_idx);
130 min_count = min_count.min(sketch[[hash_idx, bucket]]);
131 }
132
133 min_count
134 }
135
136 fn extract_sketch_features(&self, sketch: &Array2<u32>, data: &Array2<f64>) -> Vec<f64> {
138 let mut features = Vec::new();
139
140 if self.include_frequency_statistics {
141 let total_count: u32 = sketch.sum();
143 features.push(total_count as f64);
144
145 let non_zero_buckets = sketch.iter().filter(|&&x| x > 0).count();
147 features.push(non_zero_buckets as f64);
148
149 let load_factor = non_zero_buckets as f64 / (self.width * self.depth) as f64;
151 features.push(load_factor);
152
153 let max_count = sketch.iter().max().copied().unwrap_or(0);
155 features.push(max_count as f64);
156
157 let avg_count = if non_zero_buckets > 0 {
159 total_count as f64 / non_zero_buckets as f64
160 } else {
161 0.0
162 };
163 features.push(avg_count);
164 }
165
166 if self.include_sketch_properties {
167 features.push(self.width as f64);
169 features.push(self.depth as f64);
170
171 let unique_items = data
173 .iter()
174 .map(|&x| (x * 1000.0) as u64)
175 .collect::<std::collections::HashSet<_>>()
176 .len();
177 let collision_rate = if unique_items > 0 {
178 1.0 - (unique_items as f64 / (self.width as f64))
179 } else {
180 0.0
181 };
182 features.push(collision_rate.max(0.0));
183 }
184
185 features
186 }
187}
188
189impl Default for CountMinSketch {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195impl Transform<ArrayView2<'_, Float>, Array1<Float>> for CountMinSketch {
196 fn transform(&self, data: &ArrayView2<'_, Float>) -> SklResult<Array1<Float>> {
197 if data.is_empty() {
198 return Err(SklearsError::InvalidInput(
199 "Cannot create sketch from empty data".to_string(),
200 ));
201 }
202
203 let data_owned = data.mapv(|x| x).to_owned();
205
206 let sketch = self.build_sketch(&data_owned);
207 let features = self.extract_sketch_features(&sketch, &data_owned);
208
209 Ok(Array1::from_vec(
210 features.into_iter().map(|x| x as Float).collect(),
211 ))
212 }
213}
214
215#[derive(Debug, Clone)]
242pub struct FastJohnsonLindenstrauss {
243 target_dimension: usize,
244 sparsity_parameter: f64,
245 random_state: Option<u64>,
246 include_transform_stats: bool,
247}
248
249impl FastJohnsonLindenstrauss {
250 pub fn new() -> Self {
252 Self {
253 target_dimension: 100,
254 sparsity_parameter: 0.1,
255 random_state: Some(42),
256 include_transform_stats: false,
257 }
258 }
259
260 pub fn target_dimension(mut self, target_dimension: usize) -> Self {
262 self.target_dimension = target_dimension;
263 self
264 }
265
266 pub fn sparsity_parameter(mut self, sparsity: f64) -> Self {
268 self.sparsity_parameter = sparsity.clamp(0.01, 1.0);
269 self
270 }
271
272 pub fn random_state(mut self, seed: u64) -> Self {
274 self.random_state = Some(seed);
275 self
276 }
277
278 pub fn include_transform_stats(mut self, include: bool) -> Self {
280 self.include_transform_stats = include;
281 self
282 }
283
284 fn generate_sparse_matrix(&self, input_dim: usize) -> Array2<f64> {
286 use std::collections::hash_map::DefaultHasher;
287 use std::hash::Hasher;
288
289 let mut matrix = Array2::zeros((self.target_dimension, input_dim));
290 let seed = self.random_state.unwrap_or(42);
291
292 for i in 0..self.target_dimension {
293 for j in 0..input_dim {
294 let mut hasher = DefaultHasher::new();
295 (seed, i, j).hash(&mut hasher);
296 let hash_val = hasher.finish();
297
298 let uniform_val = (hash_val % 1000) as f64 / 1000.0;
300
301 if uniform_val < self.sparsity_parameter {
302 let sign_hash = hash_val >> 32;
304 let sign = if sign_hash.is_multiple_of(2) {
305 1.0
306 } else {
307 -1.0
308 };
309 matrix[[i, j]] = sign / (self.sparsity_parameter * input_dim as f64).sqrt();
310 }
311 }
312 }
313
314 matrix
315 }
316
317 fn fast_hadamard_transform(&self, mut data: Array1<f64>) -> Array1<f64> {
319 let n = data.len();
320
321 let next_pow2 = n.next_power_of_two();
323
324 if next_pow2 > n {
326 let mut padded = Array1::zeros(next_pow2);
327 padded.slice_mut(s![..n]).assign(&data);
328 data = padded;
329 }
330
331 let len = data.len();
332 let mut h = 1;
333
334 while h < len {
335 for i in (0..len).step_by(h * 2) {
336 for j in i..(i + h).min(len) {
337 if j + h < len {
338 let a = data[j];
339 let b = data[j + h];
340 data[j] = a + b;
341 data[j + h] = a - b;
342 }
343 }
344 }
345 h *= 2;
346 }
347
348 let normalizer = (len as f64).sqrt();
350 data.mapv_inplace(|x| x / normalizer);
351
352 if next_pow2 > n {
354 data.slice(s![..n]).to_owned()
355 } else {
356 data
357 }
358 }
359
360 fn apply_fjlt(&self, data: &Array2<f64>) -> Array2<f64> {
362 let n_samples = data.nrows();
363 let input_dim = data.ncols();
364
365 let mut result = Array2::zeros((n_samples, self.target_dimension));
366
367 let sparse_matrix = self.generate_sparse_matrix(input_dim);
369
370 for (i, sample) in data.outer_iter().enumerate() {
372 let hadamard_result = self.fast_hadamard_transform(sample.to_owned());
374
375 for j in 0..self.target_dimension {
377 let mut sum = 0.0;
378 for k in 0..input_dim {
379 sum += sparse_matrix[[j, k]] * hadamard_result[k];
380 }
381 result[[i, j]] = sum;
382 }
383 }
384
385 result
386 }
387
388 fn extract_features(&self, transformed_data: &Array2<f64>) -> Vec<f64> {
390 if self.include_transform_stats {
391 let mut features = Vec::new();
392
393 let mean = transformed_data.mean().unwrap_or(0.0);
395 let std = transformed_data.std(0.0);
396 let min_val = transformed_data
397 .iter()
398 .copied()
399 .fold(f64::INFINITY, f64::min);
400 let max_val = transformed_data
401 .iter()
402 .copied()
403 .fold(f64::NEG_INFINITY, f64::max);
404
405 features.push(mean);
406 features.push(std);
407 features.push(min_val);
408 features.push(max_val);
409 features.push(self.target_dimension as f64);
410 features.push(self.sparsity_parameter);
411
412 features
413 } else {
414 transformed_data.iter().copied().collect()
416 }
417 }
418}
419
420impl Default for FastJohnsonLindenstrauss {
421 fn default() -> Self {
422 Self::new()
423 }
424}
425
426impl Transform<ArrayView2<'_, Float>, Array1<Float>> for FastJohnsonLindenstrauss {
427 fn transform(&self, data: &ArrayView2<'_, Float>) -> SklResult<Array1<Float>> {
428 if data.is_empty() {
429 return Err(SklearsError::InvalidInput(
430 "Cannot transform empty data".to_string(),
431 ));
432 }
433
434 if data.ncols() == 0 {
435 return Err(SklearsError::InvalidInput(
436 "Data must have at least one column".to_string(),
437 ));
438 }
439
440 let data_owned = data.mapv(|x| x).to_owned();
442
443 let transformed = self.apply_fjlt(&data_owned);
444 let features = self.extract_features(&transformed);
445
446 Ok(Array1::from_vec(
447 features.into_iter().map(|x| x as Float).collect(),
448 ))
449 }
450}
451
452#[derive(Debug, Clone)]
473pub struct ReservoirSampler {
474 reservoir_size: usize,
475 random_state: Option<u64>,
476 reservoir: Vec<usize>,
477 seen_count: usize,
478}
479
480impl ReservoirSampler {
481 pub fn new() -> Self {
483 Self {
484 reservoir_size: 1000,
485 random_state: None,
486 reservoir: Vec::new(),
487 seen_count: 0,
488 }
489 }
490
491 pub fn reservoir_size(mut self, size: usize) -> Self {
493 self.reservoir_size = size;
494 self
495 }
496
497 pub fn random_state(mut self, seed: u64) -> Self {
499 self.random_state = Some(seed);
500 self
501 }
502
503 pub fn sample_indices(&mut self, dataset_size: usize) -> SklResult<Vec<usize>> {
505 use scirs2_core::random::thread_rng;
506
507 let mut rng = thread_rng();
509
510 let actual_size = self.reservoir_size.min(dataset_size);
511 let mut reservoir = Vec::with_capacity(actual_size);
512
513 for i in 0..actual_size {
515 reservoir.push(i);
516 }
517
518 for i in actual_size..dataset_size {
520 let j = rng.random_range(0..i + 1);
521 if j < actual_size {
522 reservoir[j] = i;
523 }
524 }
525
526 self.reservoir = reservoir.clone();
527 self.seen_count = dataset_size;
528
529 Ok(reservoir)
530 }
531
532 pub fn sample_data(&mut self, data: &ArrayView2<Float>) -> SklResult<Array2<Float>> {
534 let indices = self.sample_indices(data.nrows())?;
535 let sampled_data = data.select(Axis(0), &indices);
536 Ok(sampled_data)
537 }
538
539 pub fn get_reservoir_indices(&self) -> &[usize] {
541 &self.reservoir
542 }
543
544 pub fn get_seen_count(&self) -> usize {
546 self.seen_count
547 }
548}
549
550impl Default for ReservoirSampler {
551 fn default() -> Self {
552 Self::new()
553 }
554}
555
556#[derive(Debug, Clone)]
579pub struct ImportanceSampler {
580 sample_size: usize,
581 replacement: bool,
582 random_state: Option<u64>,
583}
584
585impl ImportanceSampler {
586 pub fn new() -> Self {
588 Self {
589 sample_size: 1000,
590 replacement: false,
591 random_state: None,
592 }
593 }
594
595 pub fn sample_size(mut self, size: usize) -> Self {
597 self.sample_size = size;
598 self
599 }
600
601 pub fn replacement(mut self, replacement: bool) -> Self {
603 self.replacement = replacement;
604 self
605 }
606
607 pub fn random_state(mut self, seed: u64) -> Self {
609 self.random_state = Some(seed);
610 self
611 }
612
613 pub fn sample_indices(&self, weights: &ArrayView1<Float>) -> SklResult<Vec<usize>> {
615 use scirs2_core::random::thread_rng;
616
617 if weights.is_empty() {
618 return Err(SklearsError::InvalidInput(
619 "Weights cannot be empty".to_string(),
620 ));
621 }
622
623 let weight_sum: Float = weights.sum();
625 if weight_sum <= 0.0 {
626 return Err(SklearsError::InvalidInput(
627 "Weights must be positive".to_string(),
628 ));
629 }
630
631 let probabilities: Array1<Float> = weights / weight_sum;
632 let cumulative: Array1<Float> = {
633 let mut cum = Array1::zeros(probabilities.len());
634 cum[0] = probabilities[0];
635 for i in 1..probabilities.len() {
636 cum[i] = cum[i - 1] + probabilities[i];
637 }
638 cum
639 };
640
641 let mut rng = thread_rng();
643
644 let mut selected_indices = Vec::with_capacity(self.sample_size);
645 let mut used_indices = std::collections::HashSet::new();
646
647 for _ in 0..self.sample_size {
648 let random_value: Float = rng.random();
649
650 let mut idx = 0;
652 for (i, &cum_prob) in cumulative.iter().enumerate() {
653 if random_value <= cum_prob {
654 idx = i;
655 break;
656 }
657 }
658
659 if self.replacement || !used_indices.contains(&idx) {
660 selected_indices.push(idx);
661 if !self.replacement {
662 used_indices.insert(idx);
663 }
664 } else if !self.replacement {
665 for i in 0..weights.len() {
667 if !used_indices.contains(&i) {
668 selected_indices.push(i);
669 used_indices.insert(i);
670 break;
671 }
672 }
673 }
674
675 if !self.replacement && used_indices.len() == weights.len() {
676 break;
677 }
678 }
679
680 Ok(selected_indices)
681 }
682
683 pub fn sample_data(
685 &self,
686 data: &ArrayView2<Float>,
687 weights: &ArrayView1<Float>,
688 ) -> SklResult<Array2<Float>> {
689 if data.nrows() != weights.len() {
690 return Err(SklearsError::InvalidInput(
691 "Data rows and weights length must match".to_string(),
692 ));
693 }
694
695 let indices = self.sample_indices(weights)?;
696 let sampled_data = data.select(Axis(0), &indices);
697 Ok(sampled_data)
698 }
699}
700
701impl Default for ImportanceSampler {
702 fn default() -> Self {
703 Self::new()
704 }
705}
706
707#[derive(Debug, Clone)]
728pub struct StratifiedSampler {
729 sample_size: usize,
730 random_state: Option<u64>,
731}
732
733impl StratifiedSampler {
734 pub fn new() -> Self {
736 Self {
737 sample_size: 1000,
738 random_state: None,
739 }
740 }
741
742 pub fn sample_size(mut self, size: usize) -> Self {
744 self.sample_size = size;
745 self
746 }
747
748 pub fn random_state(mut self, seed: u64) -> Self {
750 self.random_state = Some(seed);
751 self
752 }
753
754 pub fn sample_indices(&self, strata: &ArrayView1<Float>) -> SklResult<Vec<usize>> {
756 use scirs2_core::random::thread_rng;
757
758 if strata.is_empty() {
759 return Err(SklearsError::InvalidInput(
760 "Strata cannot be empty".to_string(),
761 ));
762 }
763
764 let mut rng = thread_rng();
766
767 let mut stratum_indices: HashMap<String, Vec<usize>> = HashMap::new();
769 for (idx, &stratum) in strata.iter().enumerate() {
770 let key = format!("{:.6}", stratum); stratum_indices.entry(key).or_default().push(idx);
772 }
773
774 let total_samples = strata.len();
775 let mut selected_indices = Vec::new();
776
777 for (_, indices) in stratum_indices.iter() {
779 let stratum_proportion = indices.len() as f64 / total_samples as f64;
780 let stratum_sample_size =
781 (self.sample_size as f64 * stratum_proportion).round() as usize;
782 let stratum_sample_size = stratum_sample_size.min(indices.len());
783
784 if stratum_sample_size > 0 {
785 let mut stratum_indices = indices.clone();
786 for i in (1..stratum_indices.len()).rev() {
788 let j = rng.random_range(0..i + 1);
789 stratum_indices.swap(i, j);
790 }
791 selected_indices.extend(stratum_indices.into_iter().take(stratum_sample_size));
792 }
793 }
794
795 while selected_indices.len() < self.sample_size && selected_indices.len() < total_samples {
797 let all_indices: Vec<usize> = (0..total_samples).collect();
798 let mut remaining: Vec<usize> = all_indices
799 .into_iter()
800 .filter(|&idx| !selected_indices.contains(&idx))
801 .collect();
802
803 if remaining.is_empty() {
804 break;
805 }
806
807 for i in (1..remaining.len()).rev() {
809 let j = rng.random_range(0..i + 1);
810 remaining.swap(i, j);
811 }
812 let needed = self.sample_size - selected_indices.len();
813 selected_indices.extend(remaining.into_iter().take(needed));
814 }
815
816 selected_indices.truncate(self.sample_size);
817 Ok(selected_indices)
818 }
819
820 pub fn sample_data(
822 &self,
823 data: &ArrayView2<Float>,
824 strata: &ArrayView1<Float>,
825 ) -> SklResult<Array2<Float>> {
826 if data.nrows() != strata.len() {
827 return Err(SklearsError::InvalidInput(
828 "Data rows and strata length must match".to_string(),
829 ));
830 }
831
832 let indices = self.sample_indices(strata)?;
833 let sampled_data = data.select(Axis(0), &indices);
834 Ok(sampled_data)
835 }
836}
837
838impl Default for StratifiedSampler {
839 fn default() -> Self {
840 Self::new()
841 }
842}
843
844#[derive(Debug, Clone, PartialEq)]
868pub enum TransformType {
869 FFT,
871 DCT,
873 Walsh,
875 Haar,
877}
878
879#[derive(Debug, Clone)]
880pub struct FastTransformExtractor {
881 transform_type: TransformType,
882 include_magnitude: bool,
883 include_phase: bool,
884 include_power: bool,
885 normalize: bool,
886 zero_pad_to_power_of_2: bool,
887}
888
889impl FastTransformExtractor {
890 pub fn new() -> Self {
892 Self {
893 transform_type: TransformType::FFT,
894 include_magnitude: true,
895 include_phase: false,
896 include_power: false,
897 normalize: true,
898 zero_pad_to_power_of_2: true,
899 }
900 }
901
902 pub fn transform_type(mut self, transform_type: TransformType) -> Self {
904 self.transform_type = transform_type;
905 self
906 }
907
908 pub fn include_magnitude(mut self, include: bool) -> Self {
910 self.include_magnitude = include;
911 self
912 }
913
914 pub fn include_phase(mut self, include: bool) -> Self {
916 self.include_phase = include;
917 self
918 }
919
920 pub fn include_power(mut self, include: bool) -> Self {
922 self.include_power = include;
923 self
924 }
925
926 pub fn normalize(mut self, normalize: bool) -> Self {
928 self.normalize = normalize;
929 self
930 }
931
932 pub fn zero_pad_to_power_of_2(mut self, zero_pad: bool) -> Self {
934 self.zero_pad_to_power_of_2 = zero_pad;
935 self
936 }
937
938 pub fn extract_features(&self, data: &ArrayView2<Float>) -> SklResult<Array2<Float>> {
940 if data.is_empty() {
941 return Err(SklearsError::InvalidInput(
942 "Input data cannot be empty".to_string(),
943 ));
944 }
945
946 let mut features_list = Vec::new();
947
948 for row in data.axis_iter(Axis(0)) {
949 let row_features = match self.transform_type {
950 TransformType::FFT => self.compute_fft(&row)?,
951 TransformType::DCT => self.compute_dct(&row)?,
952 TransformType::Walsh => self.compute_walsh(&row)?,
953 TransformType::Haar => self.compute_haar(&row)?,
954 };
955 features_list.push(row_features);
956 }
957
958 if features_list.is_empty() {
959 return Err(SklearsError::InvalidInput(
960 "No features extracted".to_string(),
961 ));
962 }
963
964 let n_features = features_list[0].len();
965 let n_samples = features_list.len();
966 let mut result = Array2::zeros((n_samples, n_features));
967
968 for (i, features) in features_list.into_iter().enumerate() {
969 for (j, &value) in features.iter().enumerate() {
970 result[[i, j]] = value;
971 }
972 }
973
974 Ok(result)
975 }
976
977 fn compute_fft(&self, signal: &ArrayView1<Float>) -> SklResult<Vec<Float>> {
979 let mut padded_signal = signal.to_vec();
980
981 if self.zero_pad_to_power_of_2 {
983 let next_power_of_2 = (padded_signal.len() as f64).log2().ceil() as u32;
984 let target_size = 2_usize.pow(next_power_of_2);
985 padded_signal.resize(target_size, 0.0);
986 }
987
988 let n = padded_signal.len();
990 let mut real_part = Vec::with_capacity(n / 2 + 1);
991 let mut imag_part = Vec::with_capacity(n / 2 + 1);
992
993 for k in 0..=n / 2 {
994 let mut real_sum = 0.0;
995 let mut imag_sum = 0.0;
996
997 #[allow(clippy::needless_range_loop)]
998 for j in 0..n {
1000 let angle = -2.0 * std::f64::consts::PI * (k as f64) * (j as f64) / (n as f64);
1001 real_sum += padded_signal[j] * angle.cos();
1002 imag_sum += padded_signal[j] * angle.sin();
1003 }
1004
1005 real_part.push(real_sum);
1006 imag_part.push(imag_sum);
1007 }
1008
1009 let mut features = Vec::new();
1010
1011 if self.include_magnitude {
1012 for i in 0..real_part.len() {
1013 let magnitude = (real_part[i] * real_part[i] + imag_part[i] * imag_part[i]).sqrt();
1014 features.push(magnitude);
1015 }
1016 }
1017
1018 if self.include_phase {
1019 for i in 0..real_part.len() {
1020 let phase = imag_part[i].atan2(real_part[i]);
1021 features.push(phase);
1022 }
1023 }
1024
1025 if self.include_power {
1026 for i in 0..real_part.len() {
1027 let power = real_part[i] * real_part[i] + imag_part[i] * imag_part[i];
1028 features.push(power);
1029 }
1030 }
1031
1032 if self.normalize && !features.is_empty() {
1033 let max_val = features.iter().fold(0.0 as Float, |a, &b| a.max(b.abs()));
1034 if max_val > 0.0 {
1035 for feature in features.iter_mut() {
1036 *feature /= max_val;
1037 }
1038 }
1039 }
1040
1041 Ok(features)
1042 }
1043
1044 fn compute_dct(&self, signal: &ArrayView1<Float>) -> SklResult<Vec<Float>> {
1046 let n = signal.len();
1047 let mut dct_coeffs = Vec::with_capacity(n);
1048
1049 for k in 0..n {
1050 let mut sum = 0.0;
1051 for j in 0..n {
1052 let angle = std::f64::consts::PI * (k as f64) * (j as f64 + 0.5) / (n as f64);
1053 sum += signal[j] * angle.cos();
1054 }
1055
1056 let coeff = if k == 0 {
1058 sum * (1.0 / n as f64).sqrt()
1059 } else {
1060 sum * (2.0 / n as f64).sqrt()
1061 };
1062
1063 dct_coeffs.push(coeff);
1064 }
1065
1066 if self.normalize && !dct_coeffs.is_empty() {
1067 let max_val = dct_coeffs.iter().fold(0.0 as Float, |a, &b| a.max(b.abs()));
1068 if max_val > 0.0 {
1069 for coeff in dct_coeffs.iter_mut() {
1070 *coeff /= max_val;
1071 }
1072 }
1073 }
1074
1075 Ok(dct_coeffs)
1076 }
1077
1078 fn compute_walsh(&self, signal: &ArrayView1<Float>) -> SklResult<Vec<Float>> {
1080 let mut data = signal.to_vec();
1081 let n = data.len();
1082
1083 let next_power_of_2 = (n as f64).log2().ceil() as u32;
1085 let target_size = 2_usize.pow(next_power_of_2);
1086 data.resize(target_size, 0.0);
1087
1088 let mut step = 1;
1090 while step < target_size {
1091 for i in (0..target_size).step_by(step * 2) {
1092 for j in 0..step {
1093 let u = data[i + j];
1094 let v = data[i + j + step];
1095 data[i + j] = u + v;
1096 data[i + j + step] = u - v;
1097 }
1098 }
1099 step *= 2;
1100 }
1101
1102 if self.normalize && !data.is_empty() {
1103 let norm_factor = (target_size as f64).sqrt();
1104 for value in data.iter_mut() {
1105 *value /= norm_factor;
1106 }
1107 }
1108
1109 Ok(data)
1110 }
1111
1112 fn compute_haar(&self, signal: &ArrayView1<Float>) -> SklResult<Vec<Float>> {
1114 let mut data = signal.to_vec();
1115 let n = data.len();
1116
1117 let next_power_of_2 = (n as f64).log2().ceil() as u32;
1119 let target_size = 2_usize.pow(next_power_of_2);
1120 data.resize(target_size, 0.0);
1121
1122 let mut temp = vec![0.0; target_size];
1123 let mut length = target_size;
1124
1125 while length > 1 {
1126 let half_length = length / 2;
1127
1128 for i in 0..half_length {
1130 temp[i] = (data[2 * i] + data[2 * i + 1]) / 2.0_f64.sqrt();
1131 }
1132
1133 for i in 0..half_length {
1135 temp[half_length + i] = (data[2 * i] - data[2 * i + 1]) / 2.0_f64.sqrt();
1136 }
1137
1138 data[0..length].copy_from_slice(&temp[0..length]);
1140 length = half_length;
1141 }
1142
1143 if self.normalize && !data.is_empty() {
1144 let max_val = data.iter().fold(0.0 as Float, |a, &b| a.max(b.abs()));
1145 if max_val > 0.0 {
1146 for value in data.iter_mut() {
1147 *value /= max_val;
1148 }
1149 }
1150 }
1151
1152 Ok(data)
1153 }
1154}
1155
1156impl Default for FastTransformExtractor {
1157 fn default() -> Self {
1158 Self::new()
1159 }
1160}
1161
1162#[allow(non_snake_case)]
1163#[cfg(test)]
1164mod tests {
1165 use super::*;
1166 use scirs2_core::ndarray::Array2;
1167
1168 #[test]
1169 fn test_count_min_sketch_basic() {
1170 let data = Array2::from_shape_vec((50, 3), (0..150).map(|x| (x % 20) as f64).collect())
1171 .expect("operation should succeed");
1172
1173 let sketch = CountMinSketch::new()
1174 .width(50)
1175 .depth(3)
1176 .include_frequency_statistics(true)
1177 .include_sketch_properties(true);
1178
1179 let features = sketch
1180 .transform(&data.view())
1181 .expect("operation should succeed");
1182
1183 assert_eq!(features.len(), 8);
1185
1186 for &feature in &features {
1188 assert!(feature.is_finite(), "Feature should be finite");
1189 }
1190
1191 assert!(features[0] > 0.0, "Total count should be positive");
1193 }
1194
1195 #[test]
1196 fn test_count_min_sketch_configuration() {
1197 let data = Array2::from_shape_vec((20, 2), (0..40).map(|x| x as f64).collect())
1198 .expect("operation should succeed");
1199
1200 let sketch1 = CountMinSketch::new()
1202 .width(100)
1203 .depth(5)
1204 .include_frequency_statistics(true)
1205 .include_sketch_properties(false);
1206
1207 let features1 = sketch1
1208 .transform(&data.view())
1209 .expect("operation should succeed");
1210 assert_eq!(features1.len(), 5); let sketch2 = CountMinSketch::new()
1213 .width(100)
1214 .depth(5)
1215 .include_frequency_statistics(false)
1216 .include_sketch_properties(true);
1217
1218 let features2 = sketch2
1219 .transform(&data.view())
1220 .expect("operation should succeed");
1221 assert_eq!(features2.len(), 3); }
1223
1224 #[test]
1225 fn test_count_min_sketch_empty_data() {
1226 let data = Array2::zeros((0, 2));
1227 let sketch = CountMinSketch::new();
1228
1229 let result = sketch.transform(&data.view());
1230 assert!(result.is_err(), "Should return error for empty data");
1231 }
1232
1233 #[test]
1234 fn test_fast_johnson_lindenstrauss_basic() {
1235 let data = Array2::from_shape_vec((10, 8), (0..80).map(|x| x as f64).collect())
1236 .expect("operation should succeed");
1237
1238 let fjlt = FastJohnsonLindenstrauss::new()
1239 .target_dimension(4)
1240 .sparsity_parameter(0.2)
1241 .include_transform_stats(false);
1242
1243 let features = fjlt
1244 .transform(&data.view())
1245 .expect("operation should succeed");
1246
1247 assert_eq!(features.len(), 40);
1249
1250 for &feature in &features {
1252 assert!(feature.is_finite(), "Feature should be finite");
1253 }
1254 }
1255
1256 #[test]
1257 fn test_fast_johnson_lindenstrauss_stats() {
1258 let data = Array2::from_shape_vec((5, 4), (0..20).map(|x| x as f64).collect())
1259 .expect("operation should succeed");
1260
1261 let fjlt = FastJohnsonLindenstrauss::new()
1262 .target_dimension(2)
1263 .include_transform_stats(true);
1264
1265 let features = fjlt
1266 .transform(&data.view())
1267 .expect("operation should succeed");
1268
1269 assert_eq!(features.len(), 6);
1271
1272 for &feature in &features {
1274 assert!(feature.is_finite(), "Feature should be finite");
1275 }
1276
1277 assert_eq!(features[4], 2.0);
1279 }
1280
1281 #[test]
1282 fn test_fast_johnson_lindenstrauss_empty_data() {
1283 let data = Array2::zeros((0, 4));
1284 let fjlt = FastJohnsonLindenstrauss::new();
1285
1286 let result = fjlt.transform(&data.view());
1287 assert!(result.is_err(), "Should return error for empty data");
1288 }
1289
1290 #[test]
1291 fn test_fast_johnson_lindenstrauss_zero_columns() {
1292 let data = Array2::zeros((5, 0));
1293 let fjlt = FastJohnsonLindenstrauss::new();
1294
1295 let result = fjlt.transform(&data.view());
1296 assert!(result.is_err(), "Should return error for zero columns");
1297 }
1298
1299 #[test]
1300 fn test_sketching_methods_consistency() {
1301 let data = Array2::from_shape_vec((30, 4), (0..120).map(|x| (x % 10) as f64).collect())
1302 .expect("operation should succeed");
1303
1304 let sketch1 = CountMinSketch::new().hash_seed(123);
1306 let sketch2 = CountMinSketch::new().hash_seed(123);
1307
1308 let features1 = sketch1
1309 .transform(&data.view())
1310 .expect("operation should succeed");
1311 let features2 = sketch2
1312 .transform(&data.view())
1313 .expect("operation should succeed");
1314
1315 assert_eq!(features1.len(), features2.len());
1317 for (f1, f2) in features1.iter().zip(features2.iter()) {
1318 assert!(
1319 (f1 - f2).abs() < 1e-10,
1320 "Features should be identical with same seed"
1321 );
1322 }
1323
1324 let fjlt1 = FastJohnsonLindenstrauss::new()
1326 .random_state(456)
1327 .target_dimension(3);
1328 let fjlt2 = FastJohnsonLindenstrauss::new()
1329 .random_state(456)
1330 .target_dimension(3);
1331
1332 let fjlt_features1 = fjlt1
1333 .transform(&data.view())
1334 .expect("operation should succeed");
1335 let fjlt_features2 = fjlt2
1336 .transform(&data.view())
1337 .expect("operation should succeed");
1338
1339 assert_eq!(fjlt_features1.len(), fjlt_features2.len());
1341 for (f1, f2) in fjlt_features1.iter().zip(fjlt_features2.iter()) {
1342 assert!(
1343 (f1 - f2).abs() < 1e-10,
1344 "FJLT features should be identical with same seed"
1345 );
1346 }
1347 }
1348
1349 #[test]
1350 fn test_reservoir_sampler_basic() {
1351 let mut sampler = ReservoirSampler::new().reservoir_size(10).random_state(42);
1352
1353 let indices = sampler
1354 .sample_indices(20)
1355 .expect("operation should succeed");
1356 assert_eq!(indices.len(), 10);
1357 assert_eq!(sampler.get_seen_count(), 20);
1358
1359 for &idx in &indices {
1361 assert!(idx < 20);
1362 }
1363 }
1364
1365 #[test]
1366 fn test_importance_sampler_basic() {
1367 let weights = Array1::from_vec(vec![0.1, 0.3, 0.2, 0.4]);
1368 let sampler = ImportanceSampler::new().sample_size(3).random_state(42);
1369
1370 let indices = sampler
1371 .sample_indices(&weights.view())
1372 .expect("operation should succeed");
1373 assert_eq!(indices.len(), 3);
1374
1375 for &idx in &indices {
1377 assert!(idx < 4);
1378 }
1379 }
1380
1381 #[test]
1382 fn test_stratified_sampler_basic() {
1383 let strata = Array1::from_vec(vec![1.0, 1.0, 2.0, 2.0, 3.0, 3.0]);
1384 let sampler = StratifiedSampler::new().sample_size(4).random_state(42);
1385
1386 let indices = sampler
1387 .sample_indices(&strata.view())
1388 .expect("operation should succeed");
1389 assert_eq!(indices.len(), 4);
1390
1391 for &idx in &indices {
1393 assert!(idx < 6);
1394 }
1395 }
1396
1397 #[test]
1398 fn test_fast_transform_extractor_fft() {
1399 let data = Array2::from_shape_vec((3, 8), (0..24).map(|x| x as f64).collect())
1400 .expect("operation should succeed");
1401
1402 let extractor = FastTransformExtractor::new()
1403 .transform_type(TransformType::FFT)
1404 .include_magnitude(true)
1405 .include_phase(false);
1406
1407 let features = extractor
1408 .extract_features(&data.view())
1409 .expect("operation should succeed");
1410 assert_eq!(features.nrows(), 3);
1411 assert!(features.ncols() > 0);
1412
1413 for &value in features.iter() {
1415 assert!(value.is_finite());
1416 }
1417 }
1418
1419 #[test]
1420 fn test_fast_transform_extractor_dct() {
1421 let data = Array2::from_shape_vec((2, 4), (0..8).map(|x| x as f64).collect())
1422 .expect("operation should succeed");
1423
1424 let extractor = FastTransformExtractor::new()
1425 .transform_type(TransformType::DCT)
1426 .normalize(true);
1427
1428 let features = extractor
1429 .extract_features(&data.view())
1430 .expect("operation should succeed");
1431 assert_eq!(features.nrows(), 2);
1432 assert_eq!(features.ncols(), 4);
1433
1434 for &value in features.iter() {
1436 assert!(value.is_finite());
1437 }
1438 }
1439}