Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
31namespace pism {
32
33//! TAO (inverse modeling) utilities.
34namespace taoutil {
35
36//! Encapsulate TAO's TaoSolverTerminationReason codes as a PISM TerminationReason.
38public:
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*/
91template<class Problem>
93public:
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
156protected:
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*/
182template<class Problem>
184public:
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
194protected:
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);
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*/
229template<class Problem>
231public:
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 }
242protected:
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);
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*/
276template<class Problem>
278public:
279 static void connect(Tao tao, Problem &p) {
280 PetscErrorCode ierr;
281 ierr = TaoSetVariableBoundsRoutine(tao,
283 &p);
284 PISM_CHK(ierr, "TaoSetVariableBoundsRoutine");
285 }
286
287protected:
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);
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*/
321template<class Problem>
323public:
324 static void connect(Tao tao, Problem &p) {
325 PetscErrorCode ierr;
326 ierr = TaoSetGradientRoutine(tao,
328 &p);
329 PISM_CHK(ierr, "TaoSetGradientRoutine");
330 }
331
332protected:
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);
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*/
366template<class Problem>
368public:
369 static void connect(Tao tao, Problem &p) {
370 PetscErrorCode ierr;
371 ierr = TaoSetConvergenceTest(tao,
373 &p);
374 PISM_CHK(ierr, "TaoSetConvergenceTest");
375 }
376protected:
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);
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*/
410template<class Problem, void (Problem::*Callback)(Tao,Vec,double*,Vec) >
412public:
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 }
427protected:
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);
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*/
460template<class Problem>
462public:
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 }
482protected:
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);
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);
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);
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
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 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
virtual std::shared_ptr< TerminationReason > solve()
Solve the minimization problem.
Definition TaoUtil.hh:117
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)