xref: /petsc/src/tao/constrained/tutorials/ex1.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 /* Program usage: mpiexec -n 2 ./ex1 [-help] [all TAO options] */
2 
3 /* ----------------------------------------------------------------------
4 min f(x) = (x0 - 2)^2 + (x1 - 2)^2 - 2*(x0 + x1)
5 s.t.  x0^2 + x1 - 2 = 0
6       0  <= x0^2 - x1 <= 1
7       -1 <= x0, x1 <= 2
8 -->
9       g(x)  = 0
10       h(x) >= 0
11       -1 <= x0, x1 <= 2
12 where
13       g(x) = x0^2 + x1 - 2
14       h(x) = [x0^2 - x1
15               1 -(x0^2 - x1)]
16 ---------------------------------------------------------------------- */
17 
18 #include <petsctao.h>
19 
20 static char help[] = "Solves constrained optimization problem using pdipm.\n\
21 Input parameters include:\n\
22   -tao_type pdipm    : sets Tao solver\n\
23   -no_eq             : removes the equaility constraints from the problem\n\
24   -init_view         : view initial object setup\n\
25   -snes_fd           : snes with finite difference Jacobian (needed for pdipm)\n\
26   -snes_compare_explicit : compare user Jacobian with finite difference Jacobian \n\
27   -tao_cmonitor      : convergence monitor with constraint norm \n\
28   -tao_view_solution : view exact solution at each iteration\n\
29   Note: external package MUMPS is required to run pdipm in parallel. This is designed for a maximum of 2 processors, the code will error if size > 2.\n";
30 
31 /*
32    User-defined application context - contains data needed by the application
33 */
34 typedef struct {
35   PetscInt   n;  /* Global length of x */
36   PetscInt   ne; /* Global number of equality constraints */
37   PetscInt   ni; /* Global number of inequality constraints */
38   PetscBool  noeqflag, initview;
39   Vec        x, xl, xu;
40   Vec        ce, ci, bl, bu, Xseq;
41   Mat        Ae, Ai, H;
42   VecScatter scat;
43 } AppCtx;
44 
45 /* -------- User-defined Routines --------- */
46 PetscErrorCode InitializeProblem(AppCtx *);
47 PetscErrorCode DestroyProblem(AppCtx *);
48 PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
49 PetscErrorCode FormPDIPMHessian(Tao, Vec, Mat, Mat, void *);
50 PetscErrorCode FormInequalityConstraints(Tao, Vec, Vec, void *);
51 PetscErrorCode FormEqualityConstraints(Tao, Vec, Vec, void *);
52 PetscErrorCode FormInequalityJacobian(Tao, Vec, Mat, Mat, void *);
53 PetscErrorCode FormEqualityJacobian(Tao, Vec, Mat, Mat, void *);
54 
55 int main(int argc, char **argv)
56 {
57   Tao         tao;
58   KSP         ksp;
59   PC          pc;
60   AppCtx      user; /* application context */
61   Vec         x, G, CI, CE;
62   PetscMPIInt size;
63   TaoType     type;
64   PetscReal   f;
65   PetscBool   pdipm, mumps;
66 
67   PetscFunctionBeginUser;
68   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
69   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
70   PetscCheck(size <= 2, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "More than 2 processors detected. Example written to use max of 2 processors.");
71 
72   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "---- Constrained Problem -----\n"));
73   PetscCall(InitializeProblem(&user)); /* sets up problem, function below */
74 
75   PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
76   PetscCall(TaoSetType(tao, TAOALMM));
77   PetscCall(TaoSetSolution(tao, user.x));
78   PetscCall(TaoSetVariableBounds(tao, user.xl, user.xu));
79   PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user));
80   PetscCall(TaoSetTolerances(tao, 1.e-4, 0.0, 0.0));
81   PetscCall(TaoSetConstraintTolerances(tao, 1.e-4, 0.0));
82   PetscCall(TaoSetMaximumFunctionEvaluations(tao, 1e6));
83   PetscCall(TaoSetFromOptions(tao));
84 
85   if (!user.noeqflag) {
86     PetscCall(TaoSetEqualityConstraintsRoutine(tao, user.ce, FormEqualityConstraints, (void *)&user));
87     PetscCall(TaoSetJacobianEqualityRoutine(tao, user.Ae, user.Ae, FormEqualityJacobian, (void *)&user));
88   }
89   PetscCall(TaoSetInequalityConstraintsRoutine(tao, user.ci, FormInequalityConstraints, (void *)&user));
90   PetscCall(TaoSetJacobianInequalityRoutine(tao, user.Ai, user.Ai, FormInequalityJacobian, (void *)&user));
91 
92   PetscCall(TaoGetType(tao, &type));
93   PetscCall(PetscObjectTypeCompare((PetscObject)tao, TAOPDIPM, &pdipm));
94   if (pdipm) {
95     /*
96       PDIPM produces an indefinite KKT matrix with some zeros along the diagonal
97       Inverting this indefinite matrix requires PETSc to be configured with MUMPS
98     */
99     PetscCall(PetscHasExternalPackage("mumps", &mumps));
100     PetscCheck(mumps, PetscObjectComm((PetscObject)tao), PETSC_ERR_SUP, "TAOPDIPM requires PETSc to be configured with MUMPS (--download-mumps)");
101     PetscCall(TaoGetKSP(tao, &ksp));
102     PetscCall(KSPGetPC(ksp, &pc));
103     PetscCall(PCFactorSetMatSolverType(pc, MATSOLVERMUMPS));
104     PetscCall(KSPSetType(ksp, KSPPREONLY));
105     PetscCall(PCSetType(pc, PCCHOLESKY));
106     PetscCall(KSPSetFromOptions(ksp));
107     PetscCall(TaoSetHessian(tao, user.H, user.H, FormPDIPMHessian, (void *)&user));
108   }
109 
110   /* Print out an initial view of the problem */
111   if (user.initview) {
112     PetscCall(TaoSetUp(tao));
113     PetscCall(VecDuplicate(user.x, &G));
114     PetscCall(FormFunctionGradient(tao, user.x, &f, G, (void *)&user));
115     PetscCall(FormPDIPMHessian(tao, user.x, user.H, user.H, (void *)&user));
116     PetscCall(PetscViewerASCIIPushTab(PETSC_VIEWER_STDOUT_WORLD));
117     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\nInitial point X:\n"));
118     PetscCall(VecView(user.x, PETSC_VIEWER_STDOUT_WORLD));
119     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\nInitial objective f(x) = %g\n", (double)f));
120     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\nInitial gradient and Hessian:\n"));
121     PetscCall(VecView(G, PETSC_VIEWER_STDOUT_WORLD));
122     PetscCall(MatView(user.H, PETSC_VIEWER_STDOUT_WORLD));
123     PetscCall(VecDestroy(&G));
124     PetscCall(FormInequalityJacobian(tao, user.x, user.Ai, user.Ai, (void *)&user));
125     PetscCall(MatCreateVecs(user.Ai, NULL, &CI));
126     PetscCall(FormInequalityConstraints(tao, user.x, CI, (void *)&user));
127     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\nInitial inequality constraints and Jacobian:\n"));
128     PetscCall(VecView(CI, PETSC_VIEWER_STDOUT_WORLD));
129     PetscCall(MatView(user.Ai, PETSC_VIEWER_STDOUT_WORLD));
130     PetscCall(VecDestroy(&CI));
131     if (!user.noeqflag) {
132       PetscCall(FormEqualityJacobian(tao, user.x, user.Ae, user.Ae, (void *)&user));
133       PetscCall(MatCreateVecs(user.Ae, NULL, &CE));
134       PetscCall(FormEqualityConstraints(tao, user.x, CE, (void *)&user));
135       PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\nInitial equality constraints and Jacobian:\n"));
136       PetscCall(VecView(CE, PETSC_VIEWER_STDOUT_WORLD));
137       PetscCall(MatView(user.Ae, PETSC_VIEWER_STDOUT_WORLD));
138       PetscCall(VecDestroy(&CE));
139     }
140     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n"));
141     PetscCall(PetscViewerASCIIPopTab(PETSC_VIEWER_STDOUT_WORLD));
142   }
143 
144   PetscCall(TaoSolve(tao));
145   PetscCall(TaoGetSolution(tao, &x));
146   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Found solution:\n"));
147   PetscCall(VecView(x, PETSC_VIEWER_STDOUT_WORLD));
148 
149   /* Free objects */
150   PetscCall(DestroyProblem(&user));
151   PetscCall(TaoDestroy(&tao));
152   PetscCall(PetscFinalize());
153   return PETSC_SUCCESS;
154 }
155 
156 PetscErrorCode InitializeProblem(AppCtx *user)
157 {
158   PetscMPIInt size;
159   PetscMPIInt rank;
160   PetscInt    nloc, neloc, niloc;
161 
162   PetscFunctionBegin;
163   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
164   PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
165   user->noeqflag = PETSC_FALSE;
166   PetscCall(PetscOptionsGetBool(NULL, NULL, "-no_eq", &user->noeqflag, NULL));
167   user->initview = PETSC_FALSE;
168   PetscCall(PetscOptionsGetBool(NULL, NULL, "-init_view", &user->initview, NULL));
169 
170   /* Tell user the correct solution, not an error checking */
171   if (!user->noeqflag) {
172     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Expected solution: f(1, 1) = -2\n"));
173   } else {
174     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Expected solution (-no_eq): f(1.73205, 2) = -7.3923\n"));
175   }
176 
177   /* create vector x and set initial values */
178   user->n = 2; /* global length */
179   nloc    = (size == 1) ? user->n : 1;
180   PetscCall(VecCreate(PETSC_COMM_WORLD, &user->x));
181   PetscCall(VecSetSizes(user->x, nloc, user->n));
182   PetscCall(VecSetFromOptions(user->x));
183   PetscCall(VecSet(user->x, 0.0));
184 
185   /* create and set lower and upper bound vectors */
186   PetscCall(VecDuplicate(user->x, &user->xl));
187   PetscCall(VecDuplicate(user->x, &user->xu));
188   PetscCall(VecSet(user->xl, -1.0));
189   PetscCall(VecSet(user->xu, 2.0));
190 
191   /* create scater to zero */
192   PetscCall(VecScatterCreateToZero(user->x, &user->scat, &user->Xseq));
193 
194   user->ne = 1;
195   user->ni = 2;
196   neloc    = (rank == 0) ? user->ne : 0;
197   niloc    = (size == 1) ? user->ni : 1;
198 
199   if (!user->noeqflag) {
200     PetscCall(VecCreate(PETSC_COMM_WORLD, &user->ce)); /* a 1x1 vec for equality constraints */
201     PetscCall(VecSetSizes(user->ce, neloc, user->ne));
202     PetscCall(VecSetFromOptions(user->ce));
203     PetscCall(VecSetUp(user->ce));
204   }
205 
206   PetscCall(VecCreate(PETSC_COMM_WORLD, &user->ci)); /* a 2x1 vec for inequality constraints */
207   PetscCall(VecSetSizes(user->ci, niloc, user->ni));
208   PetscCall(VecSetFromOptions(user->ci));
209   PetscCall(VecSetUp(user->ci));
210 
211   /* nexn & nixn matricies for equally and inequalty constraints */
212   if (!user->noeqflag) {
213     PetscCall(MatCreate(PETSC_COMM_WORLD, &user->Ae));
214     PetscCall(MatSetSizes(user->Ae, neloc, nloc, user->ne, user->n));
215     PetscCall(MatSetFromOptions(user->Ae));
216     PetscCall(MatSetUp(user->Ae));
217   }
218 
219   PetscCall(MatCreate(PETSC_COMM_WORLD, &user->Ai));
220   PetscCall(MatSetSizes(user->Ai, niloc, nloc, user->ni, user->n));
221   PetscCall(MatSetFromOptions(user->Ai));
222   PetscCall(MatSetUp(user->Ai));
223 
224   PetscCall(MatCreate(PETSC_COMM_WORLD, &user->H));
225   PetscCall(MatSetSizes(user->H, nloc, nloc, user->n, user->n));
226   PetscCall(MatSetFromOptions(user->H));
227   PetscCall(MatSetUp(user->H));
228   PetscFunctionReturn(PETSC_SUCCESS);
229 }
230 
231 PetscErrorCode DestroyProblem(AppCtx *user)
232 {
233   PetscFunctionBegin;
234   if (!user->noeqflag) PetscCall(MatDestroy(&user->Ae));
235   PetscCall(MatDestroy(&user->Ai));
236   PetscCall(MatDestroy(&user->H));
237 
238   PetscCall(VecDestroy(&user->x));
239   if (!user->noeqflag) PetscCall(VecDestroy(&user->ce));
240   PetscCall(VecDestroy(&user->ci));
241   PetscCall(VecDestroy(&user->xl));
242   PetscCall(VecDestroy(&user->xu));
243   PetscCall(VecDestroy(&user->Xseq));
244   PetscCall(VecScatterDestroy(&user->scat));
245   PetscFunctionReturn(PETSC_SUCCESS);
246 }
247 
248 /* Evaluate
249    f(x) = (x0 - 2)^2 + (x1 - 2)^2 - 2*(x0 + x1)
250    G = grad f = [2*(x0 - 2) - 2, 2*(x1 - 2) - 2] = 2*X - 6
251 */
252 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ctx)
253 {
254   const PetscScalar *x;
255   MPI_Comm           comm;
256   PetscMPIInt        rank;
257   PetscReal          fin;
258   AppCtx            *user = (AppCtx *)ctx;
259   Vec                Xseq = user->Xseq;
260   VecScatter         scat = user->scat;
261 
262   PetscFunctionBegin;
263   /* f = (x0 - 2)^2 + (x1 - 2)^2 - 2*(x0 + x1) */
264   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
265   PetscCallMPI(MPI_Comm_rank(comm, &rank));
266 
267   PetscCall(VecScatterBegin(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
268   PetscCall(VecScatterEnd(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
269 
270   fin = 0.0;
271   if (rank == 0) {
272     PetscCall(VecGetArrayRead(Xseq, &x));
273     fin = (x[0] - 2.0) * (x[0] - 2.0) + (x[1] - 2.0) * (x[1] - 2.0) - 2.0 * (x[0] + x[1]);
274     PetscCall(VecRestoreArrayRead(Xseq, &x));
275   }
276   PetscCall(MPIU_Allreduce(&fin, f, 1, MPIU_REAL, MPIU_SUM, comm));
277 
278   /* G = 2*X - 6 */
279   PetscCall(VecSet(G, -6.0));
280   PetscCall(VecAXPY(G, 2.0, X));
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 /* Evaluate PDIPM Hessian, see Eqn(22) in http://doi.org/10.1049/gtd2.12708
285    H = Wxx = fxx        + Jacobian(grad g^T*DE)   - Jacobian(grad h^T*DI)]
286            = fxx        + Jacobin([2*x0; 1]de[0]) - Jacobian([2*x0, -2*x0; -1, 1][di[0] di[1]]^T)
287            = [2 0; 0 2] + [2*de[0]  0;      0  0] - [2*di[0]-2*di[1], 0; 0, 0]
288            = [ 2*(1+de[0]-di[0]+di[1]), 0;
289                           0,            2]
290 */
291 PetscErrorCode FormPDIPMHessian(Tao tao, Vec x, Mat H, Mat Hpre, void *ctx)
292 {
293   AppCtx            *user = (AppCtx *)ctx;
294   Vec                DE, DI;
295   const PetscScalar *de, *di;
296   PetscInt           zero = 0, one = 1;
297   PetscScalar        two = 2.0;
298   PetscScalar        val = 0.0;
299   Vec                Deseq, Diseq;
300   VecScatter         Descat, Discat;
301   PetscMPIInt        rank;
302   MPI_Comm           comm;
303 
304   PetscFunctionBegin;
305   PetscCall(TaoGetDualVariables(tao, &DE, &DI));
306 
307   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
308   PetscCallMPI(MPI_Comm_rank(comm, &rank));
309 
310   if (!user->noeqflag) {
311     PetscCall(VecScatterCreateToZero(DE, &Descat, &Deseq));
312     PetscCall(VecScatterBegin(Descat, DE, Deseq, INSERT_VALUES, SCATTER_FORWARD));
313     PetscCall(VecScatterEnd(Descat, DE, Deseq, INSERT_VALUES, SCATTER_FORWARD));
314   }
315   PetscCall(VecScatterCreateToZero(DI, &Discat, &Diseq));
316   PetscCall(VecScatterBegin(Discat, DI, Diseq, INSERT_VALUES, SCATTER_FORWARD));
317   PetscCall(VecScatterEnd(Discat, DI, Diseq, INSERT_VALUES, SCATTER_FORWARD));
318 
319   if (rank == 0) {
320     if (!user->noeqflag) { PetscCall(VecGetArrayRead(Deseq, &de)); /* places equality constraint dual into array */ }
321     PetscCall(VecGetArrayRead(Diseq, &di)); /* places inequality constraint dual into array */
322 
323     if (!user->noeqflag) {
324       val = 2.0 * (1 + de[0] - di[0] + di[1]);
325       PetscCall(VecRestoreArrayRead(Deseq, &de));
326       PetscCall(VecRestoreArrayRead(Diseq, &di));
327     } else {
328       val = 2.0 * (1 - di[0] + di[1]);
329     }
330     PetscCall(VecRestoreArrayRead(Diseq, &di));
331     PetscCall(MatSetValues(H, 1, &zero, 1, &zero, &val, INSERT_VALUES));
332     PetscCall(MatSetValues(H, 1, &one, 1, &one, &two, INSERT_VALUES));
333   }
334   PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY));
335   PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY));
336   if (!user->noeqflag) {
337     PetscCall(VecScatterDestroy(&Descat));
338     PetscCall(VecDestroy(&Deseq));
339   }
340   PetscCall(VecScatterDestroy(&Discat));
341   PetscCall(VecDestroy(&Diseq));
342   PetscFunctionReturn(PETSC_SUCCESS);
343 }
344 
345 /* Evaluate
346    h = [ x0^2 - x1;
347          1 -(x0^2 - x1)]
348 */
349 PetscErrorCode FormInequalityConstraints(Tao tao, Vec X, Vec CI, void *ctx)
350 {
351   const PetscScalar *x;
352   PetscScalar        ci;
353   MPI_Comm           comm;
354   PetscMPIInt        rank;
355   AppCtx            *user = (AppCtx *)ctx;
356   Vec                Xseq = user->Xseq;
357   VecScatter         scat = user->scat;
358 
359   PetscFunctionBegin;
360   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
361   PetscCallMPI(MPI_Comm_rank(comm, &rank));
362 
363   PetscCall(VecScatterBegin(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
364   PetscCall(VecScatterEnd(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
365 
366   if (rank == 0) {
367     PetscCall(VecGetArrayRead(Xseq, &x));
368     ci = x[0] * x[0] - x[1];
369     PetscCall(VecSetValue(CI, 0, ci, INSERT_VALUES));
370     ci = -x[0] * x[0] + x[1] + 1.0;
371     PetscCall(VecSetValue(CI, 1, ci, INSERT_VALUES));
372     PetscCall(VecRestoreArrayRead(Xseq, &x));
373   }
374   PetscCall(VecAssemblyBegin(CI));
375   PetscCall(VecAssemblyEnd(CI));
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 /* Evaluate
380    g = [ x0^2 + x1 - 2]
381 */
382 PetscErrorCode FormEqualityConstraints(Tao tao, Vec X, Vec CE, void *ctx)
383 {
384   const PetscScalar *x;
385   PetscScalar        ce;
386   MPI_Comm           comm;
387   PetscMPIInt        rank;
388   AppCtx            *user = (AppCtx *)ctx;
389   Vec                Xseq = user->Xseq;
390   VecScatter         scat = user->scat;
391 
392   PetscFunctionBegin;
393   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
394   PetscCallMPI(MPI_Comm_rank(comm, &rank));
395 
396   PetscCall(VecScatterBegin(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
397   PetscCall(VecScatterEnd(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
398 
399   if (rank == 0) {
400     PetscCall(VecGetArrayRead(Xseq, &x));
401     ce = x[0] * x[0] + x[1] - 2.0;
402     PetscCall(VecSetValue(CE, 0, ce, INSERT_VALUES));
403     PetscCall(VecRestoreArrayRead(Xseq, &x));
404   }
405   PetscCall(VecAssemblyBegin(CE));
406   PetscCall(VecAssemblyEnd(CE));
407   PetscFunctionReturn(PETSC_SUCCESS);
408 }
409 
410 /*
411   grad h = [  2*x0, -1;
412              -2*x0,  1]
413 */
414 PetscErrorCode FormInequalityJacobian(Tao tao, Vec X, Mat JI, Mat JIpre, void *ctx)
415 {
416   AppCtx            *user = (AppCtx *)ctx;
417   PetscInt           zero = 0, one = 1, cols[2];
418   PetscScalar        vals[2];
419   const PetscScalar *x;
420   Vec                Xseq = user->Xseq;
421   VecScatter         scat = user->scat;
422   MPI_Comm           comm;
423   PetscMPIInt        rank;
424 
425   PetscFunctionBegin;
426   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
427   PetscCallMPI(MPI_Comm_rank(comm, &rank));
428   PetscCall(VecScatterBegin(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
429   PetscCall(VecScatterEnd(scat, X, Xseq, INSERT_VALUES, SCATTER_FORWARD));
430 
431   PetscCall(VecGetArrayRead(Xseq, &x));
432   if (rank == 0) {
433     cols[0] = 0;
434     cols[1] = 1;
435     vals[0] = 2 * x[0];
436     vals[1] = -1.0;
437     PetscCall(MatSetValues(JI, 1, &zero, 2, cols, vals, INSERT_VALUES));
438     vals[0] = -2 * x[0];
439     vals[1] = 1.0;
440     PetscCall(MatSetValues(JI, 1, &one, 2, cols, vals, INSERT_VALUES));
441   }
442   PetscCall(VecRestoreArrayRead(Xseq, &x));
443   PetscCall(MatAssemblyBegin(JI, MAT_FINAL_ASSEMBLY));
444   PetscCall(MatAssemblyEnd(JI, MAT_FINAL_ASSEMBLY));
445   PetscFunctionReturn(PETSC_SUCCESS);
446 }
447 
448 /*
449   grad g = [2*x0, 1.0]
450 */
451 PetscErrorCode FormEqualityJacobian(Tao tao, Vec X, Mat JE, Mat JEpre, void *ctx)
452 {
453   PetscInt           zero = 0, cols[2];
454   PetscScalar        vals[2];
455   const PetscScalar *x;
456   PetscMPIInt        rank;
457   MPI_Comm           comm;
458 
459   PetscFunctionBegin;
460   PetscCall(PetscObjectGetComm((PetscObject)tao, &comm));
461   PetscCallMPI(MPI_Comm_rank(comm, &rank));
462 
463   if (rank == 0) {
464     PetscCall(VecGetArrayRead(X, &x));
465     cols[0] = 0;
466     cols[1] = 1;
467     vals[0] = 2 * x[0];
468     vals[1] = 1.0;
469     PetscCall(MatSetValues(JE, 1, &zero, 2, cols, vals, INSERT_VALUES));
470     PetscCall(VecRestoreArrayRead(X, &x));
471   }
472   PetscCall(MatAssemblyBegin(JE, MAT_FINAL_ASSEMBLY));
473   PetscCall(MatAssemblyEnd(JE, MAT_FINAL_ASSEMBLY));
474   PetscFunctionReturn(PETSC_SUCCESS);
475 }
476 
477 /*TEST
478 
479    build:
480       requires: !complex !defined(PETSC_USE_CXX)
481 
482    test:
483       args: -tao_converged_reason -tao_gatol 1.e-6 -tao_type pdipm -tao_pdipm_kkt_shift_pd
484       requires: mumps
485       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
486 
487    test:
488       suffix: pdipm_2
489       requires: mumps
490       nsize: 2
491       args: -tao_converged_reason -tao_gatol 1.e-6 -tao_type pdipm
492       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
493 
494    test:
495       suffix: 2
496       args: -tao_converged_reason
497       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
498 
499    test:
500       suffix: 3
501       args: -tao_converged_reason -no_eq
502       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
503 
504    test:
505       suffix: 4
506       args: -tao_converged_reason -tao_almm_type classic
507       requires: !single
508       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
509 
510    test:
511       suffix: 5
512       args: -tao_converged_reason -tao_almm_type classic -no_eq
513       requires: !single !defined(PETSCTEST_VALGRIND)
514       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
515 
516    test:
517       suffix: 6
518       args: -tao_converged_reason -tao_almm_subsolver_tao_type bqnktr
519       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
520 
521    test:
522       suffix: 7
523       args: -tao_converged_reason -tao_almm_subsolver_tao_type bncg
524       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
525 
526    test:
527       suffix: 8
528       nsize: 2
529       args: -tao_converged_reason
530       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
531 
532    test:
533       suffix: 9
534       nsize: 2
535       args: -tao_converged_reason -vec_type cuda -mat_type aijcusparse
536       requires: cuda
537       filter: sed  -e "s/CONVERGED_GATOL iterations *[0-9]\{1,\}/CONVERGED_GATOL/g"
538 
539 TEST*/
540