Example 10: Singular Value Decomposition (SVD)
This example demonstrates computing the SVD of a matrix \(A = U \Sigma V^H\).
Key Concepts
Singular Values Only: Computing just \(\Sigma\) using
svd_vals.Full SVD: Computing \(\Sigma\), \(U\), and \(V^H\) using
svd(gesvd).Partial Vectors: Computing only \(U\) or only \(V^H\).
C++ Example
Setup (Lines 29-30)
slate::Matrix<scalar_type> A( m, n, nb, ... );
std::vector<real_t> Sigma( min_mn );
A: Input matrix m by n.
Sigma: Vector to store the singular values. Size must be at least min(m, n).
Singular Values Only (Lines 41-46)
slate::svd_vals( A, Sigma );
// or
slate::svd( A, Sigma );
If you only need the singular values (not the vectors U and V), use svd_vals or svd without matrix arguments. This is significantly faster than computing vectors.
Singular Vectors (Lines 57-70)
slate::Matrix<scalar_type> U( m, min_mn, nb, ... );
slate::Matrix<scalar_type> VH( min_mn, n, nb, ... );
slate::svd( A, Sigma, U, VH );
To compute vectors:
U: Left singular vectors. Dimensions m by min(m, n).
VH: Right singular vectors (transposed). Dimensions min(m, n) by n.
Note: This example computes the reduced SVD.
Partial Vectors (Lines 75-80)
slate::Matrix<scalar_type> Uempty, Vempty;
slate::svd( A, Sigma, U, Vempty ); // only U
slate::svd( A, Sigma, Uempty, VH ); // only V^H
You can compute just U or just VH by passing an empty matrix placeholder for the unwanted component. This saves computation time.
1// ex10_svd.cc
2// Solve Singular Value Decomposition, A = U Sigma V^H
3
4/// !!! Lines between `//---------- begin label` !!!
5/// !!! and `//---------- end label` !!!
6/// !!! are included in the SLATE Users' Guide. !!!
7
8#include <slate/slate.hh>
9
10#include "util.hh"
11
12int mpi_size = 0;
13int mpi_rank = 0;
14int grid_p = 0;
15int grid_q = 0;
16
17//------------------------------------------------------------------------------
18template <typename scalar_type>
19void test_svd()
20{
21 using real_t = blas::real_type<scalar_type>;
22
23 print_func( mpi_rank );
24
25 int64_t m=2000, n=1000, nb=256;
26
27 //---------- begin svd1
28 int64_t min_mn = std::min( m, n );
29 slate::Matrix<scalar_type> A( m, n, nb, grid_p, grid_q, MPI_COMM_WORLD );
30 std::vector<real_t> Sigma( min_mn );
31 // ...
32 //---------- end svd1
33
34 A.insertLocalTiles();
35 random_matrix( A );
36
37 //----------------------------------------
38 //---------- begin svd2
39
40 // A = U Sigma V^H, singular values only
41 slate::svd_vals( A, Sigma );
42 //---------- end svd2
43 random_matrix( A );
44
45 //---------- begin svd3
46 slate::svd( A, Sigma );
47 //---------- end svd3
48
49 // todo: full SVD not yet supported?
50
51 //----------------------------------------
52 //---------- begin svd4
53
54 // Singular vectors
55 // U is m x min_mn (reduced SVD) or m x m (full SVD)
56 // V is min_mn x n (reduced SVD) or n x n (full SVD)
57 slate::Matrix<scalar_type> U( m, min_mn, nb, grid_p, grid_q, MPI_COMM_WORLD );
58 slate::Matrix<scalar_type> VH( min_mn, n, nb, grid_p, grid_q, MPI_COMM_WORLD );
59 // empty, 0-by-0 matrices as placeholders for U and VH.
60 slate::Matrix<scalar_type> Uempty, Vempty;
61 // ...
62 //---------- end svd4
63
64 U.insertLocalTiles();
65 VH.insertLocalTiles();
66 random_matrix( A );
67
68 //---------- begin svd5
69
70 slate::svd( A, Sigma, U, VH ); // both U and V^H
71 //---------- end svd5
72 random_matrix( A );
73
74 //---------- begin svd6
75 slate::svd( A, Sigma, U, Vempty ); // only U
76 //---------- end svd6
77 random_matrix( A );
78
79 //---------- begin svd7
80 slate::svd( A, Sigma, Uempty, VH ); // only V^H
81 //---------- end svd7
82}
83
84//------------------------------------------------------------------------------
85int main( int argc, char** argv )
86{
87 try {
88 // Parse command line to set types for s, d, c, z precisions.
89 bool types[ 4 ];
90 parse_args( argc, argv, types );
91
92 int provided = 0;
93 slate_mpi_call(
94 MPI_Init_thread( &argc, &argv, MPI_THREAD_MULTIPLE, &provided ) );
95 assert( provided == MPI_THREAD_MULTIPLE );
96
97 slate_mpi_call(
98 MPI_Comm_size( MPI_COMM_WORLD, &mpi_size ) );
99
100 slate_mpi_call(
101 MPI_Comm_rank( MPI_COMM_WORLD, &mpi_rank ) );
102
103 // Determine p-by-q grid for this MPI size.
104 grid_size( mpi_size, &grid_p, &grid_q );
105 if (mpi_rank == 0) {
106 printf( "mpi_size %d, grid_p %d, grid_q %d\n",
107 mpi_size, grid_p, grid_q );
108 }
109
110 // so random_matrix is different on different ranks.
111 srand( 100 * mpi_rank );
112
113 if (types[ 0 ]) {
114 test_svd< float >();
115 }
116
117 if (types[ 1 ]) {
118 test_svd< double >();
119 }
120
121 if (types[ 2 ]) {
122 test_svd< std::complex<float> >();
123 }
124
125 if (types[ 3 ]) {
126 test_svd< std::complex<double> >();
127 }
128
129 slate_mpi_call(
130 MPI_Finalize() );
131 }
132 catch (std::exception const& ex) {
133 fprintf( stderr, "%s", ex.what() );
134 return 1;
135 }
136 return 0;
137}