22boost::shared_ptr<IncrementalOptimizationContext>
24 boost::shared_ptr<IncrementalOptimizationProblem> problem,
26 if (!problem || !
state)
28 "Incremental optimization requires a problem and state");
31 "Validate incremental-optimization problem");
32 auto context = boost::make_shared<IncrementalOptimizationContext>();
33 context->problem = std::move(problem);
36 "Create incremental-optimization control data");
40 "Incremental-optimization problem returned incomplete control data");
42 if (
context->committedControl.get() ==
context->candidateControl.get())
45 "Candidate and committed controls must be distinct vectors");
48 DM control_dm =
context->problem->getControlDM();
52 "Incremental-optimization problem returned no control DM");
53 const Problem *control_problem_ptr =
nullptr;
55 "Get incremental-optimization control problem");
56 if (!control_problem_ptr)
59 "Incremental-optimization control DM has no MoFEM problem");
61 const MPI_Comm control_comm =
62 PetscObjectComm(
reinterpret_cast<PetscObject
>(control_dm));
64 const std::string constraint_problem_name =
65 control_problem_ptr->
getName() +
"_CONSTRAINTS";
67 constraint_problem_name.c_str()),
68 "Create incremental-optimization constraint DM");
70 "Set constraint-DM problem ownership");
72 "Set rectangular constraint-DM layout");
75 "Configure incremental-optimization constraint DM");
77 "Set up incremental-optimization constraint DM");
79 context->inequalityConstraints =
82 if (!
context->inequalityConstraints || !
context->inequalityJacobian)
85 "Incremental-optimization problem returned incomplete inequality "
89 PetscInt local_controls = 0;
90 PetscInt global_controls = 0;
91 PetscInt local_constraints = 0;
92 PetscInt global_constraints = 0;
93 PetscInt local_rows = 0;
94 PetscInt local_columns = 0;
95 PetscInt global_rows = 0;
96 PetscInt global_columns = 0;
98 "Get local incremental-control size");
100 "Get global incremental-control size");
102 VecGetLocalSize(
context->inequalityConstraints, &local_constraints),
103 "Get local inequality-constraint size");
105 VecGetSize(
context->inequalityConstraints, &global_constraints),
106 "Get global inequality-constraint size");
109 "Get local constraint-Jacobian size");
112 "Get global constraint-Jacobian size");
113 if (local_rows != local_constraints || global_rows != global_constraints ||
114 local_columns != local_controls || global_columns != global_controls)
117 "Constraint DM is incompatible with the constraint/control vectors");
131 "-incremental_optimization_equilibrium_tolerance",
132 &
context->equilibriumTolerance, PETSC_NULLPTR),
133 "Read incremental-optimization equilibrium tolerance");
134 if (!(
context->equilibriumTolerance > 0) ||
135 !std::isfinite(
context->equilibriumTolerance))
138 "Incremental-optimization equilibrium tolerance must be positive");
141 "Initialise equilibrated state");
144 "Initialise incremental-optimization reference control");
146 "Initialise baseline state snapshot");
148 "Initialise last-valid state snapshot");
150 "Initialise cached state snapshot");
154 return problem_ptr->solveState(trial_state, report);
161bool isFiniteSuccessfulObjective(
162 const IncrementalObjectiveEvaluation &evaluation) {
164 std::isfinite(evaluation.conservativeValue) &&
165 std::isfinite(evaluation.dissipativeValue) &&
166 std::isfinite(evaluation.equilibriumNorm);
169bool isFiniteSuccessfulEvaluation(
170 const IncrementalObjectiveEvaluation &evaluation) {
171 return isFiniteSuccessfulObjective(evaluation) &&
172 evaluation.smoothGradient.get() &&
173 evaluation.objectiveGradient.get() &&
174 std::isfinite(evaluation.smoothGradientNorm) &&
175 std::isfinite(evaluation.objectiveGradientNorm);
179clearIncrementalOptimizationForces(IncrementalOptimizationContext &
context) {
187struct IncrementalOptimizationSolveRollback {
188 boost::shared_ptr<IncrementalOptimizationContext>
context;
193 IncrementalOptimizationSolveRollback(
194 boost::shared_ptr<IncrementalOptimizationContext> context, Vec state,
201 const auto record_error = [&first_error](
const MoFEMErrorCode error) {
202 if (error && !first_error)
205 record_error(
context->rollbackTrial());
206 record_error(VecCopy(stateBefore,
context->equilibratedState));
207 record_error(VecCopy(stateBefore,
context->baselineState));
208 record_error(VecCopy(stateBefore,
context->lastValidState));
209 record_error(
context->problem->restoreState(
context->equilibratedState));
210 record_error(VecCopy(stateBefore, state));
211 record_error(clearIncrementalOptimizationForces(*context));
212 context->baselineValid =
false;
218 void dismiss() {
active =
false; }
220 ~IncrementalOptimizationSolveRollback() {
222 const auto error = rollback();
225 <<
"Incremental-optimization rollback failed with error "
232 boost::shared_ptr<IncrementalOptimizationContext>
context;
235 explicit IncrementalOptimizationTAOCtx(
236 boost::shared_ptr<IncrementalOptimizationContext> context)
238 "INCREMENTAL_OPTIMIZATION"),
241 [
this](Tao tao, Vec control, PetscReal *objective) {
242 return evaluate(tao, control, objective,
nullptr);
244 getGradientHook() = [
this](Tao tao, Vec control, Vec gradient) {
246 PetscReal objective = 0;
247 CHKERR evaluate(tao, control, &objective, gradient);
250 SETERRQ(PetscObjectComm(
reinterpret_cast<PetscObject
>(tao)),
252 "Cannot evaluate the incremental objective gradient");
255 getObjectiveAndGradientHook() =
256 [
this](Tao tao, Vec control, PetscReal *objective, Vec gradient) {
257 return evaluate(tao, control, objective, gradient);
261 static PetscErrorCode evaluateInequalityConstraints(Tao, Vec control,
265 auto *tao_context =
static_cast<IncrementalOptimizationTAOCtx *
>(data);
268 "Incremental inequality callback has no TAO context");
274 tao_context->context, control, constraints);
278 static PetscErrorCode evaluateInequalityJacobian(Tao, Vec control,
283 auto *tao_context =
static_cast<IncrementalOptimizationTAOCtx *
>(data);
286 "Incremental inequality-Jacobian callback has no TAO context");
287 if (jacobian != preconditioner)
288 SETERRQ(PetscObjectComm(
reinterpret_cast<PetscObject
>(jacobian)),
290 "Incremental inequality Jacobian and preconditioner must use "
295 tao_context->context, control, preconditioner);
299 MoFEMErrorCode evaluate(Tao tao, Vec control, PetscReal *objective,
303 IncrementalObjectiveEvaluation evaluation;
305 context, control, *objective, gradient, evaluation);
311 if (!std::isfinite(*objective) ||
312 !isFiniteSuccessfulObjective(evaluation) ||
313 (gradient && !isFiniteSuccessfulEvaluation(evaluation))) {
315 *objective = std::numeric_limits<PetscReal>::infinity();
317 CHKERR VecZeroEntries(gradient);
321 PetscInt iteration = 0;
322 CHKERR TaoGetIterationNumber(tao, &iteration);
323 const double conservative_increment =
324 evaluation.conservativeValue -
context->baselineConservativeValue;
326 <<
context->problem->getName() <<
" TAO evaluation at iteration "
327 << iteration <<
": objective " << *objective
328 <<
" (conservative increment " << conservative_increment
329 <<
", dissipation " << evaluation.dissipativeValue <<
")"
330 <<
", equilibrium residual " << evaluation.equilibriumNorm;
333 <<
context->problem->getName() <<
" TAO gradient norm "
334 << evaluation.objectiveGradientNorm;
342 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
347 "Incremental-optimization evaluation has an incomplete context");
349 PetscBool is_reference = PETSC_FALSE;
350 CHKERR VecEqual(control,
context->referenceControl, &is_reference);
351 if (!
context->baselineValid && !is_reference) {
356 evaluation = baseline_evaluation;
362 PetscBool cache_match = PETSC_FALSE;
363 CHKERR VecEqual(control,
context->cachedControl, &cache_match);
366 CHKERR VecGhostUpdateBegin(
context->candidateControl, INSERT_VALUES,
368 CHKERR VecGhostUpdateEnd(
context->candidateControl, INSERT_VALUES,
372 evaluation =
context->cachedEvaluation;
381 CHKERR VecGhostUpdateBegin(
context->candidateControl, INSERT_VALUES,
383 CHKERR VecGhostUpdateEnd(
context->candidateControl, INSERT_VALUES,
389 PetscLogDouble equilibrium_start = 0;
390 PetscLogDouble equilibrium_end = 0;
391 CHKERR PetscTime(&equilibrium_start);
394 CHKERR PetscTime(&equilibrium_end);
395 context->equilibriumSeconds += equilibrium_end - equilibrium_start;
396 if (callback_error) {
422 double conservative_value = 0;
423 CHKERR context->problem->evaluateConservativeValue(conservative_value);
424 double dissipative_value = 0;
427 if (!std::isfinite(conservative_value) ||
428 !std::isfinite(dissipative_value)) {
453 if (is_reference && !
context->baselineValid) {
454 context->baselineConservativeValue = conservative_value;
460 context->cachedEvaluation = evaluation;
468 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
476 PetscLogDouble gradient_start = 0;
477 PetscLogDouble gradient_end = 0;
478 CHKERR PetscTime(&gradient_start);
480 CHKERR PetscTime(&gradient_end);
481 context->gradientAssemblySeconds += gradient_end - gradient_start;
482 PetscReal gradient_norm = 0;
484 if (!std::isfinite(gradient_norm)) {
494 context->cachedEvaluation = evaluation;
499 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
500 Vec control, PetscReal &objective, Vec objective_gradient,
503 if (objective_gradient)
508 objective = std::numeric_limits<PetscReal>::infinity();
509 if (objective_gradient)
510 CHKERR VecZeroEntries(objective_gradient);
516 if (!std::isfinite(objective)) {
517 if (objective_gradient)
518 CHKERR VecZeroEntries(objective_gradient);
521 if (!objective_gradient)
529 PetscReal gradient_norm = 0;
530 CHKERR VecNorm(
context->objectiveGradient, NORM_2, &gradient_norm);
533 if (!std::isfinite(gradient_norm)) {
534 objective = std::numeric_limits<PetscReal>::infinity();
541 if (objective_gradient !=
context->objectiveGradient.get())
542 CHKERR VecCopy(
context->objectiveGradient, objective_gradient);
547 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
548 Vec control, Vec constraints) {
552 "Cannot evaluate incomplete incremental inequalities");
553 CHKERR VecZeroEntries(constraints);
554 CHKERR context->problem->evaluateInequalityConstraints(control,
560 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
561 Vec control, Mat jacobian) {
565 "Cannot assemble incomplete incremental inequality Jacobian");
568 PetscBool cache_match = PETSC_FALSE;
569 CHKERR VecEqual(control,
context->cachedControl, &cache_match);
571 evaluation =
context->cachedEvaluation;
574 context, control, evaluation, jacobian);
579 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
585 "Cannot assemble an inequality Jacobian from incomplete "
589 const bool has_finite_objective_data =
590 isFiniteSuccessfulObjective(evaluation) &&
594 if (has_finite_objective_data) {
595 PetscBool matches_candidate = PETSC_FALSE;
596 CHKERR VecEqual(control,
context->candidateControl, &matches_candidate);
597 if (!matches_candidate)
598 SETERRQ(PetscObjectComm(
reinterpret_cast<PetscObject
>(control)),
600 "Constraint Jacobian evaluation does not match the current "
601 "equilibrated control");
604 CHKERR MatZeroEntries(jacobian);
605 if (has_finite_objective_data) {
610 ->assembleInequalityJacobianWithoutSmoothGradient(control, jacobian);
615 const boost::shared_ptr<IncrementalOptimizationContext> &
context,
620 "Cannot solve an incomplete incremental-optimization problem");
626 IncrementalOptimizationSolveRollback rollback_guard(
context,
state,
630 auto tao_context = boost::make_shared<IncrementalOptimizationTAOCtx>(
context);
631 DM control_dm =
context->problem->getControlDM();
632 const MPI_Comm control_comm =
633 PetscObjectComm(
reinterpret_cast<PetscObject
>(control_dm));
637 CHKERR TaoSetOptionsPrefix(tao,
"incremental_optimization_");
638 CHKERR TaoRegisterNullSpace();
639 CHKERR TaoSetType(tao, TAOALMM);
640 Tao subsolver =
nullptr;
641 CHKERR TaoALMMGetSubsolver(tao, &subsolver);
642 CHKERR TaoSetType(subsolver, TAOBQNKTR);
643 CHKERR TaoSetInitialTrustRegionRadius(subsolver, 1e-3);
644 CHKERR TaoSetTolerances(tao, 1e-8, PETSC_DEFAULT, PETSC_DEFAULT);
645 CHKERR TaoSetConstraintTolerances(tao, 1e-8, PETSC_DEFAULT);
647 CHKERR ::TaoSetGradient(tao,
context->objectiveGradient,
649 CHKERR ::TaoSetObjectiveAndGradient(
652 CHKERR TaoSetInequalityConstraintsRoutine(
653 tao,
context->inequalityConstraints,
654 IncrementalOptimizationTAOCtx::evaluateInequalityConstraints,
656 CHKERR TaoSetJacobianInequalityRoutine(
658 IncrementalOptimizationTAOCtx::evaluateInequalityJacobian,
660 CHKERR TaoSetFromOptions(tao);
661 PetscBool is_almm = PETSC_FALSE;
662 PetscBool is_nullspace = PETSC_FALSE;
663 CHKERR PetscObjectTypeCompare(
reinterpret_cast<PetscObject
>(tao.get()),
665 CHKERR PetscObjectTypeCompare(
reinterpret_cast<PetscObject
>(tao.get()),
666 TAONULLSPACE, &is_nullspace);
667 if (!is_almm && !is_nullspace)
669 "Incremental constrained optimization currently requires "
670 "-incremental_optimization_tao_type almm or nullspace");
672 CHKERR TaoNullSpaceSetConstraints(
675 CHKERR TaoSetSolution(tao, tao_solution);
677 Vec tao_multipliers =
nullptr;
679 TaoALMMType almm_type;
680 CHKERR TaoALMMGetType(tao, &almm_type);
681 if (almm_type != TAO_ALMM_PHR)
683 "Incremental constrained optimization currently requires "
684 "-incremental_optimization_tao_almm_type phr so that returned "
685 "inequality multipliers use the physical nonnegative sign");
686 CHKERR TaoALMMGetMultipliers(tao, &tao_multipliers);
688 CHKERR TaoNullSpaceGetMultipliers(tao, &tao_multipliers);
690 CHKERR VecZeroEntries(tao_multipliers);
692 context->constraintDM, tao_multipliers);
696 CHKERR TaoSetRecycleHistory(tao, PETSC_TRUE);
698 TaoConvergedReason reason;
699 CHKERR TaoGetConvergedReason(tao, &reason);
700 const bool reached_iteration_limit = reason == TAO_DIVERGED_MAXITS;
701 if (reached_iteration_limit) {
704 <<
" incremental optimization reached the TAO iteration limit; "
705 "checking the final iterate before continuing";
706 }
else if (reason <= 0) {
708 "Incremental-optimization TAO did not converge: %s",
709 TaoConvergedReasons[reason]);
712 Vec solution =
nullptr;
713 CHKERR TaoGetSolution(tao, &solution);
714 PetscReal final_objective = 0;
715 CHKERR tao_context->evaluate(tao, solution, &final_objective,
717 if (tao_context->finalEvaluation.stateStatus !=
720 "Final TAO control does not have an equilibrated state");
721 if (!std::isfinite(final_objective))
723 "Final TAO control has a non-finite objective");
724 if (!isFiniteSuccessfulEvaluation(tao_context->finalEvaluation))
726 "Final TAO control has incomplete or non-finite objective data");
729 PetscBool candidate_matches_solution = PETSC_FALSE;
731 &candidate_matches_solution);
732 if (!candidate_matches_solution)
734 "Final equilibrated control does not match the TAO solution");
737 PetscReal gradient_absolute_tolerance = 0;
738 CHKERR TaoGetTolerances(tao, &gradient_absolute_tolerance, PETSC_NULLPTR,
740 PetscReal constraint_absolute_tolerance = 0;
741 CHKERR TaoGetConstraintTolerances(tao, &constraint_absolute_tolerance,
743 Vec inequality_multipliers =
nullptr;
745 CHKERR TaoALMMGetMultipliers(tao, &inequality_multipliers);
747 CHKERR TaoNullSpaceGetMultipliers(tao, &inequality_multipliers);
748 if (!inequality_multipliers)
750 "TAO did not provide final inequality multipliers");
751 std::string diagnostics;
754 inequality_multipliers, gradient_absolute_tolerance,
755 constraint_absolute_tolerance, diagnostics);
757 PetscBool validated_control_matches_solution = PETSC_FALSE;
759 &validated_control_matches_solution);
760 if (!validated_control_matches_solution)
762 "Validated control no longer matches the TAO solution");
765 CHKERR VecCopy(
context->candidateControl, accepted_solution);
766 CHKERR VecGhostUpdateBegin(accepted_solution, INSERT_VALUES,
768 CHKERR VecGhostUpdateEnd(accepted_solution, INSERT_VALUES, SCATTER_FORWARD);
775 accepted_solution,
context->equilibratedState,
780 context->baselineValid =
false;
782 rollback_guard.dismiss();
784 MOFEM_LOG(
"EP", reached_iteration_limit ? Sev::warning : Sev::inform)
785 <<
context->problem->getName() <<
" incremental optimization "
786 << (reached_iteration_limit ?
"accepted at the TAO iteration limit: "
788 <<
"equilibrium " << tao_context->finalEvaluation.equilibriumNorm
790 << tao_context->finalEvaluation.objectiveGradientNorm <<
", objective "
791 << final_objective <<
", state evaluations " <<
context->stateSolveCount
792 <<
", rejected trials " <<
context->rejectedTrialCount
793 <<
", seconds equilibrium/gradient = " <<
context->equilibriumSeconds
794 <<
"/" <<
context->gradientAssemblySeconds
795 << (diagnostics.empty() ?
"" :
", ") << diagnostics;
IncrementalObjectiveEvaluation finalEvaluation
SmartPetscObj< Vec > stateBefore
boost::shared_ptr< IncrementalOptimizationContext > context
Physics-independent incremental optimization API.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_OPERATION_UNSUCCESSFUL
@ MOFEM_DATA_INCONSISTENCY
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
PetscErrorCode DMMoFEMCreateSubDM(DM subdm, DM dm, const char problem_name[])
Must be called by user to set Sub DM MoFEM data structures.
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
PetscErrorCode DMMoFEMGetProblemPtr(DM dm, const MoFEM::Problem **problem_ptr)
Get pointer to problem data structure.
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
auto createDMMatrix(DM dm)
Get smart matrix from DM.
#define MOFEM_LOG(channel, severity)
Log.
boost::shared_ptr< IncrementalOptimizationContext > createIncrementalOptimizationContext(boost::shared_ptr< IncrementalOptimizationProblem > problem, SmartPetscObj< Vec > state)
MoFEMErrorCode solveIncrementalOptimizationTAO(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec state)
MoFEMErrorCode evaluateIncrementalObjectiveAndGradient(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, IncrementalObjectiveEvaluation &evaluation)
MoFEMErrorCode assembleIncrementalOptimizationInequalityJacobianFromEvaluation(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, const IncrementalObjectiveEvaluation &evaluation, Mat jacobian)
MoFEMErrorCode evaluateIncrementalOptimizationTAOObjectiveAndGradient(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, PetscReal &objective, Vec objective_gradient, IncrementalObjectiveEvaluation &evaluation)
MoFEMErrorCode evaluateIncrementalObjective(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, IncrementalObjectiveEvaluation &evaluation)
MoFEMErrorCode assembleIncrementalOptimizationInequalityJacobian(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, Mat jacobian)
MoFEMErrorCode evaluateIncrementalOptimizationInequalityConstraints(const boost::shared_ptr< IncrementalOptimizationContext > &context, Vec control, Vec constraints)
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
PetscErrorCode TaoSetObjective(Tao tao, Vec x, PetscReal *f, void *ctx)
Sets the objective function value for a TAO optimization context.
PetscErrorCode PetscOptionsGetReal(PetscOptions *, const char pre[], const char name[], PetscReal *dval, PetscBool *set)
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
auto getInterfacePtr(DM dm)
Get the Interface Ptr object.
PetscErrorCode TaoSetObjectiveAndGradient(Tao tao, Vec x, PetscReal *f, Vec g, void *ctx)
Sets the objective function value and gradient for a TAO optimization solver.
PetscErrorCode TaoSetGradient(Tao tao, Vec x, Vec f, void *ctx)
Sets the gradient vector for a TAO optimization context.
auto createDM(MPI_Comm comm, const std::string dm_type_name)
Creates smart DM object.
auto createTao(MPI_Comm comm)
EquilibriumSolveStatus status
MoFEMErrorCode stateSolveError
SmartPetscObj< Vec > objectiveGradient
double objectiveGradientNorm
EquilibriumSolveStatus stateStatus
double smoothGradientNorm
SmartPetscObj< Vec > smoothGradient
MoFEMErrorCode rollbackTrial()
SmartPetscObj< Vec > candidateControl
SmartPetscObj< Vec > committedControl
keeps basic data about problem
intrusive_ptr for managing petsc objects
Interface for TAO solvers.