single_svdlib/randomized/
mod.rs

1use crate::error::SvdLibError;
2use crate::{Diagnostics, SMat, SvdFloat, SvdRec};
3use nalgebra_sparse::na::{ComplexField, DMatrix, DVector, RealField};
4use ndarray::Array1;
5use nshare::IntoNdarray2;
6use rand::prelude::{Distribution, StdRng};
7use rand::SeedableRng;
8use rand_distr::Normal;
9use rayon::iter::ParallelIterator;
10use rayon::prelude::{IndexedParallelIterator, IntoParallelIterator};
11use std::ops::Mul;
12use crate::utils::determine_chunk_size;
13
14pub enum PowerIterationNormalizer {
15    QR,
16    LU,
17    None,
18}
19
20const PARALLEL_THRESHOLD_ROWS: usize = 5000;
21const PARALLEL_THRESHOLD_COLS: usize = 1000;
22const PARALLEL_THRESHOLD_ELEMENTS: usize = 100_000;
23
24pub fn randomized_svd<T, M>(
25    m: &M,
26    target_rank: usize,
27    n_oversamples: usize,
28    n_power_iters: usize,
29    power_iteration_normalizer: PowerIterationNormalizer,
30    seed: Option<u64>,
31) -> anyhow::Result<SvdRec<T>>
32where
33    T: SvdFloat + RealField,
34    M: SMat<T>,
35    T: ComplexField,
36{
37    let start = std::time::Instant::now(); // only for debugging
38    let m_rows = m.nrows();
39    let m_cols = m.ncols();
40
41    let rank = target_rank.min(m_rows.min(m_cols));
42    let l = rank + n_oversamples;
43    println!("Basic statistics: {:?}", start.elapsed());
44
45    let omega = generate_random_matrix(m_cols, l, seed);
46    println!("Generated Random Matrix here: {:?}", start.elapsed());
47
48    let mut y = DMatrix::<T>::zeros(m_rows, l);
49    multiply_matrix(m, &omega, &mut y, false);
50    println!(
51        "First multiplication took: {:?}, Continuing for power iterations:",
52        start.elapsed()
53    );
54
55    if n_power_iters > 0 {
56        let mut z = DMatrix::<T>::zeros(m_cols, l);
57
58        for w in 0..n_power_iters {
59            multiply_matrix(m, &y, &mut z, true);
60            println!("{}-nd power-iteration forward: {:?}", w, start.elapsed());
61            match power_iteration_normalizer {
62                PowerIterationNormalizer::QR => {
63                    let qr = z.qr();
64                    z = qr.q();
65                }
66                PowerIterationNormalizer::LU => {
67                    normalize_columns(&mut z);
68                }
69                PowerIterationNormalizer::None => {}
70            }
71            println!(
72                "{}-nd power-iteration forward, normalization: {:?}",
73                w,
74                start.elapsed()
75            );
76
77            multiply_matrix(m, &z, &mut y, false);
78            println!("{}-nd power-iteration backward: {:?}", w, start.elapsed());
79            match power_iteration_normalizer {
80                PowerIterationNormalizer::QR => {
81                    let qr = y.qr();
82                    y = qr.q();
83                }
84                PowerIterationNormalizer::LU => normalize_columns(&mut y),
85                PowerIterationNormalizer::None => {}
86            }
87            println!(
88                "{}-nd power-iteration backward, normalization: {:?}",
89                w,
90                start.elapsed()
91            );
92        }
93    }
94    println!(
95        "Finished power-iteration, continuing QR: {:?}",
96        start.elapsed()
97    );
98    let qr = y.qr();
99    println!("QR finished: {:?}", start.elapsed());
100    let q = qr.q();
101
102    let mut b = DMatrix::<T>::zeros(q.ncols(), m_cols);
103    multiply_transposed_by_matrix(&q, m, &mut b);
104    println!(
105        "QMB matrix multiplication transposed: {:?}",
106        start.elapsed()
107    );
108
109    let svd = b.svd(true, true);
110    println!("SVD decomposition took: {:?}", start.elapsed());
111    let u_b = svd
112        .u
113        .ok_or_else(|| SvdLibError::Las2Error("SVD U computation failed".to_string()))?;
114    let singular_values = svd.singular_values;
115    let vt = svd
116        .v_t
117        .ok_or_else(|| SvdLibError::Las2Error("SVD V_t computation failed".to_string()))?;
118
119    let actual_rank = target_rank.min(singular_values.len());
120
121    let u_b_subset = u_b.columns(0, actual_rank);
122    let u = q.mul(&u_b_subset);
123
124    let vt_subset = vt.rows(0, actual_rank).into_owned();
125
126    // Convert to the format required by SvdRec
127    let d = actual_rank;
128    println!("SVD Result Cropping: {:?}", start.elapsed());
129
130    let ut = u.transpose().into_ndarray2();
131    let s = convert_singular_values(
132        <DVector<T>>::from(singular_values.rows(0, actual_rank)),
133        actual_rank,
134    );
135    let vt = vt_subset.into_ndarray2();
136    println!("Translation to ndarray: {:?}", start.elapsed());
137
138    Ok(SvdRec {
139        d,
140        ut,
141        s,
142        vt,
143        diagnostics: create_diagnostics(m, d, target_rank, n_power_iters, seed.unwrap_or(0) as u32),
144    })
145}
146
147fn convert_singular_values<T: SvdFloat + ComplexField>(
148    values: DVector<T::RealField>,
149    size: usize,
150) -> Array1<T> {
151    let mut array = Array1::zeros(size);
152
153    for i in 0..size {
154        // Convert from RealField to T using f64 as intermediate
155        array[i] = T::from_real(values[i].clone());
156    }
157
158    array
159}
160
161fn create_diagnostics<T, M: SMat<T>>(
162    a: &M,
163    d: usize,
164    target_rank: usize,
165    power_iterations: usize,
166    seed: u32,
167) -> Diagnostics<T>
168where
169    T: SvdFloat,
170{
171    Diagnostics {
172        non_zero: a.nnz(),
173        dimensions: target_rank,
174        iterations: power_iterations,
175        transposed: false,
176        lanczos_steps: 0, // we dont do that
177        ritz_values_stabilized: d,
178        significant_values: d,
179        singular_values: d,
180        end_interval: [T::from(-1e-30).unwrap(), T::from(1e-30).unwrap()],
181        kappa: T::from(1e-6).unwrap(),
182        random_seed: seed,
183    }
184}
185
186fn normalize_columns<T: SvdFloat + RealField + Send + Sync>(matrix: &mut DMatrix<T>) {
187    let rows = matrix.nrows();
188    let cols = matrix.ncols();
189
190    // Use sequential processing for small matrices
191    if rows < PARALLEL_THRESHOLD_ROWS && cols < PARALLEL_THRESHOLD_COLS {
192        for j in 0..cols {
193            let mut norm = T::zero();
194
195            // Calculate column norm
196            for i in 0..rows {
197                norm += ComplexField::powi(matrix[(i, j)], 2);
198            }
199            norm = ComplexField::sqrt(norm);
200
201            // Normalize the column if the norm is not too small
202            if norm > T::from_f64(1e-10).unwrap() {
203                let scale = T::one() / norm;
204                for i in 0..rows {
205                    matrix[(i, j)] *= scale;
206                }
207            }
208        }
209        return;
210    }
211
212    let norms: Vec<T> = (0..cols)
213        .into_par_iter()
214        .map(|j| {
215            let mut norm = T::zero();
216            for i in 0..rows {
217                let val = unsafe { *matrix.get_unchecked((i, j)) };
218                norm += ComplexField::powi(val, 2);
219            }
220            ComplexField::sqrt(norm)
221        })
222        .collect();
223
224    // Now create a vector of (column_index, scale) pairs
225    let scales: Vec<(usize, T)> = norms
226        .into_iter()
227        .enumerate()
228        .filter_map(|(j, norm)| {
229            if norm > T::from_f64(1e-10).unwrap() {
230                Some((j, T::one() / norm))
231            } else {
232                None // Skip columns with too small norms
233            }
234        })
235        .collect();
236
237    // Apply normalization
238    scales.iter().for_each(|(j, scale)| {
239        for i in 0..rows {
240            let value = matrix.get_mut((i, *j)).unwrap();
241            *value = value.clone() * scale.clone();
242        }
243    });
244}
245
246// ----------------------------------------
247// Utils Functions
248// ----------------------------------------
249
250fn generate_random_matrix<T: SvdFloat + RealField>(
251    rows: usize,
252    cols: usize,
253    seed: Option<u64>,
254) -> DMatrix<T> {
255    let mut rng = match seed {
256        Some(s) => StdRng::seed_from_u64(s),
257        None => StdRng::seed_from_u64(0),
258    };
259
260    let normal = Normal::new(0.0, 1.0).unwrap();
261    DMatrix::from_fn(rows, cols, |_, _| {
262        T::from_f64(normal.sample(&mut rng)).unwrap()
263    })
264}
265
266fn multiply_matrix<T: SvdFloat, M: SMat<T>>(
267    sparse: &M,
268    dense: &DMatrix<T>,
269    result: &mut DMatrix<T>,
270    transpose_sparse: bool,
271) {
272    let cols = dense.ncols();
273
274    let results: Vec<(usize, Vec<T>)> = (0..cols)
275        .into_par_iter()
276        .map(|j| {
277            let mut col_vec = vec![T::zero(); dense.nrows()];
278            let mut result_vec = vec![T::zero(); result.nrows()];
279
280            for i in 0..dense.nrows() {
281                col_vec[i] = dense[(i, j)];
282            }
283
284            sparse.svd_opa(&col_vec, &mut result_vec, transpose_sparse);
285
286            (j, result_vec)
287        })
288        .collect();
289
290    for (j, col_result) in results {
291        for i in 0..result.nrows() {
292            result[(i, j)] = col_result[i];
293        }
294    }
295}
296
297fn multiply_transposed_by_matrix<T: SvdFloat, M: SMat<T>>(
298    q: &DMatrix<T>, 
299    sparse: &M,
300    result: &mut DMatrix<T>,
301) {
302    let q_rows = q.nrows();
303    let q_cols = q.ncols();
304    let sparse_rows = sparse.nrows();
305    let sparse_cols = sparse.ncols();
306    
307    eprintln!("Q dimensions: {} x {}", q_rows, q_cols);
308    eprintln!("Sparse dimensions: {} x {}", sparse_rows, sparse_cols);
309    eprintln!("Result dimensions: {} x {}", result.nrows(), result.ncols());
310    
311    assert_eq!(
312        q_rows, sparse_rows,
313        "Dimension mismatch: Q has {} rows but sparse has {} rows",
314        q_rows, sparse_rows
315    );
316    
317    assert_eq!(
318        result.nrows(),
319        q_cols,
320        "Result matrix has incorrect row count: expected {}, got {}",
321        q_cols,
322        result.nrows()
323    );
324    assert_eq!(
325        result.ncols(),
326        sparse_cols,
327        "Result matrix has incorrect column count: expected {}, got {}",
328        sparse_cols,
329        result.ncols()
330    );
331    
332    let chunk_size = determine_chunk_size(q_cols);
333    
334    let chunk_results: Vec<Vec<(usize, Vec<T>)>> = (0..q_cols)
335        .into_par_iter()
336        .chunks(chunk_size)
337        .map(|chunk| {
338            let mut chunk_results = Vec::with_capacity(chunk.len());
339            
340            for &col_idx in &chunk {
341                let mut q_col = vec![T::zero(); q_rows];
342                for i in 0..q_rows {
343                    q_col[i] = q[(i, col_idx)];
344                }
345                
346                let mut result_row = vec![T::zero(); sparse_cols];
347                
348                sparse.svd_opa(&q_col, &mut result_row, true);
349                
350                chunk_results.push((col_idx, result_row));
351            }
352            chunk_results
353        })
354        .collect();
355    
356    for chunk_result in chunk_results {
357        for (row_idx, row_values) in chunk_result {
358            for j in 0..sparse_cols {
359                result[(row_idx, j)] = row_values[j];
360            }
361        }
362    }
363}