1use ghostflow_core::Tensor;
4use rand::prelude::*;
5
6pub struct TSNE {
8 pub n_components: usize,
9 pub perplexity: f32,
10 pub learning_rate: f32,
11 pub n_iter: usize,
12 pub early_exaggeration: f32,
13 pub metric: TSNEMetric,
14 embedding_: Option<Vec<f32>>,
15 kl_divergence_: Option<f32>,
16}
17
18#[derive(Clone, Copy, Debug)]
19pub enum TSNEMetric {
20 Euclidean,
21 Cosine,
22 Manhattan,
23}
24
25impl TSNE {
26 pub fn new(n_components: usize) -> Self {
27 TSNE {
28 n_components,
29 perplexity: 30.0,
30 learning_rate: 200.0,
31 n_iter: 1000,
32 early_exaggeration: 12.0,
33 metric: TSNEMetric::Euclidean,
34 embedding_: None,
35 kl_divergence_: None,
36 }
37 }
38
39 pub fn perplexity(mut self, p: f32) -> Self {
40 self.perplexity = p;
41 self
42 }
43
44 pub fn learning_rate(mut self, lr: f32) -> Self {
45 self.learning_rate = lr;
46 self
47 }
48
49 pub fn n_iter(mut self, n: usize) -> Self {
50 self.n_iter = n;
51 self
52 }
53
54 fn distance(&self, a: &[f32], b: &[f32]) -> f32 {
55 match self.metric {
56 TSNEMetric::Euclidean => {
57 a.iter().zip(b.iter()).map(|(&ai, &bi)| (ai - bi).powi(2)).sum::<f32>().sqrt()
58 }
59 TSNEMetric::Cosine => {
60 let dot: f32 = a.iter().zip(b.iter()).map(|(&ai, &bi)| ai * bi).sum();
61 let norm_a: f32 = a.iter().map(|&x| x * x).sum::<f32>().sqrt();
62 let norm_b: f32 = b.iter().map(|&x| x * x).sum::<f32>().sqrt();
63 1.0 - dot / (norm_a * norm_b + 1e-10)
64 }
65 TSNEMetric::Manhattan => {
66 a.iter().zip(b.iter()).map(|(&ai, &bi)| (ai - bi).abs()).sum()
67 }
68 }
69 }
70
71 fn compute_pairwise_distances(&self, x: &[f32], n_samples: usize, n_features: usize) -> Vec<Vec<f32>> {
72 let mut distances = vec![vec![0.0f32; n_samples]; n_samples];
73
74 for i in 0..n_samples {
75 for j in (i + 1)..n_samples {
76 let xi = &x[i * n_features..(i + 1) * n_features];
77 let xj = &x[j * n_features..(j + 1) * n_features];
78 let d = self.distance(xi, xj);
79 distances[i][j] = d;
80 distances[j][i] = d;
81 }
82 }
83
84 distances
85 }
86
87 fn compute_perplexity_row(&self, distances: &[f32], target_perplexity: f32, i: usize) -> (Vec<f32>, f32) {
88 let n = distances.len();
89 let target_entropy = target_perplexity.ln();
90
91 let mut beta = 1.0f32;
92 let mut beta_min = f32::NEG_INFINITY;
93 let mut beta_max = f32::INFINITY;
94
95 let mut p = vec![0.0f32; n];
96
97 for _ in 0..50 {
98 let mut sum_p = 0.0f32;
100 for j in 0..n {
101 if i != j {
102 p[j] = (-beta * distances[j] * distances[j]).exp();
103 sum_p += p[j];
104 } else {
105 p[j] = 0.0;
106 }
107 }
108
109 if sum_p > 0.0 {
111 for pj in &mut p {
112 *pj /= sum_p;
113 }
114 }
115
116 let mut entropy = 0.0f32;
118 for j in 0..n {
119 if p[j] > 1e-10 {
120 entropy -= p[j] * p[j].ln();
121 }
122 }
123
124 let diff = entropy - target_entropy;
126 if diff.abs() < 1e-5 {
127 break;
128 }
129
130 if diff > 0.0 {
131 beta_min = beta;
132 beta = if beta_max.is_infinite() { beta * 2.0 } else { (beta + beta_max) / 2.0 };
133 } else {
134 beta_max = beta;
135 beta = if beta_min.is_infinite() { beta / 2.0 } else { (beta + beta_min) / 2.0 };
136 }
137 }
138
139 (p, beta)
140 }
141
142 fn compute_joint_probabilities(&self, distances: &[Vec<f32>], n_samples: usize) -> Vec<Vec<f32>> {
143 let mut p = vec![vec![0.0f32; n_samples]; n_samples];
144
145 for i in 0..n_samples {
147 let (p_row, _) = self.compute_perplexity_row(&distances[i], self.perplexity, i);
148 for j in 0..n_samples {
149 p[i][j] = p_row[j];
150 }
151 }
152
153 for i in 0..n_samples {
155 for j in (i + 1)..n_samples {
156 let pij = (p[i][j] + p[j][i]) / (2.0 * n_samples as f32);
157 p[i][j] = pij.max(1e-12);
158 p[j][i] = pij.max(1e-12);
159 }
160 }
161
162 p
163 }
164
165 fn compute_q_distribution(&self, y: &[f32], n_samples: usize) -> Vec<Vec<f32>> {
166 let mut q = vec![vec![0.0f32; n_samples]; n_samples];
167 let mut sum_q = 0.0f32;
168
169 for i in 0..n_samples {
170 for j in (i + 1)..n_samples {
171 let mut dist_sq = 0.0f32;
172 for d in 0..self.n_components {
173 let diff = y[i * self.n_components + d] - y[j * self.n_components + d];
174 dist_sq += diff * diff;
175 }
176
177 let qij = 1.0 / (1.0 + dist_sq);
179 q[i][j] = qij;
180 q[j][i] = qij;
181 sum_q += 2.0 * qij;
182 }
183 }
184
185 if sum_q > 0.0 {
187 for i in 0..n_samples {
188 for j in 0..n_samples {
189 q[i][j] = (q[i][j] / sum_q).max(1e-12);
190 }
191 }
192 }
193
194 q
195 }
196
197 pub fn fit_transform(&mut self, x: &Tensor) -> Tensor {
198 let x_data = x.data_f32();
199 let n_samples = x.dims()[0];
200 let n_features = x.dims()[1];
201
202 let distances = self.compute_pairwise_distances(&x_data, n_samples, n_features);
204
205 let mut p = self.compute_joint_probabilities(&distances, n_samples);
207
208 for i in 0..n_samples {
210 for j in 0..n_samples {
211 p[i][j] *= self.early_exaggeration;
212 }
213 }
214
215 let mut rng = thread_rng();
217 let mut y: Vec<f32> = (0..n_samples * self.n_components)
218 .map(|_| rng.gen::<f32>() * 0.0001)
219 .collect();
220
221 let mut velocity = vec![0.0f32; n_samples * self.n_components];
223 let momentum = 0.5f32;
224 let final_momentum = 0.8f32;
225 let momentum_switch_iter = 250;
226
227 for iter in 0..self.n_iter {
228 if iter == 100 {
230 for i in 0..n_samples {
231 for j in 0..n_samples {
232 p[i][j] /= self.early_exaggeration;
233 }
234 }
235 }
236
237 let q = self.compute_q_distribution(&y, n_samples);
239
240 let mut grad = vec![0.0f32; n_samples * self.n_components];
242
243 for i in 0..n_samples {
244 for j in 0..n_samples {
245 if i != j {
246 let mut dist_sq = 0.0f32;
247 for d in 0..self.n_components {
248 let diff = y[i * self.n_components + d] - y[j * self.n_components + d];
249 dist_sq += diff * diff;
250 }
251
252 let pq_diff = p[i][j] - q[i][j];
253 let mult = 4.0 * pq_diff / (1.0 + dist_sq);
254
255 for d in 0..self.n_components {
256 let diff = y[i * self.n_components + d] - y[j * self.n_components + d];
257 grad[i * self.n_components + d] += mult * diff;
258 }
259 }
260 }
261 }
262
263 let current_momentum = if iter < momentum_switch_iter { momentum } else { final_momentum };
265
266 for i in 0..n_samples * self.n_components {
267 velocity[i] = current_momentum * velocity[i] - self.learning_rate * grad[i];
268 y[i] += velocity[i];
269 }
270
271 let mut mean = vec![0.0f32; self.n_components];
273 for i in 0..n_samples {
274 for d in 0..self.n_components {
275 mean[d] += y[i * self.n_components + d];
276 }
277 }
278 for d in 0..self.n_components {
279 mean[d] /= n_samples as f32;
280 }
281 for i in 0..n_samples {
282 for d in 0..self.n_components {
283 y[i * self.n_components + d] -= mean[d];
284 }
285 }
286 }
287
288 let q = self.compute_q_distribution(&y, n_samples);
290 let mut kl = 0.0f32;
291 for i in 0..n_samples {
292 for j in 0..n_samples {
293 if i != j && p[i][j] > 1e-12 {
294 kl += p[i][j] * (p[i][j] / q[i][j]).ln();
295 }
296 }
297 }
298
299 self.embedding_ = Some(y.clone());
300 self.kl_divergence_ = Some(kl);
301
302 Tensor::from_slice(&y, &[n_samples, self.n_components]).unwrap()
303 }
304}
305
306
307pub struct MDS {
309 pub n_components: usize,
310 pub metric: bool,
311 pub n_init: usize,
312 pub max_iter: usize,
313 pub eps: f32,
314 pub dissimilarity: Dissimilarity,
315 embedding_: Option<Vec<f32>>,
316 stress_: Option<f32>,
317}
318
319#[derive(Clone, Copy, Debug)]
320pub enum Dissimilarity {
321 Euclidean,
322 Precomputed,
323}
324
325impl MDS {
326 pub fn new(n_components: usize) -> Self {
327 MDS {
328 n_components,
329 metric: true,
330 n_init: 4,
331 max_iter: 300,
332 eps: 1e-3,
333 dissimilarity: Dissimilarity::Euclidean,
334 embedding_: None,
335 stress_: None,
336 }
337 }
338
339 pub fn metric(mut self, metric: bool) -> Self {
340 self.metric = metric;
341 self
342 }
343
344 fn compute_distances(&self, x: &[f32], n_samples: usize, n_features: usize) -> Vec<Vec<f32>> {
345 let mut distances = vec![vec![0.0f32; n_samples]; n_samples];
346
347 for i in 0..n_samples {
348 for j in (i + 1)..n_samples {
349 let mut dist_sq = 0.0f32;
350 for k in 0..n_features {
351 let diff = x[i * n_features + k] - x[j * n_features + k];
352 dist_sq += diff * diff;
353 }
354 let dist = dist_sq.sqrt();
355 distances[i][j] = dist;
356 distances[j][i] = dist;
357 }
358 }
359
360 distances
361 }
362
363 fn compute_stress(&self, y: &[f32], distances: &[Vec<f32>], n_samples: usize) -> f32 {
364 let mut stress = 0.0f32;
365 let mut norm = 0.0f32;
366
367 for i in 0..n_samples {
368 for j in (i + 1)..n_samples {
369 let mut dist_sq = 0.0f32;
370 for d in 0..self.n_components {
371 let diff = y[i * self.n_components + d] - y[j * self.n_components + d];
372 dist_sq += diff * diff;
373 }
374 let dist = dist_sq.sqrt();
375
376 let diff = distances[i][j] - dist;
377 stress += diff * diff;
378 norm += distances[i][j] * distances[i][j];
379 }
380 }
381
382 (stress / norm.max(1e-10)).sqrt()
383 }
384
385 pub fn fit_transform(&mut self, x: &Tensor) -> Tensor {
386 let x_data = x.data_f32();
387 let n_samples = x.dims()[0];
388 let n_features = x.dims()[1];
389
390 let distances = match self.dissimilarity {
392 Dissimilarity::Euclidean => self.compute_distances(&x_data, n_samples, n_features),
393 Dissimilarity::Precomputed => {
394 let mut d = vec![vec![0.0f32; n_samples]; n_samples];
396 for i in 0..n_samples {
397 for j in 0..n_samples {
398 d[i][j] = x_data[i * n_samples + j];
399 }
400 }
401 d
402 }
403 };
404
405 let mut best_y = None;
407 let mut best_stress = f32::INFINITY;
408
409 for _ in 0..self.n_init {
410 let mut b = vec![vec![0.0f32; n_samples]; n_samples];
412
413 for i in 0..n_samples {
415 for j in 0..n_samples {
416 b[i][j] = -0.5 * distances[i][j] * distances[i][j];
417 }
418 }
419
420 let row_means: Vec<f32> = (0..n_samples)
422 .map(|i| b[i].iter().sum::<f32>() / n_samples as f32)
423 .collect();
424
425 let col_means: Vec<f32> = (0..n_samples)
427 .map(|j| (0..n_samples).map(|i| b[i][j]).sum::<f32>() / n_samples as f32)
428 .collect();
429
430 let grand_mean: f32 = row_means.iter().sum::<f32>() / n_samples as f32;
432
433 for i in 0..n_samples {
435 for j in 0..n_samples {
436 b[i][j] = b[i][j] - row_means[i] - col_means[j] + grand_mean;
437 }
438 }
439
440 let mut y = vec![0.0f32; n_samples * self.n_components];
442 let mut rng = thread_rng();
443
444 let mut b_flat: Vec<f32> = b.iter().flat_map(|row| row.clone()).collect();
446
447 for comp in 0..self.n_components {
448 let mut v: Vec<f32> = (0..n_samples).map(|_| rng.gen::<f32>()).collect();
450 let norm: f32 = v.iter().map(|&x| x * x).sum::<f32>().sqrt();
451 for vi in &mut v {
452 *vi /= norm;
453 }
454
455 for _ in 0..100 {
456 let mut w = vec![0.0f32; n_samples];
458 for i in 0..n_samples {
459 for j in 0..n_samples {
460 w[i] += b_flat[i * n_samples + j] * v[j];
461 }
462 }
463
464 let norm: f32 = w.iter().map(|&x| x * x).sum::<f32>().sqrt();
466 if norm < 1e-10 {
467 break;
468 }
469 for wi in &mut w {
470 *wi /= norm;
471 }
472
473 let diff: f32 = v.iter().zip(w.iter()).map(|(&vi, &wi)| (vi - wi).abs()).sum();
475 v = w;
476
477 if diff < 1e-6 {
478 break;
479 }
480 }
481
482 let mut eigenvalue = 0.0f32;
484 for i in 0..n_samples {
485 let mut bv = 0.0f32;
486 for j in 0..n_samples {
487 bv += b_flat[i * n_samples + j] * v[j];
488 }
489 eigenvalue += v[i] * bv;
490 }
491
492 let scale = eigenvalue.max(0.0).sqrt();
494 for i in 0..n_samples {
495 y[i * self.n_components + comp] = v[i] * scale;
496 }
497
498 for i in 0..n_samples {
500 for j in 0..n_samples {
501 b_flat[i * n_samples + j] -= eigenvalue * v[i] * v[j];
502 }
503 }
504 }
505
506 let stress = self.compute_stress(&y, &distances, n_samples);
507 if stress < best_stress {
508 best_stress = stress;
509 best_y = Some(y);
510 }
511 }
512
513 let y = best_y.unwrap();
514 self.embedding_ = Some(y.clone());
515 self.stress_ = Some(best_stress);
516
517 Tensor::from_slice(&y, &[n_samples, self.n_components]).unwrap()
518 }
519}
520
521pub struct Isomap {
523 pub n_components: usize,
524 pub n_neighbors: usize,
525 embedding_: Option<Vec<f32>>,
526 reconstruction_error_: Option<f32>,
527}
528
529impl Isomap {
530 pub fn new(n_components: usize) -> Self {
531 Isomap {
532 n_components,
533 n_neighbors: 5,
534 embedding_: None,
535 reconstruction_error_: None,
536 }
537 }
538
539 pub fn n_neighbors(mut self, n: usize) -> Self {
540 self.n_neighbors = n;
541 self
542 }
543
544 fn compute_knn_graph(&self, x: &[f32], n_samples: usize, n_features: usize) -> Vec<Vec<(usize, f32)>> {
545 let mut graph = vec![Vec::new(); n_samples];
546
547 for i in 0..n_samples {
548 let mut distances: Vec<(usize, f32)> = (0..n_samples)
549 .filter(|&j| j != i)
550 .map(|j| {
551 let mut dist_sq = 0.0f32;
552 for k in 0..n_features {
553 let diff = x[i * n_features + k] - x[j * n_features + k];
554 dist_sq += diff * diff;
555 }
556 (j, dist_sq.sqrt())
557 })
558 .collect();
559
560 distances.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
561 graph[i] = distances.into_iter().take(self.n_neighbors).collect();
562 }
563
564 graph
565 }
566
567 fn floyd_warshall(&self, graph: &[Vec<(usize, f32)>], n_samples: usize) -> Vec<Vec<f32>> {
568 let mut dist = vec![vec![f32::INFINITY; n_samples]; n_samples];
569
570 for i in 0..n_samples {
572 dist[i][i] = 0.0;
573 for &(j, d) in &graph[i] {
574 dist[i][j] = d;
575 dist[j][i] = d; }
577 }
578
579 for k in 0..n_samples {
581 for i in 0..n_samples {
582 for j in 0..n_samples {
583 if dist[i][k] + dist[k][j] < dist[i][j] {
584 dist[i][j] = dist[i][k] + dist[k][j];
585 }
586 }
587 }
588 }
589
590 dist
591 }
592
593 pub fn fit_transform(&mut self, x: &Tensor) -> Tensor {
594 let x_data = x.data_f32();
595 let n_samples = x.dims()[0];
596 let n_features = x.dims()[1];
597
598 let graph = self.compute_knn_graph(&x_data, n_samples, n_features);
600
601 let geodesic_dist = self.floyd_warshall(&graph, n_samples);
603
604 let mut mds = MDS::new(self.n_components);
606 mds.dissimilarity = Dissimilarity::Precomputed;
607
608 let dist_flat: Vec<f32> = geodesic_dist.iter().flat_map(|row| row.clone()).collect();
610 let dist_tensor = Tensor::from_slice(&dist_flat, &[n_samples, n_samples]).unwrap();
611
612 let embedding = mds.fit_transform(&dist_tensor);
613
614 self.embedding_ = Some(embedding.data_f32());
615 self.reconstruction_error_ = mds.stress_;
616
617 embedding
618 }
619}
620
621
622pub struct LocallyLinearEmbedding {
624 pub n_components: usize,
625 pub n_neighbors: usize,
626 pub reg: f32,
627 pub method: LLEMethod,
628 embedding_: Option<Vec<f32>>,
629 #[allow(dead_code)]
630 reconstruction_error_: Option<f32>,
631}
632
633#[derive(Clone, Copy, Debug)]
634pub enum LLEMethod {
635 Standard,
636 Modified,
637 HLLE,
638 LTSA,
639}
640
641impl LocallyLinearEmbedding {
642 pub fn new(n_components: usize) -> Self {
643 LocallyLinearEmbedding {
644 n_components,
645 n_neighbors: 5,
646 reg: 1e-3,
647 method: LLEMethod::Standard,
648 embedding_: None,
649 reconstruction_error_: None,
650 }
651 }
652
653 pub fn n_neighbors(mut self, n: usize) -> Self {
654 self.n_neighbors = n;
655 self
656 }
657
658 fn find_neighbors(&self, x: &[f32], n_samples: usize, n_features: usize) -> Vec<Vec<usize>> {
659 let mut neighbors = vec![Vec::new(); n_samples];
660
661 for i in 0..n_samples {
662 let mut distances: Vec<(usize, f32)> = (0..n_samples)
663 .filter(|&j| j != i)
664 .map(|j| {
665 let mut dist_sq = 0.0f32;
666 for k in 0..n_features {
667 let diff = x[i * n_features + k] - x[j * n_features + k];
668 dist_sq += diff * diff;
669 }
670 (j, dist_sq)
671 })
672 .collect();
673
674 distances.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
675 neighbors[i] = distances.into_iter().take(self.n_neighbors).map(|(j, _)| j).collect();
676 }
677
678 neighbors
679 }
680
681 fn compute_weights(&self, x: &[f32], neighbors: &[Vec<usize>], n_samples: usize, n_features: usize) -> Vec<Vec<f32>> {
682 let mut weights = vec![vec![0.0f32; n_samples]; n_samples];
683
684 for i in 0..n_samples {
685 let k = neighbors[i].len();
686 if k == 0 {
687 continue;
688 }
689
690 let mut c = vec![0.0f32; k * k];
692
693 for (a, &ja) in neighbors[i].iter().enumerate() {
694 for (b, &jb) in neighbors[i].iter().enumerate() {
695 let mut dot = 0.0f32;
696 for f in 0..n_features {
697 let diff_a = x[ja * n_features + f] - x[i * n_features + f];
698 let diff_b = x[jb * n_features + f] - x[i * n_features + f];
699 dot += diff_a * diff_b;
700 }
701 c[a * k + b] = dot;
702 }
703 }
704
705 let trace: f32 = (0..k).map(|a| c[a * k + a]).sum();
707 let reg = self.reg * trace / k as f32;
708 for a in 0..k {
709 c[a * k + a] += reg;
710 }
711
712 let ones = vec![1.0f32; k];
714 let w = self.solve_linear_system(&c, &ones, k);
715
716 let sum: f32 = w.iter().sum();
718 for (a, &ja) in neighbors[i].iter().enumerate() {
719 weights[i][ja] = w[a] / sum.max(1e-10);
720 }
721 }
722
723 weights
724 }
725
726 fn solve_linear_system(&self, a: &[f32], b: &[f32], n: usize) -> Vec<f32> {
727 let mut aug = vec![0.0f32; n * (n + 1)];
729 for i in 0..n {
730 for j in 0..n {
731 aug[i * (n + 1) + j] = a[i * n + j];
732 }
733 aug[i * (n + 1) + n] = b[i];
734 }
735
736 for i in 0..n {
738 let mut max_row = i;
740 for k in (i + 1)..n {
741 if aug[k * (n + 1) + i].abs() > aug[max_row * (n + 1) + i].abs() {
742 max_row = k;
743 }
744 }
745
746 for j in 0..=n {
748 let tmp = aug[i * (n + 1) + j];
749 aug[i * (n + 1) + j] = aug[max_row * (n + 1) + j];
750 aug[max_row * (n + 1) + j] = tmp;
751 }
752
753 let pivot = aug[i * (n + 1) + i];
755 if pivot.abs() < 1e-10 {
756 continue;
757 }
758
759 for k in (i + 1)..n {
760 let factor = aug[k * (n + 1) + i] / pivot;
761 for j in i..=n {
762 aug[k * (n + 1) + j] -= factor * aug[i * (n + 1) + j];
763 }
764 }
765 }
766
767 let mut x = vec![0.0f32; n];
769 for i in (0..n).rev() {
770 let mut sum = aug[i * (n + 1) + n];
771 for j in (i + 1)..n {
772 sum -= aug[i * (n + 1) + j] * x[j];
773 }
774 let pivot = aug[i * (n + 1) + i];
775 x[i] = if pivot.abs() > 1e-10 { sum / pivot } else { 0.0 };
776 }
777
778 x
779 }
780
781 pub fn fit_transform(&mut self, x: &Tensor) -> Tensor {
782 let x_data = x.data_f32();
783 let n_samples = x.dims()[0];
784 let n_features = x.dims()[1];
785
786 let neighbors = self.find_neighbors(&x_data, n_samples, n_features);
788
789 let weights = self.compute_weights(&x_data, &neighbors, n_samples, n_features);
791
792 let mut m = vec![0.0f32; n_samples * n_samples];
794
795 for i in 0..n_samples {
796 for j in 0..n_samples {
797 let mut val = 0.0f32;
798
799 for k in 0..n_samples {
800 let ik = if i == k { 1.0 } else { 0.0 } - weights[k][i];
801 let jk = if j == k { 1.0 } else { 0.0 } - weights[k][j];
802 val += ik * jk;
803 }
804
805 m[i * n_samples + j] = val;
806 }
807 }
808
809 let mut y = vec![0.0f32; n_samples * self.n_components];
811 let mut rng = thread_rng();
812
813 let max_eig = 2.0f32; for i in 0..n_samples {
817 m[i * n_samples + i] = max_eig - m[i * n_samples + i];
818 for j in 0..n_samples {
819 if i != j {
820 m[i * n_samples + j] = -m[i * n_samples + j];
821 }
822 }
823 }
824
825 for comp in 0..(self.n_components + 1) {
827 let mut v: Vec<f32> = (0..n_samples).map(|_| rng.gen::<f32>()).collect();
828 let norm: f32 = v.iter().map(|&x| x * x).sum::<f32>().sqrt();
829 for vi in &mut v {
830 *vi /= norm;
831 }
832
833 for _ in 0..100 {
834 let mut w = vec![0.0f32; n_samples];
835 for i in 0..n_samples {
836 for j in 0..n_samples {
837 w[i] += m[i * n_samples + j] * v[j];
838 }
839 }
840
841 let norm: f32 = w.iter().map(|&x| x * x).sum::<f32>().sqrt();
842 if norm < 1e-10 {
843 break;
844 }
845 for wi in &mut w {
846 *wi /= norm;
847 }
848
849 let diff: f32 = v.iter().zip(w.iter()).map(|(&vi, &wi)| (vi - wi).abs()).sum();
850 v = w;
851
852 if diff < 1e-6 {
853 break;
854 }
855 }
856
857 if comp > 0 {
859 for i in 0..n_samples {
860 y[i * self.n_components + (comp - 1)] = v[i];
861 }
862 }
863
864 let mut eigenvalue = 0.0f32;
866 for i in 0..n_samples {
867 let mut mv = 0.0f32;
868 for j in 0..n_samples {
869 mv += m[i * n_samples + j] * v[j];
870 }
871 eigenvalue += v[i] * mv;
872 }
873
874 for i in 0..n_samples {
875 for j in 0..n_samples {
876 m[i * n_samples + j] -= eigenvalue * v[i] * v[j];
877 }
878 }
879 }
880
881 self.embedding_ = Some(y.clone());
882
883 Tensor::from_slice(&y, &[n_samples, self.n_components]).unwrap()
884 }
885}
886
887#[cfg(test)]
888mod tests {
889 use super::*;
890
891 #[test]
892 fn test_tsne() {
893 let x = Tensor::from_slice(&[
894 0.0, 0.0,
895 0.1, 0.1,
896 5.0, 5.0,
897 5.1, 5.1,
898 ], &[4, 2]).unwrap();
899
900 let mut tsne = TSNE::new(2).perplexity(2.0).n_iter(100);
901 let embedding = tsne.fit_transform(&x);
902
903 assert_eq!(embedding.dims(), &[4, 2]);
904 }
905
906 #[test]
907 fn test_mds() {
908 let x = Tensor::from_slice(&[
909 0.0, 0.0,
910 1.0, 0.0,
911 0.0, 1.0,
912 1.0, 1.0,
913 ], &[4, 2]).unwrap();
914
915 let mut mds = MDS::new(2);
916 let embedding = mds.fit_transform(&x);
917
918 assert_eq!(embedding.dims(), &[4, 2]);
919 }
920
921 #[test]
922 fn test_isomap() {
923 let x = Tensor::from_slice(&[
924 0.0, 0.0,
925 1.0, 0.0,
926 2.0, 0.0,
927 0.0, 1.0,
928 1.0, 1.0,
929 ], &[5, 2]).unwrap();
930
931 let mut isomap = Isomap::new(2).n_neighbors(3);
932 let embedding = isomap.fit_transform(&x);
933
934 assert_eq!(embedding.dims(), &[5, 2]);
935 }
936}