clarabel/algebra/dense/
matrix_math.rs

1#![allow(non_snake_case)]
2use crate::algebra::*;
3
4
5// PJG : MatrixMath<T> should be implemented for a more 
6// general type, e.g. the DenseStorageMatrix<S,T> type 
7// or similar.   That would provide math functionality
8// for more types, e.g. statically size matrices or 
9// the ones on borrowed data.   
10
11
12impl<T: FloatT> MatrixMath<T> for Matrix<T> {
13
14    fn col_sums(&self, sums: &mut [T]) {
15        assert_eq!(self.ncols(), sums.len());
16        for (col, sum) in sums.iter_mut().enumerate() {
17            *sum = self.col_slice(col).sum();
18        }
19    }
20
21    fn row_sums(&self, sums: &mut [T]) {
22        assert_eq!(self.nrows(), sums.len());
23        sums.fill(T::zero());
24        for col in 0..self.ncols() {
25            let slice = self.col_slice(col);
26            for (row, &v) in slice.iter().enumerate() {
27                sums[row] += v;
28            }
29        }
30    }
31
32    fn col_norms(&self, norms: &mut [T]) {
33        norms.fill(T::zero());
34        self.col_norms_no_reset(norms);
35    }
36
37    fn col_norms_no_reset(&self, norms: &mut [T]) {
38        for (i, norm) in norms.iter_mut().enumerate() {
39            let colnorm = self.col_slice(i).norm_inf();
40            *norm = T::max(*norm, colnorm);
41        }
42    }
43
44    fn col_norms_sym(&self, norms: &mut [T]) {
45        norms.fill(T::zero());
46        self.col_norms_sym_no_reset(norms);
47    }
48
49    fn col_norms_sym_no_reset(&self, norms: &mut [T]) {
50        for c in 0..self.ncols() {
51            for r in 0..=c {
52                let tmp = self[(r, c)];
53                norms[r] = T::max(norms[r], tmp);
54                norms[c] = T::max(norms[c], tmp);
55            }
56        }
57    }
58
59    fn row_norms(&self, norms: &mut [T]) {
60        norms.fill(T::zero());
61        self.row_norms_no_reset(norms);
62    }
63
64    fn row_norms_no_reset(&self, norms: &mut [T]) {
65        for r in 0..self.nrows() {
66            for c in 0..self.ncols() {
67                norms[r] = T::max(norms[r], T::abs(self[(r, c)]))
68            }
69        }
70    }
71}
72
73impl<T: FloatT> MatrixMathMut<T> for Matrix<T> {
74
75    //scalar mut operations
76    fn scale(&mut self, c: T) {
77        self.data.scale(c);
78    }
79
80    fn negate(&mut self) {
81        self.data.negate();
82    }
83
84    fn lscale(&mut self, l: &[T]) {
85        for col in 0..self.ncols() {
86            self.col_slice_mut(col).hadamard(l);
87        }
88    }
89
90    fn rscale(&mut self, r: &[T]) {
91        for (col, val) in r.iter().enumerate() {
92            self.col_slice_mut(col).scale(*val);
93        }
94    }
95
96    fn lrscale(&mut self, l: &[T], r: &[T]) {
97        for i in 0..self.nrows() {
98            for j in 0..self.ncols() {
99                self[(i, j)] *= l[i] * r[j];
100            }
101        }
102    }
103
104}
105
106impl<T> Matrix<T>
107where
108    T: FloatT,
109{
110    #[allow(dead_code)]
111    pub(crate) fn kron<MATA, MATB>(&mut self, A: &MATA, B: &MATB)
112    where
113        MATA: DenseMatrix<T>,
114        MATB: DenseMatrix<T>,
115    {
116        let (pp, qq) = A.size();
117        let (rr, ss) = B.size();
118        assert!(self.nrows() == pp * rr);
119        assert!(self.ncols() == qq * ss);
120
121        let mut i = 0;
122        for q in 0..qq {
123            for s in 0..ss {
124                for p in 0..pp {
125                    let Apq = A[(p, q)];
126                    for r in 0..rr {
127                        self.data_mut()[i] = (Apq) * B[(r, s)];
128                        i += 1;
129                    }
130                }
131            }
132        }
133    }
134}
135
136
137// additional functions that require floating point operations
138
139// allow dead code here since dense matrix and its supporting
140// functionality could eventually become a public interface.
141#[allow(dead_code)]
142impl<S,T> DenseStorageMatrix<S, T> 
143where 
144    T: FloatT,
145    S: AsRef<[T]> + AsMut<[T]>
146{
147    /// Set A = (A + A') / 2.  Assumes A is real
148    pub fn symmetric_part(&mut self) -> &mut Self {
149        assert!(self.is_square());
150        let half: T = (0.5_f64).as_T();
151
152        for r in 0..self.nrows() {
153            for c in 0..r {
154                let val = half * (self[(r, c)] + self[(c, r)]);
155                self[(c, r)] = val;
156                self[(r, c)] = val;
157            }
158        }
159        self
160    }
161}
162
163
164
165pub(crate) fn svec_to_mat<S,T>(M: &mut DenseStorageMatrix<S,T>, x: &[T]) 
166where
167    T: FloatT,
168    S: AsRef<[T]> + AsMut<[T]>,
169{
170    let mut idx = 0;
171    for col in 0..M.ncols() {
172        for row in 0..=col {
173            if row == col {
174                M[(row, col)] = x[idx];
175            } else {
176                M[(row, col)] = x[idx] * T::FRAC_1_SQRT_2();
177                M[(col, row)] = x[idx] * T::FRAC_1_SQRT_2();
178            }
179            idx += 1;
180        }
181    }
182}
183
184//PJG : Perhaps implementation for Symmetric type would be faster
185pub(crate) fn mat_to_svec<T,MATM>(x: &mut [T], M: &MATM)
186where 
187MATM: DenseMatrix<T>,
188     T: FloatT,
189{
190    let mut idx = 0;
191    for col in 0..M.ncols() {
192        for row in 0..=col {
193            x[idx] = {
194                if row == col {
195                    M[(row, col)]
196                } else {
197                    (M[(row, col)] + M[(col, row)]) * T::FRAC_1_SQRT_2()
198                }
199            };
200            idx += 1;
201        }
202    }
203}
204
205
206#[test]
207fn test_row_col_sums_and_norms() {
208    #[rustfmt::skip]
209    let A = Matrix::from(&[
210        [-1.,  4.,  6.], 
211        [ 3., -8.,  7.], 
212        [ 0.,  4.,  9.],
213    ]);
214
215    let mut rsums = vec![0.0; 3];
216    let mut csums = vec![0.0; 3];
217
218    A.row_sums(&mut rsums);
219    assert_eq!(rsums, [9.0, 2.0, 13.0]);
220    A.col_sums(&mut csums);
221    assert_eq!(csums, [2.0, 0.0, 22.0]);
222
223    let mut rnorms = vec![0.0; 3];
224    let mut cnorms = vec![0.0; 3];
225
226    A.row_norms(&mut rnorms);
227    assert!(rnorms == [6.0, 8.0, 9.0]);
228    A.col_norms(&mut cnorms);
229    assert!(cnorms == [3.0, 8.0, 9.0]);
230
231    //no reset versions
232    let mut rnorms = vec![0.0; 3];
233    let mut cnorms = vec![0.0; 3];
234    rnorms[2] = 100.;
235    cnorms[2] = 100.;
236
237    A.row_norms_no_reset(&mut rnorms);
238    assert!(rnorms == [6.0, 8.0, 100.0]);
239    A.col_norms_no_reset(&mut cnorms);
240    assert!(cnorms == [3.0, 8.0, 100.0]);
241}
242
243#[test]
244#[rustfmt::skip]
245fn test_l_r_scalings() {
246
247    let A = Matrix::from(&[
248        [-1.,  4.,  6.], 
249        [ 3., -8.,  7.], 
250        [ 0.,  4.,  9.],
251    ]);
252
253    let lscale = vec![1., -2., 3.];
254    let rscale = vec![-2., 1., -3.];
255
256    //right scale
257    let mut B = A.clone();
258    B.rscale(&rscale);
259    let Btest = Matrix::from(&[
260        [ 2.,  4.,  -18.], 
261        [-6., -8.,  -21.], 
262        [ 0.,  4.,  -27.],
263    ]);
264    assert_eq!(B,Btest);
265
266    //left scale
267    let mut B = A.clone();
268    B.lscale(&lscale);
269    let Btest = Matrix::from(&[
270        [-1.,  4.,   6.], 
271        [-6., 16., -14.], 
272        [ 0., 12.,  27.],
273    ]);
274    assert_eq!(B,Btest);
275
276    //left-right scale
277    let mut B = A;
278    B.lrscale(&lscale, &rscale);
279    let Btest = Matrix::from(&[
280        [ 2.,  4., -18.], 
281        [12., 16.,  42.], 
282        [ 0., 12., -81.],
283    ]);
284    assert_eq!(B,Btest);
285}
286
287#[test]
288fn test_symmetric_part() {
289
290    let mut A = Matrix::from(&[
291        [-1.,  4.,  6.],
292        [ 2., -8.,  8.],
293        [ 0.,  4.,  9.],
294    ]);
295
296    let B = Matrix::from(&[
297        [-1.,  3.,  3.],
298        [ 3., -8.,  6.],
299        [ 3.,  6.,  9.],
300    ]);
301
302    A.symmetric_part();
303    assert_eq!(B,A);
304}
305
306#[test]
307fn test_col_norms_sym() {
308
309    let A = Matrix::from(&[
310        [-1.,  4.,  6.],
311        [ 2., -8.,  8.],
312        [ 0.,  4.,  9.],
313    ]);
314
315    let mut v = vec![0.0; 3];
316    A.col_norms_sym(&mut v);
317    assert_eq!(v, [6.0, 8.0, 9.0]);
318}
319
320
321
322#[test]
323#[rustfmt::skip]
324fn test_kron() {
325
326    let A = Matrix::from(
327        &[[ 1.,  2.],
328          [ 4.,  5.]]);
329
330    let B = Matrix::from(
331        &[[ 1.,  2.]]);
332
333
334    // A ⊗ B
335    let (k1, m1) = A.size();
336    let (k2, m2) = B.size();
337    let mut K = Matrix::<f64>::zeros((k1 * k2, m1 * m2));
338    K.kron(&A, &B);
339
340    let Ktest = Matrix::from(
341        &[[ 1.,  2.,  2.,  4.],
342          [ 4.,  8.,  5., 10.]]);
343
344    assert_eq!(K,Ktest);
345
346    // A' ⊗ B
347    let (k1, m1) = A.t().size();
348    let (k2, m2) = B.size();
349    let mut K = Matrix::<f64>::zeros((k1 * k2, m1 * m2));
350    K.kron(&A.t(), &B);
351
352    let Ktest = Matrix::from(
353        &[[ 1.,  2.,  4.,  8.],
354          [ 2.,  4.,  5., 10.]]);
355          
356    assert_eq!(K,Ktest);
357
358    // A ⊗ B'            
359    let (k1, m1) = A.size();
360    let (k2, m2) = B.t().size();
361    let mut K = Matrix::<f64>::zeros((k1 * k2, m1 * m2));
362    K.kron(&A, &B.t());
363
364    let Ktest = Matrix::from(
365        &[[1., 2. ],
366          [2., 4. ],
367          [4., 5. ],
368          [8., 10.]]);
369          
370    assert_eq!(K,Ktest);
371
372    // A' ⊗ B'  
373    let (k1, m1) = A.t().size();
374    let (k2, m2) = B.t().size();  
375    let mut K = Matrix::<f64>::zeros((k1 * k2, m1 * m2));  
376    K.kron(&A.t(), &B.t());
377
378    let Ktest = Matrix::from(
379        &[[1., 4. ],
380          [2., 8. ],
381          [2., 5. ],
382          [4., 10.]]);
383
384    assert_eq!(K,Ktest);
385}
386
387
388
389#[test]
390fn test_svec_conversions() {
391    let n = 3;
392
393    let X = Matrix::from(&[
394        [1., 3., -2.], //
395        [3., -4., 7.], //
396        [-2., 7., 5.], //
397    ]);
398
399    let Y = Matrix::from(&[
400        [2., 5., -4.],  //
401        [5., 6., 2.],   //
402        [-4., 2., -3.], //
403    ]);
404
405    let mut Z = Matrix::zeros((3, 3));
406
407    let mut x = vec![0.; triangular_number(n)];
408    let mut y = vec![0.; triangular_number(n)];
409
410    // check inner product identity
411    mat_to_svec(&mut x, &X);
412    mat_to_svec(&mut y, &Y);
413
414    assert!(f64::abs(x.dot(&y) - X.data().dot(Y.data())) < 1e-12);
415
416    // check round trip
417    mat_to_svec(&mut x, &X);
418    svec_to_mat(&mut Z, &x);
419    assert!(X.data().norm_inf_diff(Z.data()) < 1e-12);
420}