legume_numeric/matrix/
dmatrix_rsvd.rs1use crate::matrix::traits::*;
2use nalgebra::{DMatrix, DVector};
3use nalgebra_sparse::{csc::CscMatrix, csr::CsrMatrix};
4
5const RSVD_SUBSPACE_SEED: u64 = 0x5253_5644_5342_5350; pub fn nystrom_basis(u: &DMatrix<f32>, s: &DVector<f32>) -> DMatrix<f32> {
16 let eps = 1e-8;
17 let sinv = DVector::from_iterator(s.len(), s.iter().map(|&si| 1.0 / (si + eps)));
18 u * DMatrix::from_diagonal(&sinv)
19}
20
21trait IntoDense<OutMat> {
22 fn matmul(&self, other: &OutMat) -> OutMat;
23 fn transpose_matmul(&self, other: &OutMat) -> OutMat;
24 fn num_rows(&self) -> usize;
25 fn num_columns(&self) -> usize;
26}
27
28impl<T> IntoDense<DMatrix<T>> for DMatrix<T>
29where
30 T: nalgebra::RealField + num_traits::Float + Copy,
31{
32 fn matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
33 self * other
34 }
35
36 fn transpose_matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
37 self.transpose() * other
38 }
39
40 fn num_rows(&self) -> usize {
41 self.nrows()
42 }
43 fn num_columns(&self) -> usize {
44 self.ncols()
45 }
46}
47
48impl<T> IntoDense<DMatrix<T>> for CscMatrix<T>
49where
50 T: nalgebra::RealField + num_traits::Float + Copy,
51{
52 fn matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
53 self * other
54 }
55 fn transpose_matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
56 self.transpose() * other
57 }
58 fn num_rows(&self) -> usize {
59 self.nrows()
60 }
61 fn num_columns(&self) -> usize {
62 self.ncols()
63 }
64}
65
66impl<T> IntoDense<DMatrix<T>> for CsrMatrix<T>
67where
68 T: nalgebra::RealField + num_traits::Float + Copy,
69{
70 fn matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
71 self * other
72 }
73 fn transpose_matmul(&self, other: &DMatrix<T>) -> DMatrix<T> {
74 self.transpose() * other
75 }
76 fn num_rows(&self) -> usize {
77 self.nrows()
78 }
79 fn num_columns(&self) -> usize {
80 self.ncols()
81 }
82}
83
84fn _subspace_iteration<T, D>(xx: &D, rank_and_oversample: usize) -> anyhow::Result<DMatrix<T>>
85where
86 T: nalgebra::RealField + num_traits::Float + Copy,
87 D: IntoDense<DMatrix<T>>,
88{
89 let max_iter = 5; let nc = xx.num_columns();
92 let mut qq = DMatrix::<T>::runif_seeded(nc, rank_and_oversample, RSVD_SUBSPACE_SEED);
98 let half = T::from(0.5).expect("no half found");
99 qq.iter_mut().for_each(|x| *x -= half);
100
101 for _i in 0..max_iter {
106 let ll = xx.matmul(&qq).qr().q();
107 qq = xx.transpose_matmul(&ll).qr().q();
108 }
109
110 let qr_q = xx.matmul(&qq).qr().q();
113 let kk = rank_and_oversample.min(qr_q.ncols());
114 let ret = qr_q.columns(0, kk).into_owned();
115
116 Ok(ret)
117}
118
119fn _randomized_svd<T, D>(
120 xx: &D,
121 max_rank: usize,
122) -> anyhow::Result<(DMatrix<T>, DVector<T>, DMatrix<T>)>
123where
124 T: nalgebra::RealField + num_traits::Float + Copy,
125 D: IntoDense<DMatrix<T>>,
126{
127 let nr = xx.num_rows();
128 let nc = xx.num_columns();
129
130 let mut rank = nr.min(nc);
131 let mut oversample = 0;
132
133 if max_rank > 0 && rank > max_rank {
134 rank = max_rank;
135 oversample = 5;
136 }
137
138 debug_assert!(rank > 0, "Must be at least rank = 1");
139
140 let qq = _subspace_iteration(xx, rank + oversample)?;
144 let rank = rank.min(qq.ncols());
145
146 let bb = xx.transpose_matmul(&qq).transpose();
148
149 let svd = bb.svd(true, true);
150
151 if let (Some(svd_u), Some(svd_vt)) = (svd.u, svd.v_t) {
152 return Ok((
153 qq.clone() * svd_u.columns(0, rank).into_owned(),
154 svd.singular_values.rows(0, rank).into_owned(),
155 svd_vt.transpose().columns(0, rank).into_owned(),
156 ));
157 }
158 Err(anyhow::anyhow!("randomized SVD failed"))
159}
160
161impl<T> RandomizedAlgs for DMatrix<T>
162where
163 T: nalgebra::RealField + num_traits::Float + Copy,
164{
165 type InMat = DMatrix<T>;
166 type OutMat = DMatrix<T>;
167 type DVec = DVector<T>;
168 type Scalar = T;
169
170 fn rsvd(&self, max_rank: usize) -> anyhow::Result<(Self::OutMat, Self::DVec, Self::OutMat)> {
171 _randomized_svd(self, max_rank)
172 }
173}
174
175impl<T> RandomizedAlgs for CscMatrix<T>
176where
177 T: nalgebra::RealField + num_traits::Float + Copy,
178{
179 type InMat = CscMatrix<T>;
180 type OutMat = DMatrix<T>;
181 type DVec = DVector<T>;
182 type Scalar = T;
183
184 fn rsvd(&self, max_rank: usize) -> anyhow::Result<(Self::OutMat, Self::DVec, Self::OutMat)> {
185 _randomized_svd(self, max_rank)
186 }
187}
188
189impl<T> RandomizedAlgs for CsrMatrix<T>
190where
191 T: nalgebra::RealField + num_traits::Float + Copy,
192{
193 type InMat = CsrMatrix<T>;
194 type OutMat = DMatrix<T>;
195 type DVec = DVector<T>;
196 type Scalar = T;
197
198 fn rsvd(&self, max_rank: usize) -> anyhow::Result<(Self::OutMat, Self::DVec, Self::OutMat)> {
199 _randomized_svd(self, max_rank)
200 }
201}
202
203#[cfg(test)]
204#[path = "dmatrix_rsvd_tests.rs"]
205mod tests;