optimize_least_squares_petsc.cpp
Go to the documentation of this file.
1 // Copyright 2019, University of Maryland and the MANGO development team.
2 //
3 // This file is part of MANGO.
4 //
5 // MANGO is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // MANGO is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with MANGO. If not, see
17 // <https://www.gnu.org/licenses/>.
18 
19 #include <iostream>
20 #include <iomanip>
21 #include <stdexcept>
22 #include <cassert>
23 #include "mango.hpp"
24 #include "Least_squares_solver.hpp"
25 #include "Package_petsc.hpp"
26 
27 #ifdef MANGO_PETSC_AVAILABLE
28 #include <petsctao.h>
29 #endif
30 
31 static char help[]="";
32 
34 #ifdef MANGO_PETSC_AVAILABLE
35 
36  int N_parameters = solver->N_parameters;
37  int N_terms = solver->N_terms;
38 
39  // The need for this line is described on https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Sys/PetscInitialize.html
40  PETSC_COMM_WORLD = MPI_COMM_SELF;
41 
42  int ierr;
43  ierr = PetscInitialize(&(solver->argc),&(solver->argv),(char *)0,help);
44  if (ierr) throw std::runtime_error("Error in PetscInitialize in mango::Package_petsc::optimize_least_squares().");
45  ierr = PetscInitializeFortran();
46 
47  Tao my_tao;
48  TaoCreate(PETSC_COMM_SELF, &my_tao);
49 
50  Vec tao_state_vec;
51  VecCreateSeq(PETSC_COMM_SELF, N_parameters, &tao_state_vec);
52  Vec tao_residual_vec;
53  VecCreateSeq(PETSC_COMM_SELF, N_terms, &tao_residual_vec);
54  Mat petsc_Jacobian;
55  MatCreateSeqDense(MPI_COMM_SELF,N_terms,N_parameters,NULL,&petsc_Jacobian);
56 
57  // Set initial condition
58  double* temp_array;
59  VecGetArray(tao_state_vec, &temp_array);
60  memcpy(temp_array, solver->state_vector, N_parameters * sizeof(double));
61  VecRestoreArray(tao_state_vec, &temp_array);
62  if (solver->verbose > 0) {
63  std::cout << "Here comes petsc vec for initial condition:" << std::endl;
64  VecView(tao_state_vec, PETSC_VIEWER_STDOUT_SELF);
65  }
66  TaoSetInitialVector(my_tao, tao_state_vec);
67 
68  if (solver->verbose > 0) std::cout << "PETSc has been initialized." << std::endl;
69 
70 #if (PETSC_VERSION_MAJOR < 3 || (PETSC_VERSION_MAJOR==3 && PETSC_VERSION_MINOR < 11))
71  TaoSetSeparableObjectiveRoutine(my_tao, tao_residual_vec, &mango_petsc_residual_function, (void*)solver);
72 #else
73  TaoSetResidualRoutine(my_tao, tao_residual_vec, &mango_petsc_residual_function, (void*)solver);
74 #endif
75 
76  switch (solver->algorithm) {
77  case PETSC_POUNDERS:
78  TaoSetType(my_tao, TAOPOUNDERS);
79  break;
80  case PETSC_BRGN:
81 #if (PETSC_VERSION_MAJOR < 3 || (PETSC_VERSION_MAJOR==3 && PETSC_VERSION_MINOR < 11))
82  throw std::runtime_error("The petsc_brgn algorithm requires PETSc version 3.11 or newer.");
83 #else
84  TaoSetJacobianResidualRoutine(my_tao, petsc_Jacobian, petsc_Jacobian, &mango_petsc_Jacobian_function, (void*)solver);
85  TaoSetType(my_tao, TAOBRGN);
86  break;
87 #endif
88  default:
89  std::cerr << "Should not get here! algorithm = " << solver->algorithm << " i.e. " << algorithms[solver->algorithm].name << std::endl;
90  throw std::runtime_error("Error in mango::problem::optimize_least_squares_petsc()");
91  }
92 
93  TaoSetMaximumFunctionEvaluations(my_tao, (PetscInt) solver->max_function_and_gradient_evaluations);
94 
95  Vec lower_bounds_vec, upper_bounds_vec;
96  if (solver->bound_constraints_set) {
97  VecCreateSeq(PETSC_COMM_SELF, N_parameters, &lower_bounds_vec);
98  VecCreateSeq(PETSC_COMM_SELF, N_parameters, &upper_bounds_vec);
99 
100  for (int j=0; j<N_parameters; j++) {
101  VecSetValue(lower_bounds_vec, j, solver->lower_bounds[j], INSERT_VALUES);
102  VecSetValue(upper_bounds_vec, j, solver->upper_bounds[j], INSERT_VALUES);
103  }
104  VecAssemblyBegin(lower_bounds_vec);
105  VecAssemblyBegin(upper_bounds_vec);
106  VecAssemblyEnd(lower_bounds_vec);
107  VecAssemblyEnd(upper_bounds_vec);
108 
109  TaoSetVariableBounds(my_tao, lower_bounds_vec, upper_bounds_vec);
110  }
111 
112  // TaoSetTolerances(my_tao, 1e-30, 1e-30, 1e-30);
113  TaoSetFromOptions(my_tao);
114  TaoSolve(my_tao);
115  if (solver->verbose > 0) TaoView(my_tao, PETSC_VIEWER_STDOUT_SELF);
116  //TaoView(my_tao, PETSC_VIEWER_STDOUT_SELF);
117 
118  // Copy PETSc solution to the mango state vector.
119  VecGetArray(tao_state_vec, &temp_array);
120  memcpy(solver->state_vector, temp_array, N_parameters * sizeof(double));
121  VecRestoreArray(tao_state_vec, &temp_array);
122 
123 
124  TaoDestroy(&my_tao);
125  VecDestroy(&tao_state_vec);
126 
127  PetscFinalize();
128 
129 #else
130  throw std::runtime_error("Error! A PETSc algorithm was requested, but Mango was compiled without PETSc support.");
131 #endif
132 
133 }
134 
135 ///////////////////////////////////////////////////////////////////////////////////
136 ///////////////////////////////////////////////////////////////////////////////////
137 
138 #ifdef MANGO_PETSC_AVAILABLE
139 PetscErrorCode mango::Package_petsc::mango_petsc_residual_function(Tao my_tao, Vec x, Vec f, void* user_context) {
140 
141  int j;
142  const double* x_array;
143  double* f_array;
144  VecGetArrayRead(x, &x_array);
145  VecGetArray(f, &f_array);
146 
147  Least_squares_solver* solver = (Least_squares_solver*) user_context;
148 
149  assert(solver->mpi_partition->get_proc0_world()); // This subroutine should only ever be called by proc 0.
150 
151  bool failed;
152  solver->residual_function_wrapper(x_array, f_array, &failed);
153 
154  if (solver->verbose > 0) {
155  std::cout << "mango_petsc_residual_function before sigma shift. state_vector:";
156  for (j=0; j < solver->N_parameters; j++) {
157  std::cout << std::setw(24) << std::setprecision(15) << x_array[j];
158  }
159  std::cout << std::endl;
160  std::cout << "residual:";
161  for (j=0; j < solver->N_terms; j++) {
162  std::cout << std::setw(24) << std::setprecision(15) << f_array[j];
163  }
164  std::cout << std::endl << std::flush;
165  }
166 
167  // PETSc's definition of the residual function does not include sigmas or targets, so shift and scale the mango residuals appropriately:
168  for (j=0; j<solver->N_terms; j++) {
169  f_array[j] = (f_array[j] - solver->targets[j]) / solver->sigmas[j];
170  if (failed) f_array[j] = mango::NUMBER_FOR_FAILED;
171  }
172 
173  if (solver->verbose > 0) {
174  std::cout << "mango_petsc_residual_function after sigma shift. state_vector:";
175  for (j=0; j < solver->N_parameters; j++) {
176  std::cout << std::setw(24) << std::setprecision(15) << x_array[j];
177  }
178  std::cout << std::endl;
179  std::cout << "residual:";
180  for (j=0; j < solver->N_terms; j++) {
181  std::cout << std::setw(24) << std::setprecision(15) << f_array[j];
182  }
183  std::cout << std::endl << std::flush;
184  }
185 
186  VecRestoreArrayRead(x, &x_array);
187  VecRestoreArray(f, &f_array);
188 
189  return(0);
190 }
191 
192 ///////////////////////////////////////////////////////////////////////////////////
193 ///////////////////////////////////////////////////////////////////////////////////
194 
195 PetscErrorCode mango::Package_petsc::mango_petsc_Jacobian_function(Tao my_tao, Vec x, Mat Jacobian, Mat preconditioner, void* user_context) {
196  int j;
197  const double* x_array;
198  double* Jacobian_array;
199  VecGetArrayRead(x, &x_array);
200  MatDenseGetArray(Jacobian, &Jacobian_array);
201 
202  Least_squares_solver* solver = (Least_squares_solver*) user_context;
203  // Introduce shorthand:
204  int N_parameters = solver->N_parameters;
205  int N_terms = solver->N_terms;
206 
207  assert(solver->mpi_partition->get_proc0_world()); // This subroutine should only ever be called by proc 0.
208 
209  if (solver->verbose > 0) {
210  std::cout << "mango_petsc_Jacobian_function before sigma shift. state_vector:";
211  for (j=0; j < N_parameters; j++) {
212  std::cout << std::setw(24) << std::setprecision(15) << x_array[j];
213  }
214  std::cout << std::endl << std::flush;
215  }
216 
217  solver->finite_difference_Jacobian(x_array, solver->residuals, Jacobian_array);
218  // PETSc does not actually use the residuals computed here, only the Jacobian.
219 
220 
221  // PETSc's definition of the residual function does not include sigmas or targets, so scale the Jacobian appropriately.
222  // There is probably a faster approach than these explicit loops, but I'll worry about optimizing this later.
223  for (int j_parameter = 0; j_parameter < N_parameters; j_parameter++) {
224  for (int j_term = 0; j_term < N_terms; j_term++) {
225  // MANGO's convention is Jacobian[j_parameter*N_terms+j_term].
226  // PETSc's convention for the arrays underlying MatDense is that the Jacobian is in column major order, i.e. Fortran convention.
227  // These are consistent.
228  Jacobian_array[j_parameter*N_terms+j_term] /= solver->sigmas[j_term];
229  }
230  }
231 
232  VecRestoreArrayRead(x, &x_array);
233  MatDenseRestoreArray(Jacobian, &Jacobian_array);
234 
235  return(0);
236 }
237 
238 #endif
Least_squares_solver.hpp
help
static char help[]
Definition: optimize_least_squares_petsc.cpp:31
mango::Least_squares_solver::N_terms
int N_terms
Definition: Least_squares_solver.hpp:38
mango::Least_squares_solver::sigmas
double * sigmas
Definition: Least_squares_solver.hpp:40
mango::Solver::verbose
int verbose
Definition: Solver.hpp:63
mango::PETSC_POUNDERS
@ PETSC_POUNDERS
Definition: mango.hpp:81
mango::Solver::argc
int argc
Definition: Solver.hpp:46
mango::Least_squares_solver
Definition: Least_squares_solver.hpp:28
Package_petsc.hpp
mango::Package_petsc::optimize_least_squares
void optimize_least_squares(Least_squares_solver *)
Definition: optimize_least_squares_petsc.cpp:33
mango::Solver::state_vector
double * state_vector
Definition: Solver.hpp:58
mango::algorithms
const algorithm_properties algorithms[NUM_ALGORITHMS]
A database of the algorithms that MANGO is aware of, including various properties of each algorithm.
Definition: mango.hpp:124
mango::Solver::mpi_partition
MPI_Partition * mpi_partition
Definition: Solver.hpp:65
mango::Solver::argv
char ** argv
Definition: Solver.hpp:47
mango.hpp
mango::Solver::N_parameters
int N_parameters
Definition: Solver.hpp:43
mango::NUMBER_FOR_FAILED
const double NUMBER_FOR_FAILED
A large finite number.
Definition: mango.hpp:52
mango::MPI_Partition::get_proc0_world
bool get_proc0_world()
Determine whether this MPI processor has rank 0 in MANGO's world communicator.
Definition: mpi_partition.cpp:56
mango::PETSC_BRGN
@ PETSC_BRGN
Definition: mango.hpp:82
mango::algorithm_properties::name
std::string name
A short string with the algorithm's name, e.g. 'petsc_pounders' or 'hopspack'.
Definition: mango.hpp:59
mango::Least_squares_solver::residual_function_wrapper
void residual_function_wrapper(const double *, double *, bool *)
Definition: residual_function_wrapper.cpp:26
mango::Least_squares_solver::targets
double * targets
Definition: Least_squares_solver.hpp:39