single_svdlib/
utils.rs

1use ndarray::{Array1, Array2};
2use num_traits::{Float, FromPrimitive, One, Zero};
3use std::fmt::Debug;
4use std::iter::Sum;
5use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
6
7pub fn determine_chunk_size(nrows: usize) -> usize {
8    let num_threads = rayon::current_num_threads();
9
10    let min_rows_per_thread = 16;
11    let desired_chunks_per_thread = 4;
12
13    let target_total_chunks = num_threads * desired_chunks_per_thread;
14    let chunk_size = nrows.div_ceil(target_total_chunks);
15
16    chunk_size.max(min_rows_per_thread)
17}
18
19pub trait SMat<T: Float>: Sync {
20    fn nrows(&self) -> usize;
21    fn ncols(&self) -> usize;
22    fn nnz(&self) -> usize;
23    fn svd_opa(&self, x: &[T], y: &mut [T], transposed: bool); // y = A*x
24}
25
26/// Singular Value Decomposition Components
27///
28/// # Fields
29/// - d:  Dimensionality (rank), the number of rows of both `ut`, `vt` and the length of `s`
30/// - ut: Transpose of left singular vectors, the vectors are the rows of `ut`
31/// - s:  Singular values (length `d`)
32/// - vt: Transpose of right singular vectors, the vectors are the rows of `vt`
33/// - diagnostics: Computational diagnostics
34#[derive(Debug, Clone, PartialEq)]
35pub struct SvdRec<T: Float> {
36    pub d: usize,
37    pub ut: Array2<T>,
38    pub s: Array1<T>,
39    pub vt: Array2<T>,
40    pub diagnostics: Diagnostics<T>,
41}
42
43/// Computational Diagnostics
44///
45/// # Fields
46/// - non_zero:  Number of non-zeros in the matrix
47/// - dimensions: Number of dimensions attempted (bounded by matrix shape)
48/// - iterations: Number of iterations attempted (bounded by dimensions and matrix shape)
49/// - transposed:  True if the matrix was transposed internally
50/// - lanczos_steps: Number of Lanczos steps performed
51/// - ritz_values_stabilized: Number of ritz values
52/// - significant_values: Number of significant values discovered
53/// - singular_values: Number of singular values returned
54/// - end_interval: left, right end of interval containing unwanted eigenvalues
55/// - kappa: relative accuracy of ritz values acceptable as eigenvalues
56/// - random_seed: Random seed provided or the seed generated
57#[derive(Debug, Clone, PartialEq)]
58pub struct Diagnostics<T: Float> {
59    pub non_zero: usize,
60    pub dimensions: usize,
61    pub iterations: usize,
62    pub transposed: bool,
63    pub lanczos_steps: usize,
64    pub ritz_values_stabilized: usize,
65    pub significant_values: usize,
66    pub singular_values: usize,
67    pub end_interval: [T; 2],
68    pub kappa: T,
69    pub random_seed: u32,
70}
71
72pub trait SvdFloat:
73    Float
74    + FromPrimitive
75    + Debug
76    + Send
77    + Sync
78    + Zero
79    + One
80    + AddAssign
81    + SubAssign
82    + MulAssign
83    + Neg<Output = Self>
84    + Sum
85{
86    fn eps() -> Self;
87    fn eps34() -> Self;
88    fn compare(a: Self, b: Self) -> bool;
89}
90
91impl SvdFloat for f32 {
92    fn eps() -> Self {
93        f32::EPSILON
94    }
95
96    fn eps34() -> Self {
97        f32::EPSILON.powf(0.75)
98    }
99
100    fn compare(a: Self, b: Self) -> bool {
101        (b - a).abs() < f32::EPSILON
102    }
103}
104
105impl SvdFloat for f64 {
106    fn eps() -> Self {
107        f64::EPSILON
108    }
109
110    fn eps34() -> Self {
111        f64::EPSILON.powf(0.75)
112    }
113
114    fn compare(a: Self, b: Self) -> bool {
115        (b - a).abs() < f64::EPSILON
116    }
117}