rusty_compression/
random_matrix.rs1use ndarray::Array2;
4use ndarray_linalg::{Lapack, SVDDCInto, Scalar, UVTFlag};
5use num::complex::Complex;
6use num::traits::cast::cast;
7use num::Float;
8use rand::Rng;
9use rand_distr::{Distribution, Normal};
10
11pub trait RandomMatrix
12where
13 Self: Scalar + Lapack,
14{
15 fn random_gaussian<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<Self>;
22
23 fn random_orthogonal_matrix<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<Self> {
36 let mut m = dimension.0;
37 let mut n = dimension.1;
38
39 if dimension.1 > dimension.0 {
41 std::mem::swap(&mut m, &mut n);
42 }
43
44 let mat = Self::random_gaussian((m, n), rng);
45
46 let (u, _, _) = mat
47 .svddc_into(UVTFlag::Some)
48 .expect("`compress_svd_rank_based`: SVD computation failed.");
49
50 if dimension.1 > dimension.0 {
52 u.unwrap().t().map(|item| item.conj())
53 } else {
54 u.unwrap()
55 }
56 }
57
58 fn random_approximate_low_rank_matrix<R: Rng>(
71 dimension: (usize, usize),
72 sigma_max: f64,
73 sigma_min: f64,
74 rng: &mut R,
75 ) -> Array2<Self> {
76 use ndarray::Array;
77
78 assert!(
79 sigma_min < sigma_max,
80 "`sigma_min` must be smaller than `sigma_max`"
81 );
82 assert!(sigma_min > 0.0, "`sigma_min` must be positive.");
83
84 let min_dim = std::cmp::min(dimension.0, dimension.1);
85
86 let u = Self::random_orthogonal_matrix((dimension.0, min_dim), rng);
87 let vt = Self::random_orthogonal_matrix((min_dim, dimension.1), rng);
88 let singvals = Array::geomspace(sigma_min, sigma_max, min_dim)
89 .unwrap()
90 .map(|&item| cast::<f64, Self>(item).unwrap());
91 let sigma = Array2::from_diag(&singvals);
92 u.dot(&sigma.dot(&vt))
93 }
94}
95
96impl RandomMatrix for f64 {
97 fn random_gaussian<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<f64> {
98 random_gaussian_real::<f64, R>(dimension, rng)
99 }
100}
101
102impl RandomMatrix for f32 {
103 fn random_gaussian<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<f32> {
104 random_gaussian_real::<f32, R>(dimension, rng)
105 }
106}
107
108impl RandomMatrix for Complex<f64> {
109 fn random_gaussian<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<Complex<f64>> {
110 random_gaussian_complex::<f64, R>(dimension, rng)
111 }
112}
113
114impl RandomMatrix for Complex<f32> {
115 fn random_gaussian<R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<Complex<f32>> {
116 random_gaussian_complex::<f32, R>(dimension, rng)
117 }
118}
119
120fn random_gaussian_real<T: Float, R: Rng>(dimension: (usize, usize), rng: &mut R) -> Array2<T> {
121 let mut mat = Array2::<T>::zeros(dimension);
122 let normal = Normal::new(0.0, 1.0).unwrap();
123 mat.map_inplace(|item| *item = cast::<f64, T>(normal.sample(rng)).unwrap());
124 mat
125}
126
127fn random_gaussian_complex<T: Float, R: Rng>(
134 dimension: (usize, usize),
135 rng: &mut R,
136) -> Array2<Complex<T>> {
137 let mut mat = Array2::<Complex<T>>::zeros(dimension);
138 let normal = Normal::new(0.0, 1.0).unwrap();
139 mat.map_inplace(|item| {
140 let re = cast::<f64, T>(normal.sample(rng)).unwrap();
141 let im = cast::<f64, T>(normal.sample(rng)).unwrap();
142 *item = Complex::new(re, im);
143 });
144 mat
145}