libROM  v1.0
Data-driven physical simulation library
IncrementalSVDFastUpdate.cpp
1 
11 // Description: The concrete implementation of the incremental SVD algorithm
12 // using Matthew Brand's "fast update" method.
13 
14 #include "IncrementalSVDFastUpdate.h"
15 #include "utils/HDFDatabase.h"
16 
17 #include "mpi.h"
18 
19 #include <cmath>
20 #include <limits>
21 
22 namespace CAROM {
23 
24 IncrementalSVDFastUpdate::IncrementalSVDFastUpdate(
25  Options options,
26  const std::string& basis_file_name) :
27  IncrementalSVD(
28  options,
29  basis_file_name),
30  d_singular_value_tol(options.singular_value_tol)
31 {
32  CAROM_VERIFY(options.singular_value_tol >= 0);
33 
34  // If the state of the SVD is to be restored, do it now. The base class,
35  // IncrementalSVD, has already opened the database and restored the state
36  // common to all incremental algorithms. This particular class must also
37  // read the state of d_Up and then compute the basis. If the database could
38  // not be found then we can not restore the state.
39  if (options.restore_state && d_state_database) {
40  // Read d_Up.
41  int num_rows;
42  d_state_database->getInteger("Up_num_rows", num_rows);
43  int num_cols;
44  d_state_database->getInteger("Up_num_cols", num_cols);
45  d_Up.reset(new Matrix(num_rows, num_cols, true));
46  d_state_database->getDoubleArray("Up",
47  &d_Up->item(0, 0),
48  num_rows*num_cols);
49 
50  // Close and delete the database.
51  d_state_database->close();
52  delete d_state_database;
53 
54  // Compute the basis.
55  computeBasis();
56  }
57 }
58 
59 IncrementalSVDFastUpdate::~IncrementalSVDFastUpdate()
60 {
61  // If the state of the SVD is to be saved, then create the database now.
62  // The IncrementalSVD base class destructor will save d_S and d_U. This
63  // derived class must save its specific state data, d_Up.
64  //
65  // If there are multiple time intervals then saving and restoring the state
66  // does not make sense as there is not one, all encompassing, basis.
67  if (d_save_state && (!isFirstSample())) {
68  // Create state database file.
69  d_state_database = new HDFDatabase();
70  d_state_database->create(d_state_file_name);
71 
72  // Save d_Up.
73  int num_rows = d_Up->numRows();
74  d_state_database->putInteger("Up_num_rows", num_rows);
75  int num_cols = d_Up->numColumns();
76  d_state_database->putInteger("Up_num_cols", num_cols);
77  d_state_database->putDoubleArray("Up",
78  &d_Up->item(0, 0),
79  num_rows*num_cols);
80  }
81 }
82 
83 void
84 IncrementalSVDFastUpdate::buildInitialSVD(
85  double* u)
86 {
87  CAROM_VERIFY(u != 0);
88 
89  // Build d_S for this new time interval.
90  d_S.reset(new Vector(1, false));
91  Vector u_vec(u, d_dim, true);
92  double norm_u = u_vec.norm();
93  d_S->item(0) = norm_u;
94 
95  // Build d_Up for this new time interval.
96  d_Up.reset(new Matrix(1, 1, false));
97  d_Up->item(0, 0) = 1.0;
98 
99  // Build d_U for this new time interval.
100  d_U.reset(new Matrix(d_dim, 1, true));
101  for (int i = 0; i < d_dim; ++i) {
102  d_U->item(i, 0) = u[i]/norm_u;
103  }
104 
105  // Build d_W for this new time interval.
106  if (d_update_right_SV) {
107  d_W.reset(new Matrix(1, 1, false));
108  d_W->item(0, 0) = 1.0;
109  }
110 
111  // We now have the first sample for the new time interval.
112  d_num_samples = 1;
113  d_num_rows_of_W = 1;
114 
115  // Compute the basis vectors for this time interval.
116  computeBasis();
117 
118 }
119 
120 void
121 IncrementalSVDFastUpdate::computeBasis()
122 {
123  d_basis = d_U->mult(*d_Up);
124  if(d_update_right_SV)
125  {
126  d_basis_right.reset(new Matrix(*d_W));
127  }
128 
129  if(d_rank == 0) {
130  std::cout << "d_num_samples = " << d_num_samples << "\n";
131  std::cout << "d_num_rows_of_W = " << d_num_rows_of_W << "\n";
132  std::cout << "d_singular_value_tol = " << d_singular_value_tol << "\n";
133  std::cout << "smallest SV = " << d_S->item(d_num_samples-1) << "\n";
134  if (d_num_samples > 1) {
135  std::cout << "next smallest SV = " << d_S->item(d_num_samples-2) << "\n";
136  }
137  }
138  // remove the smallest singular value if it is smaller than d_singular_value_tol
139  if ( (d_singular_value_tol != 0.0) &&
140  (d_S->item(d_num_samples-1) < d_singular_value_tol) &&
141  (d_num_samples != 1) ) {
142 
143  if (d_rank == 0) std::cout << "removing a small singular value!\n";
144 
145  Matrix* d_basis_new = new Matrix(d_dim, d_num_samples-1,
146  d_basis->distributed());
147  for (int row = 0; row < d_dim; ++row) {
148  for (int col = 0; col < d_num_samples-1; ++col) {
149  d_basis_new->item(row, col) = d_basis->item(row,col);
150  }
151  }
152  d_basis.reset(d_basis_new);
153 
154  if (d_update_right_SV)
155  {
156  Matrix* d_basis_right_new = new Matrix(d_num_rows_of_W, d_num_samples-1,
157  d_basis_right->distributed());
158  for (int row = 0; row < d_num_rows_of_W; ++row) {
159  for (int col = 0; col < d_num_samples-1; ++col) {
160  d_basis_right_new->item(row, col) = d_basis_right->item(row,col);
161  }
162  }
163  d_basis_right.reset(d_basis_right_new);
164  }
165  --d_num_samples;
166  }
167 
168  // Reorthogonalize if necessary.
169  if (fabs(checkOrthogonality(*d_basis)) >
170  std::numeric_limits<double>::epsilon()*static_cast<double>(d_num_samples)) {
171  d_basis->orthogonalize();
172  }
173  if(d_update_right_SV)
174  {
175  if (fabs(checkOrthogonality(*d_basis_right)) >
176  std::numeric_limits<double>::epsilon()*d_num_samples) {
177  d_basis_right->orthogonalize();
178  }
179  }
180 
181 }
182 
183 void
184 IncrementalSVDFastUpdate::addLinearlyDependentSample(
185  const Matrix & A,
186  const Matrix & W,
187  const Matrix & sigma)
188 {
189  // Chop a row and a column off of A to form Amod. Also form
190  // d_S by chopping a row and a column off of sigma.
191  Matrix Amod(d_num_samples, d_num_samples, false);
192  for (int row = 0; row < d_num_samples; ++row) {
193  for (int col = 0; col < d_num_samples; ++col) {
194  Amod.item(row, col) = A.item(row, col);
195  if (row == col)
196  {
197  d_S->item(col) = sigma.item(row, col);
198  }
199  }
200  }
201 
202  // Multiply d_Up and Amod and put result into d_Up.
203  Matrix *Up_times_Amod = new Matrix(d_Up->numRows(), Amod.numColumns(), false);
204  d_Up->mult(Amod, *Up_times_Amod);
205  d_Up.reset(Up_times_Amod);
206 
207  if (d_update_right_SV) {
208  // The new d_W is the product of the current d_W extended by another row
209  // and column and W. The only new value in the extended version of d_W
210  // that is non-zero is the new lower right value and it is 1. We will
211  // construct this product without explicitly forming the extended version of
212  // d_W.
213  Matrix *new_d_W = new Matrix(d_num_rows_of_W+1, d_num_samples, false);
214  for (int row = 0; row < d_num_rows_of_W; ++row) {
215  for (int col = 0; col < d_num_samples; ++col) {
216  double new_d_W_entry = 0.0;
217  for (int entry = 0; entry < d_num_samples; ++entry) {
218  new_d_W_entry += d_W->item(row, entry)*W.item(entry, col);
219  }
220  new_d_W->item(row, col) = new_d_W_entry;
221  }
222  }
223  for (int col = 0; col < d_num_samples; ++col) {
224  new_d_W->item(d_num_rows_of_W, col) = W.item(d_num_samples, col);
225  }
226  d_W.reset(new_d_W);
227  ++d_num_rows_of_W;
228  }
229 
230 }
231 
232 void
233 IncrementalSVDFastUpdate::addNewSample(
234  const Vector & j,
235  const Matrix & A,
236  const Matrix & W,
237  const Matrix & sigma)
238 {
239  // Add j as a new column of d_U.
240  Matrix* newU = new Matrix(d_dim, d_num_samples+1, true);
241  for (int row = 0; row < d_dim; ++row) {
242  for (int col = 0; col < d_num_samples; ++col) {
243  newU->item(row, col) = d_U->item(row, col);
244  }
245  newU->item(row, d_num_samples) = j.item(row);
246  }
247  d_U.reset(newU);
248 
249  if (d_update_right_SV) {
250  // The new d_W is the product of the current d_W extended by another row
251  // and column and W. The only new value in the extended version of d_W
252  // that is non-zero is the new lower right value and it is 1. We will
253  // construct this product without explicitly forming the extended version of
254  // d_W.
255  Matrix *new_d_W = new Matrix(d_num_rows_of_W+1, d_num_samples+1, false);
256  for (int row = 0; row < d_num_rows_of_W; ++row) {
257  for (int col = 0; col < d_num_samples+1; ++col) {
258  double new_d_W_entry = 0.0;
259  for (int entry = 0; entry < d_num_samples; ++entry) {
260  new_d_W_entry += d_W->item(row, entry)*W.item(entry, col);
261  }
262  new_d_W->item(row, col) = new_d_W_entry;
263  }
264  }
265  for (int col = 0; col < d_num_samples+1; ++col) {
266  new_d_W->item(d_num_rows_of_W, col) = W.item(d_num_samples, col);
267  }
268  d_W.reset(new_d_W);
269  }
270 
271  // The new d_Up is the product of the current d_Up extended by another row
272  // and column and A. The only new value in the extended version of d_Up
273  // that is non-zero is the new lower right value and it is 1. We will
274  // construct this product without explicitly forming the extended version of
275  // d_Up.
276  Matrix* new_d_Up = new Matrix(d_num_samples+1, d_num_samples+1, false);
277  for (int row = 0; row < d_num_samples; ++row) {
278  for (int col = 0; col < d_num_samples+1; ++col) {
279  double new_d_Up_entry = 0.0;
280  for (int entry = 0; entry < d_num_samples; ++entry) {
281  new_d_Up_entry += d_Up->item(row, entry)*A.item(entry, col);
282  }
283  new_d_Up->item(row, col) = new_d_Up_entry;
284  }
285  }
286  for (int col = 0; col < d_num_samples+1; ++col) {
287  new_d_Up->item(d_num_samples, col) = A.item(d_num_samples, col);
288  }
289  d_Up.reset(new_d_Up);
290 
291  // d_S = sigma.
292  int num_dim = std::min(sigma.numRows(), sigma.numColumns());
293  d_S.reset(new Vector(num_dim, false));
294  for (int i = 0; i < num_dim; i++) {
295  d_S->item(i) = sigma.item(i,i);
296  }
297 
298  // We now have another sample.
299  ++d_num_samples;
300  ++d_num_rows_of_W;
301 
302  // Reorthogonalize if necessary.
303  long int max_U_dim;
304  if (d_num_samples > d_total_dim) {
305  max_U_dim = d_num_samples;
306  }
307  else {
308  max_U_dim = d_total_dim;
309  }
310 
311 }
312 
313 }