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