PISM, A Parallel Ice Sheet Model  stable v2.1.1 committed by Constantine Khrulev on 2024-12-04 13:36:58 -0900
TaoUtil.hh
Go to the documentation of this file.
1 // Copyright (C) 2012, 2013, 2014, 2015, 2017, 2022, 2023, 2024 David Maxwell and Constantine Khroulev
2 //
3 // This file is part of PISM.
4 //
5 // PISM is free software; you can redistribute it and/or modify it under the
6 // terms of the GNU General Public License as published by the Free Software
7 // Foundation; either version 3 of the License, or (at your option) any later
8 // version.
9 //
10 // PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with PISM; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 #ifndef TAOUTIL_HH_W42GJNRO
20 #define TAOUTIL_HH_W42GJNRO
21 
22 #include <petsc.h>
23 #include <stdexcept>
24 #include <string>
25 
26 #include "pism/util/pism_utilities.hh"
27 #include "pism/util/TerminationReason.hh"
28 #include "pism/util/petscwrappers/Tao.hh"
29 #include "pism/util/error_handling.hh"
30 
31 namespace pism {
32 
33 //! TAO (inverse modeling) utilities.
34 namespace taoutil {
35 
36 //! Encapsulate TAO's TaoSolverTerminationReason codes as a PISM TerminationReason.
38 public:
39  TAOTerminationReason(TaoConvergedReason r);
40  virtual void get_description(std::ostream &desc,int indent_level=0);
41 };
42 
43 //! \brief An interface for solving an optimization problem with TAO where the
44 //! problem itself is defined by a separate Problem class.
45 /*! The primary interface to a TAO optimization problem is mediated by a PETSc-style
46  `TaoSolver` object. The PISM TaoBasicSolver C++ class wraps a `TaoSolver` and some of
47  its initialization boilierplate, and allows a separate class to define the function to be minimized.
48 
49  To use a TaoBasicSolver you create a `Problem` class that defines the objective function and initial
50  guess, as well any auxilliary callbacks desired. The Problem class must define a
51 
52  \code
53  void Problem::connect(TaoSolver solver);
54  \endcode
55 
56  method which gives the `Problem` an opportunity to register its methods as callbacks to the solver,
57  perhaps taking advantage of the various `TaoFooCallback` classes provided in TaoUtil.hh to facilitate this.
58  For example, a problem class MyProblem that did nothing more than register a combined objective/gradient
59  callback could define
60 
61  \code
62  void MyProblem::connect(TaoSolver tao) {
63  typedef TaoObjGradCallback<Problem,&MyProblem::evaluateObjectiveAndGradient> ObjGradCallback;
64  ObjGradCallback::connect(tao,*this);
65  }
66  \endcode
67 
68  In addition to the `connect` method, a `Problem` must define
69  \code
70  TerminationReason::Ptr MyProblem::formInitialGuess(Vec *v)
71  \endcode
72  which allows the problem to set the initial guess for optimization. If the minimization
73  is successful, the solution will be found in the same vector that was returned by this method.
74 
75  Assuming a `MyProblem` called `problem` has been constructed, solution
76  of the minimization is done using, for example, the TAO algorithm
77  tao_cg:
78 
79  \code
80  TaoBasicSolver<MyProblem> solver(com,"tao_cg",problem);
81 
82  TerminationReason::Ptr reason = solver.solve();
83 
84  if (reason->succeeded()) {
85  printf("Success: %s\n",reason->description().c_str());
86  } else {
87  printf("Failure: %s\n",reason->description().c_str());
88  }
89  \endcode
90 */
91 template<class Problem>
93 public:
94 
95  //! Construct a solver to solve `prob` using TAO algorithm `tao_type`.
96  TaoBasicSolver(MPI_Comm comm, const std::string & tao_type, Problem &prob)
97  : m_comm(comm), m_problem(prob) {
98  PetscErrorCode ierr;
99 
100  ierr = TaoCreate(m_comm, m_tao.rawptr());
101  PISM_CHK(ierr, "TaoCreate");
102 
103  ierr = TaoSetType(m_tao,tao_type.c_str());
104  PISM_CHK(ierr, "TaoSetType");
105 
106  m_problem.connect(m_tao);
107 
108  ierr = TaoSetFromOptions(m_tao);
109  PISM_CHK(ierr, "TaoSetFromOptions");
110  }
111 
112  virtual ~TaoBasicSolver() {
113  // empty
114  }
115 
116  //! Solve the minimization problem.
117  virtual std::shared_ptr<TerminationReason> solve() {
118  PetscErrorCode ierr;
119 
120  /* Solve the application */
121  Vec x0;
122  auto reason = m_problem.formInitialGuess(&x0);
123  if (reason->failed()) {
124  auto root_cause = reason;
125  reason.reset(new GenericTerminationReason(-1, "Unable to form initial guess"));
126  reason->set_root_cause(root_cause);
127  return reason;
128  }
129 #if PETSC_VERSION_LT(3,17,0)
130  ierr = TaoSetInitialVector(m_tao, x0);
131 #else
132  ierr = TaoSetSolution(m_tao, x0);
133 #endif
134  PISM_CHK(ierr, "TaoSetInitialVector");
135 
136  ierr = TaoSolve(m_tao);
137  PISM_CHK(ierr, "TaoSolve");
138 
139  TaoConvergedReason tao_reason;
140  ierr = TaoGetConvergedReason(m_tao, &tao_reason);
141  PISM_CHK(ierr, "TaoGetConvergedReason");
142 
143  reason.reset(new TAOTerminationReason(tao_reason));
144  return reason;
145  }
146 
147  virtual void setMaximumIterations(int max_it) {
148  PetscErrorCode ierr = TaoSetMaximumIterations(m_tao, max_it);
149  PISM_CHK(ierr, "TaoSetMaximumIterations");
150  }
151 
152  virtual Problem &problem() {
153  return m_problem;
154  }
155 
156 protected:
157  MPI_Comm m_comm;
159  Problem &m_problem;
160 };
161 
162 
163 //! \brief Adaptor to connect a TAO objective function callback to a C++ object method.
164 /*! The TAO library interfaces with user code via C-style callback functions.
165  This class makes it convenient to associate a TAO Objective callback
166  with a C++ object method. To assign
167  \code
168  void MyObject::evaluateObjective(TaoSolver tao,Vec x, double *value);
169  \endcode
170 
171  as the objective function to a `TaoSolver` `tao`,
172 
173  \code
174  MyObject obj;
175  TaoObjectiveCallback<MyObject>::connect(tao,obj);
176  \endcode
177 
178  The method name `evaluateObjective` for the callback is hard-coded.
179  See TaoObjGradCallback for a technique to allow
180  the method name to be specified (at the expense of a little more cumbersome code).
181 */
182 template<class Problem>
184 public:
185 
186  static void connect(Tao tao, Problem &p) {
187  PetscErrorCode ierr;
188  ierr = TaoSetObjectiveRoutine(tao,
190  &p);
191  PISM_CHK(ierr, "TaoSetObjectiveRoutine");
192  }
193 
194 protected:
195  static PetscErrorCode callback(Tao tao, Vec x, double *value, void *ctx) {
196  try {
197  Problem *p = reinterpret_cast<Problem *>(ctx);
198  PetscErrorCode ierr = p->evaluateObjective(tao,x,value); CHKERRQ(ierr);
199  } catch (...) {
200  MPI_Comm com = MPI_COMM_SELF;
201  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
202  handle_fatal_errors(com);
203  SETERRQ(com, 1, "A PISM callback failed");
204  }
205  return 0;
206  }
207 };
208 
209 
210 //! \brief Adaptor to connect a TAO monitoring callback to a C++ object method.
211 /*! The TAO library interfaces with user code via C-style callback functions.
212  This class makes it convenient to associate a TAO Monitor callback
213  with a C++ object method. To assign
214  \code
215  void MyObject::monitorTao(Tao tao)
216  \endcode
217 
218  as the objective function to a `Tao` `tao`,
219 
220  \code
221  MyObject obj;
222  TaoMonitorCallback<MyObject>::connect(tao,obj);
223  \endcode
224 
225  The method name `monitorTao` for the callback is hard-coded.
226  See TaoObjGradCallback for a technique to allow
227  the method name to be specified (at the expense of a little more cumbersome code).
228 */
229 template<class Problem>
231 public:
232  static void connect(Tao tao, Problem &p) {
233 #if PETSC_VERSION_LT(3,21,0)
234  PetscErrorCode ierr = TaoSetMonitor(tao,
235 #else
236  PetscErrorCode ierr = TaoMonitorSet(tao,
237 #endif
239  &p, NULL);
240  PISM_CHK(ierr, "TaoSetMonitor");
241  }
242 protected:
243  static PetscErrorCode callback(Tao tao, void *ctx) {
244  try {
245  Problem *p = reinterpret_cast<Problem *>(ctx);
246  p->monitorTao(tao);
247  } catch (...) {
248  MPI_Comm com = MPI_COMM_SELF;
249  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
250  handle_fatal_errors(com);
251  SETERRQ(com, 1, "A PISM callback failed");
252  }
253  return 0;
254  }
255 };
256 
257 //! \brief Adaptor to connect a TAO objective function callback to a C++ object method.
258 /*! The TAO library interfaces with user code via C-style callback functions.
259  This class makes it convenient to associate a TAO VariableBounds callback
260  with a C++ object method. To assign
261  \code
262  void MyObject::getVariableBounds(Tao tao,Vec lo, Vec hi);
263  \endcode
264 
265  as the objective function to a `Tao` `tao`,
266 
267  \code
268  MyObject obj;
269  TaoGetVariableBoundsCallback<MyObject>::connect(tao,obj);
270  \endcode
271 
272  The method name `getVariableBounds` for the callback is hard-coded.
273  See TaoObjGradCallback for a technique to allow
274  the method name to be specified (at the expense of a little more cumbersome code).
275 */
276 template<class Problem>
278 public:
279  static void connect(Tao tao, Problem &p) {
280  PetscErrorCode ierr;
281  ierr = TaoSetVariableBoundsRoutine(tao,
283  &p);
284  PISM_CHK(ierr, "TaoSetVariableBoundsRoutine");
285  }
286 
287 protected:
288  static PetscErrorCode callback(Tao tao, Vec lo, Vec hi, void *ctx) {
289  try {
290  Problem *p = reinterpret_cast<Problem *>(ctx);
291  p->getVariableBounds(tao,lo,hi);
292  } catch (...) {
293  MPI_Comm com = MPI_COMM_SELF;
294  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
295  handle_fatal_errors(com);
296  SETERRQ(com, 1, "A PISM callback failed");
297  }
298  return 0;
299  }
300 };
301 
302 //! \brief Adaptor to connect a TAO objective gradient callback to a C++ object method.
303 /*! The TAO library interfaces with user code via C-style callback functions.
304  This class makes it convenient to associate a TAO Objective Gradient callback
305  with a C++ object method. To assign
306  \code
307  void MyObject::evaluateGradient(Tao tao,Vec x, Vec gradient);
308  \endcode
309 
310  as the objective function to a `Tao` `tao`,
311 
312  \code
313  MyObject obj;
314  TaoGradientCallback<MyObject>::connect(tao,obj);
315  \endcode
316 
317  The method name `evaluateGradient` for the callback is hard-coded.
318  See TaoObjGradCallback for a technique to allow
319  the method name to be specified (at the expense of a little more cumbersome code).
320 */
321 template<class Problem>
323 public:
324  static void connect(Tao tao, Problem &p) {
325  PetscErrorCode ierr;
326  ierr = TaoSetGradientRoutine(tao,
328  &p);
329  PISM_CHK(ierr, "TaoSetGradientRoutine");
330  }
331 
332 protected:
333  static PetscErrorCode callback(Tao tao, Vec x, Vec gradient, void *ctx) {
334  try {
335  Problem *p = reinterpret_cast<Problem *>(ctx);
336  p->evaluateGradient(tao,x,gradient);
337  } catch (...) {
338  MPI_Comm com = MPI_COMM_SELF;
339  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
340  handle_fatal_errors(com);
341  SETERRQ(com, 1, "A PISM callback failed");
342  }
343  return 0;
344  }
345 };
346 
347 //! \brief Adaptor to connect a TAO objective function callback to a C++ object method.
348 /*! The TAO library interfaces with user code via C-style callback functions.
349  This class makes it convenient to associate a TAO convergence monitoring callback
350  with a C++ object method. To assign
351  \code
352  void MyObject::convergenceTest(Tao tao);
353  \endcode
354 
355  as the convergence test function to a `Tao` `tao`,
356 
357  \code
358  MyObject obj;
359  TaoConvergenceCallback<MyObject>::connect(tao,obj);
360  \endcode
361 
362  The method name `convergenceTest` for the callback is hard-coded.
363  See TaoObjGradCallback for a technique to allow
364  the method name to be specified (at the expense of a little more cumbersome code).
365 */
366 template<class Problem>
368 public:
369  static void connect(Tao tao, Problem &p) {
370  PetscErrorCode ierr;
371  ierr = TaoSetConvergenceTest(tao,
373  &p);
374  PISM_CHK(ierr, "TaoSetConvergenceTest");
375  }
376 protected:
377  static PetscErrorCode callback(Tao tao, void *ctx) {
378  try {
379  Problem *p = reinterpret_cast<Problem *>(ctx);
380  p->convergenceTest(tao);
381  } catch (...) {
382  MPI_Comm com = MPI_COMM_SELF;
383  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
384  handle_fatal_errors(com);
385  SETERRQ(com, 1, "A PISM callback failed");
386  }
387  return 0;
388  }
389 };
390 
391 
392 //! \brief Adaptor to connect a TAO objective and gradient function callback to a C++ object method.
393 /*! The TAO library interfaces with user code via C-style callback functions.
394  This class makes it convenient to associate a TAO combined objective value and gradient
395  callback with a C++ object method. To assign
396  \code
397  void MyObject::someObjectiveFunction(Tao tao,Vec x, double *value, Vec gradient);
398  \endcode
399 
400  as the convergence test function to a `Tao` `tao`,
401 
402  \code
403  MyObject obj;
404  typedef TaoObjGradCallback<MyObject,&MyObject::someObjectiveFunction> ObjGradCallback;
405  ObjGradCallback::connect(tao,obj);
406  \endcode
407 
408  Note that the method name for the callback must be specified explicitly via a template argument.
409 */
410 template<class Problem, void (Problem::*Callback)(Tao,Vec,double*,Vec) >
412 public:
413  static void connect(Tao tao, Problem &p) {
414  PetscErrorCode ierr;
415 #if PETSC_VERSION_LT(3,17,0)
416  ierr = TaoSetObjectiveAndGradientRoutine(tao,
418  &p);
419 #else
420  ierr = TaoSetObjectiveAndGradient(tao,
421  NULL,
423  &p);
424 #endif
425  PISM_CHK(ierr, "TaoSetObjectiveAndGradientRoutine");
426  }
427 protected:
428  static PetscErrorCode callback(Tao tao, Vec x, double *value, Vec gradient, void *ctx) {
429  try {
430  Problem *p = reinterpret_cast<Problem *>(ctx);
431  (p->*Callback)(tao,x,value,gradient);
432  } catch (...) {
433  MPI_Comm com = MPI_COMM_SELF;
434  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
435  handle_fatal_errors(com);
436  SETERRQ(com, 1, "A PISM callback failed");
437  }
438  return 0;
439  }
440 };
441 
442 //! \brief Adaptor to connect a TAO objective function callback to a C++ object method.
443 /*! The TAO library interfaces with user code via C-style callback functions.
444  This class makes it convenient to associate a TAO Linearly Constrained Augmented Lagrangian (LCL)
445  callbacks with C++ object methods. To assign
446  \code
447  void MyObject::evaluateConstraints(Tao tao,Vec x,Vec c);
448  void MyObject::evaluateConstraintsJacobianState(Tao tao, Vec x, Mat *J, Mat *Jpc, Mat *Jinv, MatStructure *structure);
449  void MyObject::evaluateConstraintsJacobianDesign(Tao tao, Vec x, Mat *J);
450  \endcode
451  as the LCL callbacks to a `Tao` `tao`,
452 
453  \code
454  MyObject obj;
455  TaoLCLCallback<MyObject>::connect(tao,obj);
456  \endcode
457 
458  The method names for the callback (`evaluateConstraints`, etc.) are hard-coded.
459 */
460 template<class Problem>
462 public:
463  static void connect(Tao tao, Problem &p, Vec c, Mat Jc, Mat Jd, Mat Jcpc=NULL, Mat Jcinv=NULL) {
464  PetscErrorCode ierr;
465 
466  ierr = TaoSetConstraintsRoutine(tao, c,
468  PISM_CHK(ierr, "TaoSetConstraintsRoutine");
469 
470  if (Jcpc == NULL) {
471  Jcpc = Jc;
472  }
473 
474  ierr = TaoSetJacobianStateRoutine(tao, Jc, Jcpc, Jcinv,
476  PISM_CHK(ierr, "TaoSetJacobianStateRoutine");
477 
478  ierr = TaoSetJacobianDesignRoutine(tao, Jd,
480  PISM_CHK(ierr, "TaoSetJacobianDesignRoutine");
481  }
482 protected:
483  static PetscErrorCode constraints_callback(Tao tao, Vec x, Vec c, void*ctx) {
484  try {
485  Problem *p = reinterpret_cast<Problem *>(ctx);
486  p->evaluateConstraints(tao, x, c);
487  } catch (...) {
488  MPI_Comm com = MPI_COMM_SELF;
489  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
490  handle_fatal_errors(com);
491  SETERRQ(com, 1, "A PISM callback failed");
492  }
493  return 0;
494  }
495 
496  static PetscErrorCode jacobian_state_callback(Tao tao, Vec x, Mat J, Mat Jpc,
497  Mat Jinv, void*ctx) {
498  try {
499  Problem *p = reinterpret_cast<Problem *>(ctx);
500  // The MatStructure argument is not used in PETSc 3.5, but I want
501  // to preserve the signature of
502  // evaluateConstraintsJacobianState(...) for now -- (CK)
503  MatStructure structure;
504  p->evaluateConstraintsJacobianState(tao, x, J, Jpc, Jinv, &structure);
505  } catch (...) {
506  MPI_Comm com = MPI_COMM_SELF;
507  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
508  handle_fatal_errors(com);
509  SETERRQ(com, 1, "A PISM callback failed");
510  }
511  return 0;
512  }
513 
514  static PetscErrorCode jacobian_design_callback(Tao tao, Vec x, Mat J, void*ctx) {
515  try {
516  Problem *p = reinterpret_cast<Problem *>(ctx);
517  p->evaluateConstraintsJacobianDesign(tao, x, J);
518  } catch (...) {
519  MPI_Comm com = MPI_COMM_SELF;
520  PetscErrorCode ierr = PetscObjectGetComm((PetscObject)tao, &com); CHKERRQ(ierr);
521  handle_fatal_errors(com);
522  SETERRQ(com, 1, "A PISM callback failed");
523  }
524  return 0;
525  }
526 };
527 
528 } // end of namespace taoutil
529 } // end of namespace pism
530 
531 #endif /* end of include guard: TAOUTIL_HH_W42GJNRO */
T * rawptr()
Definition: Wrapper.hh:39
TAOTerminationReason(TaoConvergedReason r)
Definition: TaoUtil.cc:24
virtual void get_description(std::ostream &desc, int indent_level=0)
Definition: TaoUtil.cc:28
Encapsulate TAO's TaoSolverTerminationReason codes as a PISM TerminationReason.
Definition: TaoUtil.hh:37
virtual std::shared_ptr< TerminationReason > solve()
Solve the minimization problem.
Definition: TaoUtil.hh:117
virtual Problem & problem()
Definition: TaoUtil.hh:152
TaoBasicSolver(MPI_Comm comm, const std::string &tao_type, Problem &prob)
Construct a solver to solve prob using TAO algorithm tao_type.
Definition: TaoUtil.hh:96
virtual void setMaximumIterations(int max_it)
Definition: TaoUtil.hh:147
An interface for solving an optimization problem with TAO where the problem itself is defined by a se...
Definition: TaoUtil.hh:92
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:369
static PetscErrorCode callback(Tao tao, void *ctx)
Definition: TaoUtil.hh:377
Adaptor to connect a TAO objective function callback to a C++ object method.
Definition: TaoUtil.hh:367
static PetscErrorCode callback(Tao tao, Vec lo, Vec hi, void *ctx)
Definition: TaoUtil.hh:288
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:279
Adaptor to connect a TAO objective function callback to a C++ object method.
Definition: TaoUtil.hh:277
static PetscErrorCode callback(Tao tao, Vec x, Vec gradient, void *ctx)
Definition: TaoUtil.hh:333
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:324
Adaptor to connect a TAO objective gradient callback to a C++ object method.
Definition: TaoUtil.hh:322
static PetscErrorCode constraints_callback(Tao tao, Vec x, Vec c, void *ctx)
Definition: TaoUtil.hh:483
static void connect(Tao tao, Problem &p, Vec c, Mat Jc, Mat Jd, Mat Jcpc=NULL, Mat Jcinv=NULL)
Definition: TaoUtil.hh:463
static PetscErrorCode jacobian_design_callback(Tao tao, Vec x, Mat J, void *ctx)
Definition: TaoUtil.hh:514
static PetscErrorCode jacobian_state_callback(Tao tao, Vec x, Mat J, Mat Jpc, Mat Jinv, void *ctx)
Definition: TaoUtil.hh:496
Adaptor to connect a TAO objective function callback to a C++ object method.
Definition: TaoUtil.hh:461
static PetscErrorCode callback(Tao tao, void *ctx)
Definition: TaoUtil.hh:243
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:232
Adaptor to connect a TAO monitoring callback to a C++ object method.
Definition: TaoUtil.hh:230
static PetscErrorCode callback(Tao tao, Vec x, double *value, Vec gradient, void *ctx)
Definition: TaoUtil.hh:428
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:413
Adaptor to connect a TAO objective and gradient function callback to a C++ object method.
Definition: TaoUtil.hh:411
static void connect(Tao tao, Problem &p)
Definition: TaoUtil.hh:186
static PetscErrorCode callback(Tao tao, Vec x, double *value, void *ctx)
Definition: TaoUtil.hh:195
Adaptor to connect a TAO objective function callback to a C++ object method.
Definition: TaoUtil.hh:183
#define PISM_CHK(errcode, name)
void handle_fatal_errors(MPI_Comm com)
const int J[]
Definition: ssafd_code.cc:34