[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
NNLS_solver.cpp
1 
2 #include "NNLS_solver.h"
3 #include "helper_functions.h"
4 
5 namespace PHiLiP {
6 
8  const Parameters::AllParameters *const parameters_input,
9  const dealii::ParameterHandler &parameter_handler_input,
10  const Epetra_CrsMatrix &A,
11  Epetra_MpiComm &Comm,
12  Epetra_Vector &b): NNLSSolver(
13  parameters_input,
14  parameter_handler_input,
15  A,
16  false,
17  Comm,
18  b,
19  false,
20  false,
21  1000,
22  10E-8) {}
23 
25  const Parameters::AllParameters *const parameters_input,
26  const dealii::ParameterHandler &parameter_handler_input,
27  const Epetra_CrsMatrix &A,
28  const bool is_input_A_matrix_transposed,
29  Epetra_MpiComm &Comm,
30  Epetra_Vector &b): NNLSSolver(
31  parameters_input,
32  parameter_handler_input,
33  A,
34  is_input_A_matrix_transposed,
35  Comm,
36  b,
37  false,
38  false,
39  1000,
40  10E-8) {}
41 
43  const Parameters::AllParameters *const parameters_input,
44  const dealii::ParameterHandler &parameter_handler_input,
45  const Epetra_CrsMatrix &A,
46  Epetra_MpiComm &Comm,
47  Epetra_Vector &b,
48  bool grad_exit_crit): NNLSSolver(
49  parameters_input,
50  parameter_handler_input,
51  A,
52  false,
53  Comm,
54  b,
55  grad_exit_crit,
56  false,
57  1000,
58  10E-8) {}
59 
61  const Parameters::AllParameters *const parameters_input,
62  const dealii::ParameterHandler &parameter_handler_input,
63  const Epetra_CrsMatrix &A,
64  const bool is_input_A_matrix_transposed,
65  Epetra_MpiComm &Comm,
66  Epetra_Vector &b,
67  bool grad_exit_crit): NNLSSolver(
68  parameters_input,
69  parameter_handler_input,
70  A,
71  is_input_A_matrix_transposed,
72  Comm,
73  b,
74  grad_exit_crit,
75  false,
76  1000,
77  10E-8) {}
78 
80  const Parameters::AllParameters *const parameters_input,
81  const dealii::ParameterHandler &parameter_handler_input,
82  const Epetra_CrsMatrix &A,
83  Epetra_MpiComm &Comm,
84  Epetra_Vector &b,
85  bool iter_solver,
86  int LS_iter,
87  double LS_tol): NNLSSolver(
88  parameters_input,
89  parameter_handler_input,
90  A,
91  false,
92  Comm,
93  b,
94  false,
95  iter_solver,
96  LS_iter,
97  LS_tol) {}
98 
100  const Parameters::AllParameters *const parameters_input,
101  const dealii::ParameterHandler &parameter_handler_input,
102  const Epetra_CrsMatrix &A,
103  const bool is_input_A_matrix_transposed,
104  Epetra_MpiComm &Comm,
105  Epetra_Vector &b,
106  bool iter_solver,
107  int LS_iter,
108  double LS_tol): NNLSSolver(
109  parameters_input,
110  parameter_handler_input,
111  A,
112  is_input_A_matrix_transposed,
113  Comm,
114  b,
115  false,
116  iter_solver,
117  LS_iter,
118  LS_tol) {}
119 
121  const Parameters::AllParameters *const parameters_input,
122  const dealii::ParameterHandler &parameter_handler_input,
123  const Epetra_CrsMatrix &A,
124  const bool is_input_A_matrix_transposed,
125  Epetra_MpiComm &Comm,
126  Epetra_Vector &b,
127  bool grad_exit_crit,
128  bool iter_solver,
129  int LS_iter,
130  double LS_tol):
131  all_parameters(parameters_input),
132  parameter_handler(parameter_handler_input),
133  Comm_(Comm),
134  A_((Comm.NumProc() == 1) ? transpose_matrix_on_single_core(A, is_input_A_matrix_transposed) : allocate_matrix_to_single_core(A, is_input_A_matrix_transposed)),
135  b_((Comm.NumProc() == 1) ? b : allocate_vector_to_single_core(b)),
136  x_(A_.ColMap()),
137  multi_x_((is_input_A_matrix_transposed) ? A.RowMap(): A.ColMap()),
138  LS_iter_(LS_iter),
139  LS_tol_(LS_tol),
140  Z(A_.NumGlobalCols()),
141  P(A_.NumGlobalCols()),
142  iter_solver_(iter_solver),
143  grad_exit_crit_(grad_exit_crit),
144  is_input_A_matrix_transposed_(is_input_A_matrix_transposed)
145  {
146  index_set = Eigen::VectorXd::LinSpaced(A_.NumGlobalCols(), 0, A_.NumGlobalCols() -1); // Indices proceeding and including numInactive are in the P set (Inactive/Positive)
147  Z.flip(); // All columns begin in the Z set (Active)
148  //EpetraExt::RowMatrixToMatlabFile("C_multicore_consol", A_);
149  }
150 
151 void NNLSSolver::epetra_permutation_matrix(Epetra_CrsMatrix &P_mat){
152  // Fill diagonal matrix with ones in the positive set
153  // No longer in use
154  double posOne = 1.0;
155  for(int i = 0; i < P_mat.NumMyCols(); i++){
156  int globalRow = P_mat.GRID(i);
157  if (P[i] == 1) {
158  P_mat.InsertGlobalValues(globalRow, 1, &posOne , &i);
159  }
160  }
161 }
162 
163 void NNLSSolver::positive_set_matrix(Epetra_CrsMatrix &P_mat){
164  // Create matrix P_mat which contains the positive set of columns in A
165 
166  // Create map between index_set and the columns to be added to P_mat
167  std::vector<int> colMap(A_.NumGlobalCols());
168  int numCol = 0;
169  for(int j = 0; j < A_.NumGlobalCols(); j++){
170  int idx = index_set[j];
171  if (P[idx] == 1) {
172  colMap[idx] = numCol;
173  numCol++;
174  }
175  }
176 
177  // Fill Epetra_CrsMatrix P_mat with columns of A in set P
178  for(int i =0; i < A_.NumGlobalRows(); i++){
179  double *row = new double[A_.NumGlobalCols()];
180  int numE;
181  const int globalRow = A_.GRID(i);
182  A_.ExtractGlobalRowCopy(globalRow, A_.NumGlobalCols(), numE , row);
183  for(int j = 0; j < A_.NumGlobalCols(); j++){
184  if (P[j] == 1) {
185  P_mat.InsertGlobalValues(i, 1, &row[j] , &colMap[j]);
186 
187  }
188  }
189  delete[] row;
190  }
191 }
192 
193 void NNLSSolver::sub_into_x(Epetra_Vector &temp){
194  // Substitute new values into the solution vector
195  std::vector<int> colMap(A_.NumGlobalCols());
196  int numCol = 0;
197  for(int j = 0; j < x_.MyLength(); j++){
198  int idx = index_set[j];
199  if (P[idx] == 1) {
200  colMap[idx] = numCol;
201  numCol++;
202  }
203  }
204  for(int j = 0; j < x_.MyLength(); j++){
205  if (P[j] == 1) {
206  x_[j] = temp[colMap[j]];
207  }
208  }
209 }
210 
211 void NNLSSolver::add_into_x(Epetra_Vector &temp, double alpha){
212  // Add vector temp time scalar alpha into the vector x
213  std::vector<int> colMap(A_.NumGlobalCols());
214  int numCol = 0;
215  for(int j = 0; j < x_.MyLength(); j++){
216  int idx = index_set[j];
217  if (P[idx] == 1) {
218  colMap[idx] = numCol;
219  numCol++;
220  }
221  }
222  for(int j = 0; j < x_.MyLength(); j++){
223  if (P[j] == 1) {
224  x_[j] += alpha*(temp[colMap[j]] -x_[j]);
225  }
226  }
227 }
228 
230  // Move index at idx into the Active Set (Z set)
231  P[index_set(idx)] = 0;
232  Z[index_set(idx)] = 1;
233 
234  std::swap(index_set(idx), index_set(numInactive_ - 1));
235  numInactive_--;
236 }
237 
239  // Move index at idx into the Inactive Set (P set)
240  P[index_set(idx)] = 1;
241  Z[index_set(idx)] = 0;
242 
243  std::swap(index_set(idx), index_set(numInactive_));
244  numInactive_++;
245 }
246 
248  const int rank = this->Comm_.MyPID();
249  iter_ = 0;
250  numInactive_ = 0;
251  // Pre-mult by A^T
252  Epetra_CrsMatrix AtA(Epetra_DataAccess::Copy, A_.ColMap(), A_.NumMyCols());
253  EpetraExt::MatrixMatrix::Multiply(A_, true, A_, false, AtA);
254 
255  Epetra_Vector Atb (A_.ColMap());
256  A_.Multiply(true, b_, Atb);
257 
258  Epetra_Vector AtAx (A_.ColMap());
259  Epetra_Vector Ax (A_.RowMap());
260  Epetra_MultiVector gradient (A_.ColMap(), 1);
261  Epetra_MultiVector residual (A_.RowMap(), 1);
262  Eigen::VectorXd grad_eig (gradient.GlobalLength());
263  Epetra_Vector grad_col (A_.ColMap());
264 
265  // OUTER LOOP
266  while(true){
267  // Early exit if all variables are inactive, which breaks 'maxCoeff' below.
268  if (A_.NumGlobalCols() == numInactive_){
269  multi_x_ = allocate_vector_to_multiple_cores(this->x_, this->multi_x_);
270  return true;
271  }
272  AtA.Multiply(false, x_, AtAx);
273  gradient = Atb;
274  gradient.Update(-1.0, AtAx, 1.0); // gradient = A^T * (b-A*x)
275 
276  grad_col = *gradient(0);
277  for(int i = 0; i < gradient.MyLength() ; ++i){
278  grad_eig[i] = grad_col[i];
279  }
280 
281  // Find the maximum element of the gradient in the active set
282  // Move that variable to the inactive set
283  int numActive = A_.NumGlobalCols() - numInactive_;
284  int argmaxGradient = -1;
285  double maxGradient;
286  if(rank==0){
287  maxGradient = grad_eig(index_set.tail(numActive)).maxCoeff(&argmaxGradient);
288  }
289  MPI_Bcast(&maxGradient,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
290  argmaxGradient += numInactive_;
291  MPI_Bcast(&argmaxGradient,1, MPI_INT,0,MPI_COMM_WORLD);
292  residual = b_;
293  A_.Multiply(false, x_, Ax);
294  residual.Update(-1.0, Ax, 1.0); // residual = b - A*x
295  double normRes[1];
296  residual.Norm2(normRes);
297  double normb[1];
298  b_.Norm2(normb);
299  // Old exit condition dependent on the maxGradient
300  // Original exit condition presented in "SOLVING LEAST SQUARES PROBLEMS", by Charles L. Lawson and
301  // Richard J. Hanson, Prentice-Hall, 1974
302  // https://epubs.siam.org/doi/10.1137/1.9781611971217
303  if (grad_exit_crit_){
304  if (maxGradient < all_parameters->hyper_reduction_param.NNLS_tol){
305  std::cout << "Exited due to Gradient Criteria" << std::endl;
306  std::cout << "Norm-2 of b" << std::endl;
307  std::cout << normb[0] << std::endl;
308  std::cout << "Norm-2 of the residual (b-A*x)" << std::endl;
309  std::cout << normRes[0] << std::endl;
310  multi_x_ = allocate_vector_to_multiple_cores(this->x_, this->multi_x_);
311  return true;
312  }
313  }
314  // Exit Condition on the residual based on the norm of b
315  // Updated exit condition presented in "Accelerated mesh sampling for the hyper reduction of nonlinear computational models",
316  // by Chapman et. al, 2016 (EQUATION 13)
317  // https://onlinelibrary.wiley.com/doi/full/10.1002/nme.5332
318  else {
319  if ((normRes[0]) <= (all_parameters->hyper_reduction_param.NNLS_tol * normb[0])){
320  std::cout << "Exited due to Residual Criteria" << std::endl;
321  std::cout << "Norm-2 of b" << std::endl;
322  std::cout << normb[0] << std::endl;
323  std::cout << "Norm-2 of the residual (b-A*x)" << std::endl;
324  std::cout << normRes[0] << std::endl;
325  multi_x_ = allocate_vector_to_multiple_cores(this->x_, this->multi_x_);
326  return true;
327  }
328  }
329 
330  move_to_inactive_set(argmaxGradient);
331 
332  // INNER LOOP
333  bool no_feasible_soln = true;
334  while(no_feasible_soln){
335  // Check if max. number of iterations is reached
337  multi_x_ = allocate_vector_to_multiple_cores(this->x_, this->multi_x_);
338  return false;
339  }
340  // Create matrix P_mat with columns from set P
341  Epetra_Map Map(A_.NumGlobalRows(),(rank == 0) ? A_.NumGlobalRows() : 0, 0, Comm_);
342  Epetra_Map ColMap(numInactive_,(rank == 0) ? numInactive_ : 0, 0, Comm_);
343  Epetra_CrsMatrix P_mat(Epetra_DataAccess::Copy, Map, numInactive_);
344  positive_set_matrix(P_mat);
345  P_mat.FillComplete(ColMap, Map);
346  // Create temporary solution vector temp which is only the length of numInactive
347  Epetra_Vector temp(P_mat.ColMap());
348 
349  // Set up normal equations
350  Epetra_CrsMatrix PtP(Epetra_DataAccess::Copy, P_mat.ColMap(), P_mat.NumMyCols());
351  EpetraExt::MatrixMatrix::Multiply(P_mat, true, P_mat, false, PtP);
352 
353  Epetra_Vector Ptb (P_mat.ColMap());
354  P_mat.Multiply(true, b_, Ptb);
355  // An iterative solver can be used by setting iter_solver_ to true
356  if (iter_solver_){
357  // Solve least-squares problem in inactive set only
358  Epetra_LinearProblem problem(&P_mat, &temp, &b_);
359  AztecOO solver(problem);
360 
361  // Iterative Solver Setup
362  solver.SetAztecOption(AZ_conv, AZ_rhs);
363  solver.SetAztecOption( AZ_precond, AZ_Jacobi);
364  solver.SetAztecOption(AZ_output, AZ_none);
365  solver.Iterate(LS_iter_, LS_tol_);
366  }
367  else{
368  // Solve least-squares problem in inactive set only
369  Epetra_LinearProblem problem(&PtP, &temp, &Ptb);
370 
371  // Direct Solver Setup
372  Amesos Factory;
373  std::string SolverType = "Klu";
374  std::unique_ptr<Amesos_BaseSolver> Solver(Factory.Create(SolverType, problem));
375 
376  Teuchos::ParameterList List;
377  Solver->SetParameters(List);
378  Solver->SymbolicFactorization();
379  Solver->NumericFactorization();
380  Solver->Solve();
381  }
382  iter_++; // The solve is expensive, so that is what we count as an iteration
383 
384  // Check feasability...
385  bool feasible = true;
386  double alpha = Eigen::NumTraits<Eigen::VectorXd::Scalar>::highest();
387  int infeasibleIdx = -1;
388  if(rank == 0){ // Will only proceed on the root as the other cores do no have access to this information
389  for(int k = 0; k < numInactive_; k++){
390  int idx = index_set[k];
391  if (temp[k] < 0){
392  // t should always be in [0,1]
393  double t = -x_[idx]/(temp[k] - x_[idx]);
394  if (alpha > t){
395  alpha = t;
396  infeasibleIdx = k;
397  feasible = false;
398  }
399  }
400  }
401  }
402  // Broadcast these variables so that the loop does not fail on other cores
403  MPI_Bcast(&feasible,1,MPI_CXX_BOOL,0,MPI_COMM_WORLD);
404  MPI_Bcast(&infeasibleIdx, 1, MPI_INT,0,MPI_COMM_WORLD);
405  MPI_Bcast(&alpha,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
406  eigen_assert(feasible || 0 <= infeasibleIdx);
407 
408  // If solution is feasible, exit to outer loop
409  if (feasible){
410  sub_into_x(temp);
411  no_feasible_soln = false;
412  }
413  else{
414  // Infeasible solution -> interpolate to feasible one
415  add_into_x(temp, alpha);
416 
417  // Remove the infeasibleIdx column from the inactive set
418  move_to_active_set(infeasibleIdx);
419  }
420  }
421  }
422 }
423 
424 } // PHiLiP namespace
std::vector< bool > Z
Vector of booleans representing the columns in the active set.
Definition: NNLS_solver.h:191
const dealii::ParameterHandler & parameter_handler
Parameter handler for storing the .prm file being ran.
Definition: NNLS_solver.h:171
void move_to_active_set(int idx)
Moves the column at idx into the active set (updating the index_set, Z, P, and numInactive_) ...
std::vector< bool > P
Vector of boolean representing the columns in the inactive set.
Definition: NNLS_solver.h:193
const bool is_input_A_matrix_transposed_
Definition: NNLS_solver.h:202
Files for the baseline physics.
Definition: ADTypes.hpp:10
int NNLS_max_iter
Maximum number of iterations for NNLS Solver.
Main parameter class that contains the various other sub-parameter classes.
const Epetra_CrsMatrix A_
Epetra Matrix A allocated to a single core.
Definition: NNLS_solver.h:177
bool iter_solver_
Boolean used for iterative solvers.
Definition: NNLS_solver.h:197
Epetra_MpiComm Comm_
Epetra Commuicator Object with MPI.
Definition: NNLS_solver.h:175
double NNLS_tol
Tolerance for NNLS Solver.
Eigen::VectorXd index_set
Eigen Vector of the index_set.
Definition: NNLS_solver.h:208
Epetra_Vector multi_x_
Epetra_Vector x, to be solved. Allocated to multiple cores.
Definition: NNLS_solver.h:183
void sub_into_x(Epetra_Vector &temp)
Replaces the entries with x with the values in temp.
int LS_iter_
Needed if the an iterative solver is used.
Definition: NNLS_solver.h:185
int iter_
Number of iterations in the NNLS solver.
Definition: NNLS_solver.h:204
void move_to_inactive_set(int idx)
Moves the column at idx into the inactive set (updating the index_set, Z, P, and numInactive_) ...
NNLSSolver(const Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input, const Epetra_CrsMatrix &A, Epetra_MpiComm &Comm, Epetra_Vector &b)
Default Constructor.
Definition: NNLS_solver.cpp:7
bool grad_exit_crit_
Boolean to use an exit Condition depending on maximum gradent.
Definition: NNLS_solver.h:199
int numInactive_
Number of inactive points.
Definition: NNLS_solver.h:189
double LS_tol_
Needed if the an iterative solver is used.
Definition: NNLS_solver.h:187
Epetra_Vector x_
Epetra Vector x, to be solved. Allocated to a single core.
Definition: NNLS_solver.h:181
bool solve()
Call to solve NNLS problem.
Epetra_Vector b_
Epetra Vector b allocated to a single core.
Definition: NNLS_solver.h:179
HyperReductionParam hyper_reduction_param
Contains parameters for Hyperreduction.
void positive_set_matrix(Epetra_CrsMatrix &P_mat)
Creates a matrix using the columns in A in the set P.
void epetra_permutation_matrix(Epetra_CrsMatrix &P_mat)
Creates square permutation matrix based off the active/inactive set, no longer in use...
void add_into_x(Epetra_Vector &temp, double alpha)
Adds the values of temp times alpha into the solution vector x.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: NNLS_solver.h:168