1use crate::error::PaCMAPError;
8use flow_dimensional_reduction::Pca;
9
10pub fn pca_init(data: &[f32], n: usize, d: usize) -> Result<Vec<[f32; 2]>, PaCMAPError> {
18 debug_assert_eq!(data.len(), n * d);
19
20 let pca = Pca::new(2).fit(data, n, d)?;
21 let flat = pca.transform(data, n, d)?;
22 let k = pca.n_components();
23
24 Ok(flat
27 .chunks(k)
28 .map(|c| [c[0], if k >= 2 { c[1] } else { 0.0 }])
29 .collect())
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn pca_separates_axis_aligned_clusters() {
38 let mut data: Vec<f32> = Vec::new();
40 for _ in 0..50 {
41 data.extend_from_slice(&[0.0_f32, 0.0]);
42 }
43 for _ in 0..50 {
44 data.extend_from_slice(&[10.0_f32, 0.0]);
45 }
46 let emb = pca_init(&data, 100, 2).unwrap();
47 let mean_left_x = emb[..50].iter().map(|p| p[0]).sum::<f32>() / 50.0;
49 let mean_right_x = emb[50..].iter().map(|p| p[0]).sum::<f32>() / 50.0;
50 assert!(
51 (mean_left_x - mean_right_x).abs() > 1.0,
52 "PCA should separate the two clusters along PC1"
53 );
54 }
55}