5#include <petsc/private/taoimpl.h>
13constexpr char diagonalNNLSContextKey[] =
"_MoFEM_TaoDiagonalNNLS";
15struct TaoDiagonalNNLS {
23 PetscErrorCode (*destroyShell)(Tao) =
nullptr;
32 *f = 0.5 * PetscRealPart(squared);
40 MoFEMErrorCode monitor(Vec x, PetscInt iteration, PetscReal step) {
43 CHKERR TaoComputeObjectiveAndGradient(parentTao, x, &objective, gradient);
44 CHKERR VecCopy(gradient, work);
46 PetscScalar *projected;
50 CHKERR VecGetArray(work, &projected);
51 for (PetscInt
i = 0;
i !=
size; ++
i)
52 if (PetscRealPart(
values[
i]) == 0)
53 projected[
i] = std::min(PetscRealPart(projected[
i]), PetscReal(0));
54 CHKERR VecRestoreArray(work, &projected);
57 CHKERR VecNorm(work, NORM_2, &norm);
58 if (!std::isfinite(objective) || !std::isfinite(norm))
60 "Diagonal NNLS objective or projected gradient is not finite");
61 CHKERR TaoSetIterationNumber(parentTao, iteration);
62 CHKERR TaoLogConvergenceHistory(parentTao, objective, norm, 0, 0);
63 CHKERR TaoMonitor(parentTao, iteration, objective, norm, 0, step);
71 if (!jacobian || !target)
72 SETERRQ(
comm, PETSC_ERR_ARG_WRONGSTATE,
73 "Call TaoDiagonalNNLSSetProblem before TaoSolve");
74 if (tao->eq_constrained || tao->ineq_constrained ||
75 tao->ops->computeconstraints)
76 SETERRQ(
comm, PETSC_ERR_SUP,
77 "Diagonal NNLS only supports nonnegativity constraints");
79 CHKERR TaoGetSolution(tao, &x);
80 PetscInt
rows, columns, local_rows, local_columns;
81 PetscInt
size, local_size, target_size, target_local_size;
83 CHKERR MatGetLocalSize(jacobian, &local_rows, &local_columns);
85 CHKERR VecGetLocalSize(x, &local_size);
86 CHKERR VecGetSize(target, &target_size);
87 CHKERR VecGetLocalSize(target, &target_local_size);
88 PetscInt compatible =
rows ==
size && columns == target_size &&
89 local_rows == local_size &&
90 local_columns == target_local_size;
91 CHKERR MPIU_Allreduce(MPI_IN_PLACE, &compatible, 1, MPIU_INT, MPI_MIN,
94 SETERRQ(
comm, PETSC_ERR_ARG_SIZ,
95 "Diagonal NNLS solution/target must use the Jacobian row/column "
97 if (tao->bounded || tao->ops->computebounds) {
98 CHKERR TaoComputeVariableBounds(tao);
100 for (
const auto bound : {tao->XL, tao->XU}) {
103 PetscInt bound_size, bound_local_size;
104 CHKERR VecGetSize(bound, &bound_size);
105 CHKERR VecGetLocalSize(bound, &bound_local_size);
106 if (bound_size !=
size || bound_local_size != local_size)
109 CHKERR MPIU_Allreduce(MPI_IN_PLACE, &compatible, 1, MPIU_INT, MPI_MIN,
112 SETERRQ(
comm, PETSC_ERR_ARG_SIZ,
113 "Diagonal NNLS bounds must use the solution vector layout");
114 const PetscScalar *lower =
nullptr, *upper =
nullptr;
116 CHKERR VecGetArrayRead(tao->XL, &lower);
118 CHKERR VecGetArrayRead(tao->XU, &upper);
119 for (PetscInt
i = 0;
i != local_size; ++
i)
120 if ((lower && PetscRealPart(lower[
i]) != 0) ||
121 (upper && !(PetscRealPart(upper[
i]) >= PETSC_INFINITY)))
124 CHKERR VecRestoreArrayRead(tao->XU, &upper);
126 CHKERR VecRestoreArrayRead(tao->XL, &lower);
127 CHKERR MPIU_Allreduce(MPI_IN_PLACE, &compatible, 1, MPIU_INT, MPI_MIN,
130 SETERRQ(
comm, PETSC_ERR_SUP,
131 "Diagonal NNLS requires lower bounds zero and upper bounds "
132 "positive infinity");
136 CHKERR MatMatTransposeMult(jacobian, jacobian, MAT_INITIAL_MATRIX,
137 PETSC_DEFAULT, &matrix);
138 SmartPetscObj<Mat> gram(matrix);
139 CHKERR MatGetDiagonal(gram, diagonal);
140 const PetscScalar *entries;
141 CHKERR VecGetArrayRead(diagonal, &entries);
143 for (PetscInt
i = 0;
i != local_size; ++
i)
144 if (!std::isfinite(PetscRealPart(entries[
i])) ||
145 !(PetscRealPart(entries[
i]) > 0))
147 CHKERR VecRestoreArrayRead(diagonal, &entries);
148 CHKERR MPIU_Allreduce(MPI_IN_PLACE, &compatible, 1, MPIU_INT, MPI_MIN,
151 SETERRQ(
comm, PETSC_ERR_ARG_OUTOFRANGE,
152 "Diagonal NNLS requires nonzero finite Jacobian rows");
155 CHKERR VecCopy(diagonal, work);
157 CHKERR VecReciprocal(work);
158 CHKERR MatDiagonalScale(gram, work, work);
159 CHKERR MatShift(gram, -1);
161 CHKERR MatNorm(gram, NORM_INFINITY, &coupling);
162 if (!std::isfinite(coupling) || coupling > 100 * PETSC_MACHINE_EPSILON)
163 SETERRQ(
comm, PETSC_ERR_ARG_INCOMP,
164 "Diagonal NNLS requires mutually orthogonal Jacobian rows; "
165 "normalized Gram coupling is %g. Use blmvm for coupled rows",
166 static_cast<double>(coupling));
168 CHKERR TaoSetConvergedReason(tao, TAO_CONTINUE_ITERATING);
169 CHKERR VecZeroEntries(work);
170 CHKERR VecPointwiseMax(x, x, work);
172 if (tao->reason != TAO_CONTINUE_ITERATING)
175 CHKERR VecCopy(x, previous);
176 CHKERR MatMult(jacobian, target, x);
177 CHKERR VecPointwiseDivide(x, x, diagonal);
178 CHKERR VecZeroEntries(work);
179 CHKERR VecPointwiseMax(x, x, work);
180 CHKERR VecWAXPY(work, -1, previous, x);
182 CHKERR VecNorm(work, NORM_2, &step);
183 CHKERR monitor(x, 1, step);
184 if (tao->reason == TAO_CONTINUE_ITERATING ||
185 tao->reason == TAO_CONVERGED_STEPTOL) {
188 CHKERR TaoSetConvergedReason(tao, TAO_DIVERGED_USER);
189 CHKERR PetscInfo(tao,
"Diagonal NNLS direct update did not satisfy the "
190 "requested optimality tolerance\n");
196MoFEMErrorCode getDiagonalNNLSContext(Tao tao, TaoDiagonalNNLS *&ctx) {
201 SETERRQ(PetscObjectComm(
getPetscObject(tao)), PETSC_ERR_ARG_WRONG,
202 "TAO type must be diagonal");
203 PetscContainer container =
nullptr;
205 reinterpret_cast<PetscObject *
>(&container));
207 SETERRQ(PetscObjectComm(
getPetscObject(tao)), PETSC_ERR_ARG_WRONGSTATE,
208 "Diagonal NNLS context is not attached");
210 CHKERR PetscContainerGetPointer(container, &ptr);
211 ctx =
static_cast<TaoDiagonalNNLS *
>(ptr);
218 CHKERR TaoSetType(tao, TAOSHELL);
220 auto ctx = std::make_unique<TaoDiagonalNNLS>();
221 ctx->parentTao = tao;
222 ctx->destroyShell = tao->ops->destroy;
223 PetscContainer container;
224 CHKERR PetscContainerCreate(
comm, &container);
225 CHKERR PetscContainerSetPointer(container, ctx.get());
226 CHKERR PetscContainerSetUserDestroy(container, [](
void *ptr) {
228 auto *ctx =
static_cast<TaoDiagonalNNLS *
>(ptr);
229 const auto tao = ctx->parentTao;
230 if (tao->user_objP == ctx) {
231 tao->ops->computeobjective =
nullptr;
232 tao->user_objP =
nullptr;
234 if (tao->user_gradP == ctx) {
235 tao->ops->computegradient =
nullptr;
236 tao->user_gradP =
nullptr;
238 if (tao->user_objgradP == ctx) {
239 tao->ops->computeobjectiveandgradient =
nullptr;
240 tao->user_objgradP =
nullptr;
246 const auto error = PetscObjectCompose(
248 CHKERR PetscContainerDestroy(&container);
250 tao->ops->destroy = [](Tao tao) {
252 TaoDiagonalNNLS *ctx;
253 CHKERR getDiagonalNNLSContext(tao, ctx);
254 const auto destroy_shell = ctx->destroyShell;
257 CHKERR destroy_shell(tao);
260 tao->ops->view = [](Tao tao, PetscViewer viewer) {
266 CHKERR PetscViewerASCIIPrintf(
267 viewer,
" Direct nonnegative least squares with orthogonal rows\n"
268 " One diagonal update; no line search\n");
271 CHKERR TaoShellSetSolve(tao, [](Tao tao) {
273 TaoDiagonalNNLS *ctx;
274 CHKERR getDiagonalNNLSContext(tao, ctx);
291 TaoDiagonalNNLS *ctx;
292 CHKERR getDiagonalNNLSContext(tao, ctx);
301 CHKERR ::TaoSetObjective(
303 [](Tao, Vec x, PetscReal *f,
void *ptr) {
304 return static_cast<TaoDiagonalNNLS *
>(ptr)->evaluate(x, f,
nullptr);
307 CHKERR ::TaoSetGradient(
309 [](Tao, Vec x, Vec
g,
void *ptr) {
310 return static_cast<TaoDiagonalNNLS *>(ptr)->evaluate(x, nullptr, g);
313 CHKERR ::TaoSetObjectiveAndGradient(
315 [](Tao, Vec x, PetscReal *f, Vec
g,
void *ptr) {
316 return static_cast<TaoDiagonalNNLS *>(ptr)->evaluate(x, f, g);
SmartPetscObj< Vec > diagonal
SmartPetscObj< Vec > target
SmartPetscObj< Vec > work
SmartPetscObj< Vec > residual
SmartPetscObj< Vec > gradient
SmartPetscObj< Mat > jacobian
Direct nonnegative least squares for orthogonal constraint rows.
SmartPetscObj< Vec > values
#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 ...
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
FTensor::Index< 'i', SPACE_DIM > i
const FTensor::Tensor2< T, Dim, Dim > Vec
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
MoFEMErrorCode TaoDiagonalNNLSSetProblem(Tao tao, Mat jacobian, Vec target)
MoFEMErrorCode TaoRegisterDiagonalNNLS()
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
static PetscErrorCode solve(Mat mat, Vec x, Vec y)
constexpr char TAODIAGONALNNLS[]
PetscObject getPetscObject(T obj)
intrusive_ptr for managing petsc objects