libROM  v1.0
Data-driven physical simulation library
StaticSVD.cpp
1 
11 // Description: A class implementing interface of SVD for the static SVD
12 // algorithm.
13 
14 #include "StaticSVD.h"
15 
16 #include "mpi.h"
17 #include "linalg/scalapack_wrapper.h"
18 
19 #include <limits.h>
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 /* Use automatically detected Fortran name-mangling scheme */
25 #define dgesdd CAROM_FC_GLOBAL(dgesdd, DGESDD)
26 
27 extern "C" {
28  void dgesdd(char*, int*, int*, double*, int*,
29  double*, double*, int*, double*, int*,
30  double*, int*, int*, int*);
31 }
32 
33 namespace CAROM {
34 
35 StaticSVD::StaticSVD(
36  Options options) :
37  SVD(options),
38  d_samples(new SLPK_Matrix), d_factorizer(new SVDManager),
39  d_basis_is_current(false),
40  d_max_basis_dimension(options.max_basis_dimension),
41  d_singular_value_tol(options.singular_value_tol),
42  d_preserve_snapshot(options.static_svd_preserve_snapshot)
43 {
44  // Get the rank of this process, and the number of processors.
45  int mpi_init;
46  MPI_Initialized(&mpi_init);
47  if (mpi_init == 0) {
48  MPI_Init(nullptr, nullptr);
49  }
50 
51  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
52  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
53 
55  /* TODO: Try doing this more intelligently and see if it makes a difference */
57  d_npcol = 1;
59  if (d_total_dim % d_nprow != 0) {
60  d_blocksize += 1;
61  }
62 
63  initialize_matrix(d_samples.get(), d_total_dim, d_max_num_samples,
64  d_nprow, d_npcol, d_blocksize, d_blocksize); // TODO: should nb = 1?
65  d_factorizer->A = nullptr;
66 }
67 
69 {
72 }
73 
75 {
76  if (d_samples) {
77  free_matrix_data(d_samples.get());
78  release_context(d_samples.get());
79  }
80 }
81 
83 {
84  if (d_factorizer->A != nullptr) {
85  free(d_factorizer->S);
86  d_factorizer->S = nullptr;
87  if (d_factorizer->U != nullptr) {
88  free_matrix_data(d_factorizer->U);
89  }
90  free(d_factorizer->U);
91  d_factorizer->U = nullptr;
92  if (d_factorizer->V != nullptr) {
93  free_matrix_data(d_factorizer->V);
94  }
95  free(d_factorizer->V);
96  d_factorizer->V = nullptr;
97  }
98 }
99 
100 bool
102  double* u_in,
103  bool add_without_increase)
104 {
105  CAROM_VERIFY(u_in != 0);
106  CAROM_NULL_USE(add_without_increase);
107  CAROM_VERIFY(0 <= d_num_samples);
108  CAROM_VERIFY(d_num_samples < d_max_num_samples);
109 
110  // Check the u_in is not non-zero.
111  Vector u_vec(u_in, d_dim, true);
112  if (u_vec.norm() == 0.0) {
113  return false;
114  }
115 
116  if (isFirstSample()) {
118  d_num_samples = 0;
119  d_basis = nullptr;
120  d_basis_right = nullptr;
121  // Set the N in the global matrix so BLACS won't complain.
123  }
124  broadcast_sample(u_in);
125  ++d_num_samples;
126 
127  // Build snapshot matrix before SVD is computed
128  //d_snapshots = new Matrix(d_dim, d_num_samples, false);
129  //for (int rank = 0; rank < d_num_procs; ++rank) {
130  // int nrows = d_dims[static_cast<unsigned>(rank)];
131  // int firstrow = d_istarts[static_cast<unsigned>(rank)] + 1;
132  // gather_transposed_block(&d_snapshots->item(0, 0), d_samples.get(),
133  // firstrow, 1, nrows,
134  // d_num_samples, rank);
135  // }
136  d_basis_is_current = false;
137  return true;
138 }
139 
140 std::shared_ptr<const Matrix>
142 {
143  // If this basis is for the last time interval then it may not be up to date
144  // so recompute it.
145  if (!isBasisCurrent()) {
146  computeSVD();
147  }
148  else {
149  CAROM_ASSERT(d_basis != 0);
150  }
151  CAROM_ASSERT(isBasisCurrent());
152  return d_basis;
153 }
154 
155 std::shared_ptr<const Matrix>
157 {
158  // If this basis is for the last time interval then it may not be up to date
159  // so recompute it.
160  if (!isBasisCurrent()) {
161  computeSVD();
162  }
163  else {
164  CAROM_ASSERT(d_basis_right != 0);
165  }
166  CAROM_ASSERT(isBasisCurrent());
167  return d_basis_right;
168 }
169 
170 std::shared_ptr<const Vector>
172 {
173  // If these singular values are for the last time interval then they may not
174  // be up to date so recompute them.
175  if (!isBasisCurrent()) {
176  d_S.reset();
177  d_W.reset();
178  computeSVD();
179  }
180  else {
181  CAROM_ASSERT(d_S != 0);
182  }
183  CAROM_ASSERT(isBasisCurrent());
184  return d_S;
185 }
186 
187 std::shared_ptr<const Matrix>
189 {
190  if ((!d_preserve_snapshot) && (isBasisCurrent()) && (d_basis != 0))
191  CAROM_ERROR("StaticSVD: snapshot matrix is modified after computeSVD."
192  " To preserve the snapshots, set Options::static_svd_preserve_snapshot to be true!\n");
193 
194  d_snapshots.reset(new Matrix(d_dim, d_num_samples, true));
195 
196  for (int rank = 0; rank < d_num_procs; ++rank) {
197  int nrows = d_dims[static_cast<unsigned>(rank)];
198  int firstrow = d_istarts[static_cast<unsigned>(rank)] + 1;
199  gather_transposed_block(&d_snapshots->item(0, 0), d_samples.get(),
200  firstrow, 1, nrows,
201  d_num_samples, rank);
202  }
203 
204  CAROM_ASSERT(d_snapshots != 0);
205  return d_snapshots;
206 }
207 
208 void
210 {
211  // pointer for snapshot matrix or its transpose.
212  SLPK_Matrix *snapshot_matrix = NULL;
213 
214  // Finalizes the column size of d_samples.
216 
217  // transpose matrix if sample size > dimension.
218  bool transpose = d_total_dim < d_num_samples;
219 
220  if (transpose) {
221  // create a transposed matrix if sample size > dimension.
222  snapshot_matrix = new SLPK_Matrix;
223  int d_blocksize_tr = d_num_samples / d_nprow;
224  if (d_num_samples % d_nprow != 0) {
225  d_blocksize_tr += 1;
226  }
227 
228  initialize_matrix(snapshot_matrix, d_num_samples, d_total_dim,
229  d_nprow, d_npcol, d_blocksize_tr, d_blocksize_tr);
230 
231  for (int rank = 0; rank < d_num_procs; ++rank) {
232  transpose_submatrix(snapshot_matrix, 1,
233  d_istarts[static_cast<unsigned>(rank)]+1,
234  d_samples.get(), d_istarts[static_cast<unsigned>(rank)]+1, 1,
235  d_dims[static_cast<unsigned>(rank)], d_num_samples);
236  }
237  }
238  else {
239  // use d_samples if sample size <= dimension.
240  // SVD operation does not preserve the original snapshot matrix.
241  if (d_preserve_snapshot)
242  {
243  snapshot_matrix = new SLPK_Matrix;
244  initialize_matrix(snapshot_matrix, d_total_dim, d_num_samples,
246  copy_matrix(snapshot_matrix, 1, 1, d_samples.get(), 1, 1, d_total_dim,
247  d_num_samples);
248  }
249  else
250  // use the original snapshot matrix, which will be modified.
251  snapshot_matrix = d_samples.get();
252  }
253 
254  // This block does the actual ScaLAPACK call to do the factorization.
256  svd_init(d_factorizer.get(), snapshot_matrix);
257  d_factorizer->dov = 1;
258  factorize(d_factorizer.get());
259 
260  // Compute how many basis vectors we will actually return.
261  int sigma_cutoff = 0, hard_cutoff = d_num_samples;
262  if (d_singular_value_tol == 0) {
263  sigma_cutoff = std::numeric_limits<int>::max();
264  } else {
265  for (int i = 0; i < d_num_samples; ++i) {
266  if (d_factorizer->S[i] / d_factorizer->S[0] > d_singular_value_tol) {
267  sigma_cutoff += 1;
268  } else {
269  break;
270  }
271  }
272  }
273  if (d_max_basis_dimension != -1 && d_max_basis_dimension < hard_cutoff) {
274  hard_cutoff = d_max_basis_dimension;
275  }
276  int ncolumns = hard_cutoff < sigma_cutoff ? hard_cutoff : sigma_cutoff;
277  if (transpose) ncolumns = (ncolumns > d_total_dim) ? d_total_dim : ncolumns;
278  CAROM_VERIFY(ncolumns >= 0);
279 
280  // Allocate the appropriate matrices and gather their elements.
281  d_S.reset(new Vector(ncolumns, false));
282  {
283  CAROM_VERIFY(ncolumns >= 0);
284  unsigned nc = static_cast<unsigned>(ncolumns);
285  memset(&d_S->item(0), 0, nc*sizeof(double));
286  }
287 
288  d_basis.reset(new Matrix(d_dim, ncolumns, true));
289  d_basis_right.reset(new Matrix(d_num_samples, ncolumns, false));
290  for (int rank = 0; rank < d_num_procs; ++rank) {
291  if (transpose) {
292  // V is computed in the transposed order so no reordering necessary.
293  gather_block(&d_basis->item(0, 0), d_factorizer->V,
294  1, d_istarts[static_cast<unsigned>(rank)]+1,
295  ncolumns, d_dims[static_cast<unsigned>(rank)], rank);
296 
297  // gather_transposed_block does the same as gather_block, but transposes
298  // it; here, it is used to go from column-major to row-major order.
299  gather_transposed_block(&d_basis_right->item(0, 0), d_factorizer->U,
300  1, 1, d_num_samples, ncolumns, rank);
301 
302  }
303  else {
304  // gather_transposed_block does the same as gather_block, but transposes
305  // it; here, it is used to go from column-major to row-major order.
306  gather_transposed_block(&d_basis->item(0, 0), d_factorizer->U,
307  d_istarts[static_cast<unsigned>(rank)]+1,
308  1, d_dims[static_cast<unsigned>(rank)],
309  ncolumns, rank);
310  // V is computed in the transposed order so no reordering necessary.
311  gather_block(&d_basis_right->item(0, 0), d_factorizer->V, 1, 1,
312  ncolumns, d_num_samples, rank);
313  }
314  }
315  for (int i = 0; i < ncolumns; ++i)
316  d_S->item(i) = d_factorizer->S[static_cast<unsigned>(i)];
317  d_basis_is_current = true;
318 
319  if (d_debug_algorithm) {
320  if (d_rank == 0) {
321  printf("Distribution of sampler's A and U:\n");
322  }
323  print_debug_info(d_samples.get());
324  MPI_Barrier(MPI_COMM_WORLD);
325 
326  if (d_rank == 0) {
327  printf("Distribution of sampler's V:\n");
328  }
329  print_debug_info(d_factorizer->V);
330  MPI_Barrier(MPI_COMM_WORLD);
331 
332  if (d_rank == 0) {
333  printf("Computed singular values: ");
334  for (int i = 0; i < ncolumns; ++i)
335  printf("%8.4E ", d_factorizer->S[i]);
336  printf("\n");
337  }
338  }
339 
340  // Delete the snapshot matrix.
341  if ((transpose) || (d_preserve_snapshot))
342  {
343  free_matrix_data(snapshot_matrix);
344  delete snapshot_matrix;
345  }
346 }
347 
348 void
349 StaticSVD::broadcast_sample(const double* u_in)
350 {
351  for (int rank = 0; rank < d_num_procs; ++rank) {
352  scatter_block(d_samples.get(), d_istarts[static_cast<unsigned>(rank)]+1,
353  d_num_samples+1, u_in, d_dims[static_cast<unsigned>(rank)],
354  1, rank);
355  }
356 }
357 
358 void
360 {
361  d_dims.resize(static_cast<unsigned>(d_num_procs));
362  MPI_Allgather(&d_dim, 1, MPI_INT, d_dims.data(), 1, MPI_INT, MPI_COMM_WORLD);
363  d_total_dim = 0;
364  d_istarts = std::vector<int>(static_cast<unsigned>(d_num_procs), 0);
365 
366  for (unsigned i = 0; i < static_cast<unsigned>(d_num_procs); ++i) {
367  d_total_dim += d_dims[i];
368  if (i > 0) {
369  d_istarts[i] = d_istarts[i-1] + d_dims[i-1];
370  }
371  }
372 }
373 
374 }
Definition: SVD.h:29
std::shared_ptr< Matrix > d_basis_right
The globalized right basis vectors for the current time interval.
Definition: SVD.h:172
std::shared_ptr< Matrix > d_basis
The globalized basis vectors for the current time interval.
Definition: SVD.h:164
bool isFirstSample() const
Returns true if the next sample will result in a new time interval.
Definition: SVD.h:133
const int d_max_num_samples
The maximum number of samples.
Definition: SVD.h:156
int d_num_samples
Number of samples stored for the current time interval.
Definition: SVD.h:146
bool d_debug_algorithm
Flag to indicate if results of algorithm should be printed for debugging purposes.
Definition: SVD.h:210
std::shared_ptr< Matrix > d_snapshots
The globalized snapshot vectors for the current time interval.
Definition: SVD.h:204
std::shared_ptr< Matrix > d_W
The matrix U which is large.
Definition: SVD.h:188
const int d_dim
Dimension of the system.
Definition: SVD.h:141
std::shared_ptr< Vector > d_S
The vector S which is small.
Definition: SVD.h:196
double d_singular_value_tol
The tolerance for singular values below which to drop vectors.
Definition: StaticSVD.h:201
virtual bool takeSample(double *u_in, bool add_without_increase=false)
Collect the new sample, u_in at the supplied time.
Definition: StaticSVD.cpp:101
std::shared_ptr< const Matrix > getSpatialBasis() override
Returns the basis vectors for the current time interval as a Matrix.
Definition: StaticSVD.cpp:141
int d_rank
The rank of the process this object belongs to.
Definition: StaticSVD.h:146
int d_nprow
The number of processor rows in the grid.
Definition: StaticSVD.h:175
std::shared_ptr< const Matrix > getTemporalBasis() override
Returns the temporal basis vectors for the current time interval as a Matrix.
Definition: StaticSVD.cpp:156
virtual void computeSVD()
Gathers samples from all other processors to form complete sample of system and computes the SVD.
Definition: StaticSVD.cpp:209
int d_npcol
The number of processor columns in the grid.
Definition: StaticSVD.h:180
void delete_factorizer()
Delete the factorizer from ScaLAPACK.
Definition: StaticSVD.cpp:82
void get_global_info()
Get the system's total row dimension and where my rows sit in the matrix.
Definition: StaticSVD.cpp:359
void delete_samples()
Delete the samples from ScaLAPACK.
Definition: StaticSVD.cpp:74
void broadcast_sample(const double *u_in)
Broadcast the sample to all processors.
Definition: StaticSVD.cpp:349
bool d_basis_is_current
Flag to indicate if the basis vectors for the current time interval are up to date.
Definition: StaticSVD.h:141
std::vector< int > d_istarts
The starting row (0-based) of the matrix that each process owns. Stored to avoid an MPI operation to ...
Definition: StaticSVD.h:158
int d_total_dim
The total dimension of the system (row dimension)
Definition: StaticSVD.h:170
std::shared_ptr< const Vector > getSingularValues() override
Returns the singular values for the current time interval.
Definition: StaticSVD.cpp:171
int d_max_basis_dimension
The max number of basis vectors to return.
Definition: StaticSVD.h:196
std::unique_ptr< SLPK_Matrix > d_samples
Current samples of the system.
Definition: StaticSVD.h:130
std::unique_ptr< SVDManager > d_factorizer
Factorization manager object used to compute the SVD.
Definition: StaticSVD.h:135
int d_blocksize
The block size used internally for computing the SVD.
Definition: StaticSVD.h:185
bool isBasisCurrent()
Checks if the basis vectors are computed from the current snapshot.
Definition: StaticSVD.h:122
int d_num_procs
The number of processors being run on.
Definition: StaticSVD.h:151
std::vector< int > d_dims
The number of rows that each process owns. Stored to avoid an MPI operation to get this operation eve...
Definition: StaticSVD.h:165
std::shared_ptr< const Matrix > getSnapshotMatrix() override
Returns the snapshot matrix for the current time interval.
Definition: StaticSVD.cpp:188
double norm() const
Form the norm of this.
Definition: Vector.cpp:216