xref: /petsc/src/tao/constrained/impls/admm/admm.c (revision 4e8208cbcbc709572b8abe32f33c78b69c819375)
1 #include <../src/tao/constrained/impls/admm/admm.h> /*I "petsctao.h" I*/
2 #include <petsctao.h>
3 #include <petsc/private/petscimpl.h>
4 
5 /* Updates terminating criteria
6  *
7  * 1  ||r_k|| = ||Ax+Bz-c|| =< catol_admm* max{||Ax||,||Bz||,||c||}
8  *
9  * 2. Updates dual residual, d_k
10  *
11  * 3. ||d_k|| = ||mu*A^T*B(z_k-z_{k-1})|| =< gatol_admm * ||A^Ty||   */
12 
13 static PetscBool  cited      = PETSC_FALSE;
14 static const char citation[] = "@misc{xu2017adaptive,\n"
15                                "   title={Adaptive Relaxed ADMM: Convergence Theory and Practical Implementation},\n"
16                                "   author={Zheng Xu and Mario A. T. Figueiredo and Xiaoming Yuan and Christoph Studer and Tom Goldstein},\n"
17                                "   year={2017},\n"
18                                "   eprint={1704.02712},\n"
19                                "   archivePrefix={arXiv},\n"
20                                "   primaryClass={cs.CV}\n"
21                                "}  \n";
22 
23 const char *const TaoADMMRegularizerTypes[] = {"REGULARIZER_USER", "REGULARIZER_SOFT_THRESH", "TaoADMMRegularizerType", "TAO_ADMM_", NULL};
24 const char *const TaoADMMUpdateTypes[]      = {"UPDATE_BASIC", "UPDATE_ADAPTIVE", "UPDATE_ADAPTIVE_RELAXED", "TaoADMMUpdateType", "TAO_ADMM_", NULL};
25 const char *const TaoALMMTypes[]            = {"CLASSIC", "PHR", "TaoALMMType", "TAO_ALMM_", NULL};
26 
TaoADMMToleranceUpdate(Tao tao)27 static PetscErrorCode TaoADMMToleranceUpdate(Tao tao)
28 {
29   TAO_ADMM *am = (TAO_ADMM *)tao->data;
30   PetscReal Axnorm, Bznorm, ATynorm, temp;
31   Vec       tempJR, tempL;
32   Tao       mis;
33 
34   PetscFunctionBegin;
35   mis    = am->subsolverX;
36   tempJR = am->workJacobianRight;
37   tempL  = am->workLeft;
38   /* ATy */
39   PetscCall(TaoComputeJacobianEquality(mis, am->y, mis->jacobian_equality, mis->jacobian_equality_pre));
40   PetscCall(MatMultTranspose(mis->jacobian_equality, am->y, tempJR));
41   PetscCall(VecNorm(tempJR, NORM_2, &ATynorm));
42   /* dualres = mu * ||AT(Bz-Bzold)||_2 */
43   PetscCall(VecWAXPY(tempJR, -1., am->Bzold, am->Bz));
44   PetscCall(MatMultTranspose(mis->jacobian_equality, tempJR, tempL));
45   PetscCall(VecNorm(tempL, NORM_2, &am->dualres));
46   am->dualres *= am->mu;
47 
48   /* ||Ax||_2, ||Bz||_2 */
49   PetscCall(VecNorm(am->Ax, NORM_2, &Axnorm));
50   PetscCall(VecNorm(am->Bz, NORM_2, &Bznorm));
51 
52   /* Set catol to be catol_admm *  max{||Ax||,||Bz||,||c||} *
53    * Set gatol to be gatol_admm *  ||A^Ty|| *
54    * while cnorm is ||r_k||_2, and gnorm is ||d_k||_2 */
55   temp = am->catol_admm * PetscMax(Axnorm, (!am->const_norm) ? Bznorm : PetscMax(Bznorm, am->const_norm));
56   PetscCall(TaoSetConstraintTolerances(tao, temp, PETSC_CURRENT));
57   PetscCall(TaoSetTolerances(tao, am->gatol_admm * ATynorm, PETSC_CURRENT, PETSC_CURRENT));
58   PetscFunctionReturn(PETSC_SUCCESS);
59 }
60 
61 /* Penalty Update for Adaptive ADMM. */
AdaptiveADMMPenaltyUpdate(Tao tao)62 static PetscErrorCode AdaptiveADMMPenaltyUpdate(Tao tao)
63 {
64   TAO_ADMM *am = (TAO_ADMM *)tao->data;
65   PetscReal ydiff_norm, yhatdiff_norm, Axdiff_norm, Bzdiff_norm, Axyhat, Bzy, a_sd, a_mg, a_k, b_sd, b_mg, b_k;
66   PetscBool hflag, gflag;
67   Vec       tempJR, tempJR2;
68 
69   PetscFunctionBegin;
70   tempJR  = am->workJacobianRight;
71   tempJR2 = am->workJacobianRight2;
72   hflag   = PETSC_FALSE;
73   gflag   = PETSC_FALSE;
74   a_k     = -1;
75   b_k     = -1;
76 
77   PetscCall(VecWAXPY(tempJR, -1., am->Axold, am->Ax));
78   PetscCall(VecWAXPY(tempJR2, -1., am->yhatold, am->yhat));
79   PetscCall(VecNorm(tempJR, NORM_2, &Axdiff_norm));
80   PetscCall(VecNorm(tempJR2, NORM_2, &yhatdiff_norm));
81   PetscCall(VecDot(tempJR, tempJR2, &Axyhat));
82 
83   PetscCall(VecWAXPY(tempJR, -1., am->Bz0, am->Bz));
84   PetscCall(VecWAXPY(tempJR2, -1., am->y, am->y0));
85   PetscCall(VecNorm(tempJR, NORM_2, &Bzdiff_norm));
86   PetscCall(VecNorm(tempJR2, NORM_2, &ydiff_norm));
87   PetscCall(VecDot(tempJR, tempJR2, &Bzy));
88 
89   if (Axyhat > am->orthval * Axdiff_norm * yhatdiff_norm + am->mueps) {
90     hflag = PETSC_TRUE;
91     a_sd  = PetscSqr(yhatdiff_norm) / Axyhat; /* alphaSD */
92     a_mg  = Axyhat / PetscSqr(Axdiff_norm);   /* alphaMG */
93     a_k   = (a_mg / a_sd) > 0.5 ? a_mg : a_sd - 0.5 * a_mg;
94   }
95   if (Bzy > am->orthval * Bzdiff_norm * ydiff_norm + am->mueps) {
96     gflag = PETSC_TRUE;
97     b_sd  = PetscSqr(ydiff_norm) / Bzy;  /* betaSD */
98     b_mg  = Bzy / PetscSqr(Bzdiff_norm); /* betaMG */
99     b_k   = (b_mg / b_sd) > 0.5 ? b_mg : b_sd - 0.5 * b_mg;
100   }
101   am->muold = am->mu;
102   if (gflag && hflag) {
103     am->mu = PetscSqrtReal(a_k * b_k);
104   } else if (hflag) {
105     am->mu = a_k;
106   } else if (gflag) {
107     am->mu = b_k;
108   }
109   if (am->mu > am->muold) am->mu = am->muold;
110   if (am->mu < am->mumin) am->mu = am->mumin;
111   PetscFunctionReturn(PETSC_SUCCESS);
112 }
113 
TaoADMMSetRegularizerType_ADMM(Tao tao,TaoADMMRegularizerType type)114 static PetscErrorCode TaoADMMSetRegularizerType_ADMM(Tao tao, TaoADMMRegularizerType type)
115 {
116   TAO_ADMM *am = (TAO_ADMM *)tao->data;
117 
118   PetscFunctionBegin;
119   am->regswitch = type;
120   PetscFunctionReturn(PETSC_SUCCESS);
121 }
122 
TaoADMMGetRegularizerType_ADMM(Tao tao,TaoADMMRegularizerType * type)123 static PetscErrorCode TaoADMMGetRegularizerType_ADMM(Tao tao, TaoADMMRegularizerType *type)
124 {
125   TAO_ADMM *am = (TAO_ADMM *)tao->data;
126 
127   PetscFunctionBegin;
128   *type = am->regswitch;
129   PetscFunctionReturn(PETSC_SUCCESS);
130 }
131 
TaoADMMSetUpdateType_ADMM(Tao tao,TaoADMMUpdateType type)132 static PetscErrorCode TaoADMMSetUpdateType_ADMM(Tao tao, TaoADMMUpdateType type)
133 {
134   TAO_ADMM *am = (TAO_ADMM *)tao->data;
135 
136   PetscFunctionBegin;
137   am->update = type;
138   PetscFunctionReturn(PETSC_SUCCESS);
139 }
140 
TaoADMMGetUpdateType_ADMM(Tao tao,TaoADMMUpdateType * type)141 static PetscErrorCode TaoADMMGetUpdateType_ADMM(Tao tao, TaoADMMUpdateType *type)
142 {
143   TAO_ADMM *am = (TAO_ADMM *)tao->data;
144 
145   PetscFunctionBegin;
146   *type = am->update;
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /* This routine updates Jacobians with new x,z vectors,
151  * and then updates Ax and Bz vectors, then computes updated residual vector*/
ADMMUpdateConstraintResidualVector(Tao tao,Vec x,Vec z,Vec Ax,Vec Bz,Vec residual)152 static PetscErrorCode ADMMUpdateConstraintResidualVector(Tao tao, Vec x, Vec z, Vec Ax, Vec Bz, Vec residual)
153 {
154   TAO_ADMM *am = (TAO_ADMM *)tao->data;
155   Tao       mis, reg;
156 
157   PetscFunctionBegin;
158   mis = am->subsolverX;
159   reg = am->subsolverZ;
160   PetscCall(TaoComputeJacobianEquality(mis, x, mis->jacobian_equality, mis->jacobian_equality_pre));
161   PetscCall(MatMult(mis->jacobian_equality, x, Ax));
162   PetscCall(TaoComputeJacobianEquality(reg, z, reg->jacobian_equality, reg->jacobian_equality_pre));
163   PetscCall(MatMult(reg->jacobian_equality, z, Bz));
164 
165   PetscCall(VecWAXPY(residual, 1., Bz, Ax));
166   if (am->constraint != NULL) PetscCall(VecAXPY(residual, -1., am->constraint));
167   PetscFunctionReturn(PETSC_SUCCESS);
168 }
169 
170 /* Updates Augmented Lagrangians to given routines *
171  * For subsolverX, routine needs to be ComputeObjectiveAndGraidnet
172  * Separate Objective and Gradient routines are not supported.  */
SubObjGradUpdate(Tao tao,Vec x,PetscReal * f,Vec g,void * ptr)173 static PetscErrorCode SubObjGradUpdate(Tao tao, Vec x, PetscReal *f, Vec g, void *ptr)
174 {
175   Tao       parent = (Tao)ptr;
176   TAO_ADMM *am     = (TAO_ADMM *)parent->data;
177   PetscReal temp, temp2;
178   Vec       tempJR;
179 
180   PetscFunctionBegin;
181   tempJR = am->workJacobianRight;
182   PetscCall(ADMMUpdateConstraintResidualVector(parent, x, am->subsolverZ->solution, am->Ax, am->Bz, am->residual));
183   PetscCall((*am->ops->misfitobjgrad)(am->subsolverX, x, f, g, am->misfitobjgradP));
184 
185   am->last_misfit_val = *f;
186   /* Objective  Add + yT(Ax+Bz-c) + mu/2*||Ax+Bz-c||_2^2 */
187   PetscCall(VecTDot(am->residual, am->y, &temp));
188   PetscCall(VecTDot(am->residual, am->residual, &temp2));
189   *f += temp + (am->mu / 2) * temp2;
190 
191   /* Gradient. Add + mu*AT(Ax+Bz-c) + yTA*/
192   PetscCall(MatMultTranspose(tao->jacobian_equality, am->residual, tempJR));
193   PetscCall(VecAXPY(g, am->mu, tempJR));
194   PetscCall(MatMultTranspose(tao->jacobian_equality, am->y, tempJR));
195   PetscCall(VecAXPY(g, 1., tempJR));
196   PetscFunctionReturn(PETSC_SUCCESS);
197 }
198 
199 /* Updates Augmented Lagrangians to given routines
200  * For subsolverZ, routine needs to be ComputeObjectiveAndGraidnet
201  * Separate Objective and Gradient routines are not supported.  */
RegObjGradUpdate(Tao tao,Vec z,PetscReal * f,Vec g,void * ptr)202 static PetscErrorCode RegObjGradUpdate(Tao tao, Vec z, PetscReal *f, Vec g, void *ptr)
203 {
204   Tao       parent = (Tao)ptr;
205   TAO_ADMM *am     = (TAO_ADMM *)parent->data;
206   PetscReal temp, temp2;
207   Vec       tempJR;
208 
209   PetscFunctionBegin;
210   tempJR = am->workJacobianRight;
211   PetscCall(ADMMUpdateConstraintResidualVector(parent, am->subsolverX->solution, z, am->Ax, am->Bz, am->residual));
212   PetscCall((*am->ops->regobjgrad)(am->subsolverZ, z, f, g, am->regobjgradP));
213   am->last_reg_val = *f;
214   /* Objective  Add  + yT(Ax+Bz-c) + mu/2*||Ax+Bz-c||_2^2 */
215   PetscCall(VecTDot(am->residual, am->y, &temp));
216   PetscCall(VecTDot(am->residual, am->residual, &temp2));
217   *f += temp + (am->mu / 2) * temp2;
218 
219   /* Gradient. Add + mu*BT(Ax+Bz-c) + yTB*/
220   PetscCall(MatMultTranspose(am->subsolverZ->jacobian_equality, am->residual, tempJR));
221   PetscCall(VecAXPY(g, am->mu, tempJR));
222   PetscCall(MatMultTranspose(am->subsolverZ->jacobian_equality, am->y, tempJR));
223   PetscCall(VecAXPY(g, 1., tempJR));
224   PetscFunctionReturn(PETSC_SUCCESS);
225 }
226 
227 /* Computes epsilon padded L1 norm lambda*sum(sqrt(x^2+eps^2)-eps */
ADMML1EpsilonNorm(Tao tao,Vec x,PetscReal eps,PetscReal * norm)228 static PetscErrorCode ADMML1EpsilonNorm(Tao tao, Vec x, PetscReal eps, PetscReal *norm)
229 {
230   TAO_ADMM *am = (TAO_ADMM *)tao->data;
231   PetscInt  N;
232 
233   PetscFunctionBegin;
234   PetscCall(VecGetSize(am->workLeft, &N));
235   PetscCall(VecPointwiseMult(am->workLeft, x, x));
236   PetscCall(VecShift(am->workLeft, am->l1epsilon * am->l1epsilon));
237   PetscCall(VecSqrtAbs(am->workLeft));
238   PetscCall(VecSum(am->workLeft, norm));
239   *norm += N * am->l1epsilon;
240   *norm *= am->lambda;
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
ADMMInternalHessianUpdate(Mat H,Mat Constraint,PetscBool Identity,void * ptr)244 static PetscErrorCode ADMMInternalHessianUpdate(Mat H, Mat Constraint, PetscBool Identity, void *ptr)
245 {
246   TAO_ADMM *am = (TAO_ADMM *)ptr;
247 
248   PetscFunctionBegin;
249   switch (am->update) {
250   case TAO_ADMM_UPDATE_BASIC:
251     break;
252   case TAO_ADMM_UPDATE_ADAPTIVE:
253   case TAO_ADMM_UPDATE_ADAPTIVE_RELAXED:
254     if (H && (am->muold != am->mu)) {
255       if (!Identity) {
256         PetscCall(MatAXPY(H, am->mu - am->muold, Constraint, DIFFERENT_NONZERO_PATTERN));
257       } else {
258         PetscCall(MatShift(H, am->mu - am->muold));
259       }
260     }
261     break;
262   }
263   PetscFunctionReturn(PETSC_SUCCESS);
264 }
265 
266 /* Updates Hessian - adds second derivative of augmented Lagrangian
267  * H \gets H + \rho*ATA
268   Here, \rho does not change in TAO_ADMM_UPDATE_BASIC - thus no-op
269   For ADAPTAIVE,ADAPTIVE_RELAXED,
270   H \gets H + (\rho-\rhoold)*ATA
271   Here, we assume that A is linear constraint i.e., does not change.
272   Thus, for both ADAPTIVE, and RELAXED, ATA matrix is pre-set (except for A=I (null case)) see TaoSetUp_ADMM */
SubHessianUpdate(Tao tao,Vec x,Mat H,Mat Hpre,void * ptr)273 static PetscErrorCode SubHessianUpdate(Tao tao, Vec x, Mat H, Mat Hpre, void *ptr)
274 {
275   Tao       parent = (Tao)ptr;
276   TAO_ADMM *am     = (TAO_ADMM *)parent->data;
277 
278   PetscFunctionBegin;
279   if (am->Hxchange) {
280     /* Case where Hessian gets updated with respect to x vector input. */
281     PetscCall((*am->ops->misfithess)(am->subsolverX, x, H, Hpre, am->misfithessP));
282     PetscCall(ADMMInternalHessianUpdate(am->subsolverX->hessian, am->ATA, am->xJI, am));
283   } else if (am->Hxbool) {
284     /* Hessian doesn't get updated. H(x) = c */
285     /* Update Lagrangian only once per TAO call */
286     PetscCall(ADMMInternalHessianUpdate(am->subsolverX->hessian, am->ATA, am->xJI, am));
287     am->Hxbool = PETSC_FALSE;
288   }
289   PetscFunctionReturn(PETSC_SUCCESS);
290 }
291 
292 /* Same as SubHessianUpdate, except for B matrix instead of A matrix */
RegHessianUpdate(Tao tao,Vec z,Mat H,Mat Hpre,void * ptr)293 static PetscErrorCode RegHessianUpdate(Tao tao, Vec z, Mat H, Mat Hpre, void *ptr)
294 {
295   Tao       parent = (Tao)ptr;
296   TAO_ADMM *am     = (TAO_ADMM *)parent->data;
297 
298   PetscFunctionBegin;
299   if (am->Hzchange) {
300     /* Case where Hessian gets updated with respect to x vector input. */
301     PetscCall((*am->ops->reghess)(am->subsolverZ, z, H, Hpre, am->reghessP));
302     PetscCall(ADMMInternalHessianUpdate(am->subsolverZ->hessian, am->BTB, am->zJI, am));
303   } else if (am->Hzbool) {
304     /* Hessian doesn't get updated. H(x) = c */
305     /* Update Lagrangian only once per TAO call */
306     PetscCall(ADMMInternalHessianUpdate(am->subsolverZ->hessian, am->BTB, am->zJI, am));
307     am->Hzbool = PETSC_FALSE;
308   }
309   PetscFunctionReturn(PETSC_SUCCESS);
310 }
311 
312 /* Shell Matrix routine for A matrix.
313  * This gets used when user puts NULL for
314  * TaoSetJacobianEqualityRoutine(tao, NULL,NULL, ...)
315  * Essentially sets A=I*/
JacobianIdentity(Mat mat,Vec in,Vec out)316 static PetscErrorCode JacobianIdentity(Mat mat, Vec in, Vec out)
317 {
318   PetscFunctionBegin;
319   PetscCall(VecCopy(in, out));
320   PetscFunctionReturn(PETSC_SUCCESS);
321 }
322 
323 /* Shell Matrix routine for B matrix.
324  * This gets used when user puts NULL for
325  * TaoADMMSetRegularizerConstraintJacobian(tao, NULL,NULL, ...)
326  * Sets B=-I */
JacobianIdentityB(Mat mat,Vec in,Vec out)327 static PetscErrorCode JacobianIdentityB(Mat mat, Vec in, Vec out)
328 {
329   PetscFunctionBegin;
330   PetscCall(VecCopy(in, out));
331   PetscCall(VecScale(out, -1.));
332   PetscFunctionReturn(PETSC_SUCCESS);
333 }
334 
335 /* Solve f(x) + g(z) s.t. Ax + Bz = c */
TaoSolve_ADMM(Tao tao)336 static PetscErrorCode TaoSolve_ADMM(Tao tao)
337 {
338   TAO_ADMM *am = (TAO_ADMM *)tao->data;
339   PetscInt  N;
340   PetscReal reg_func;
341   PetscBool is_reg_shell;
342   Vec       tempL;
343 
344   PetscFunctionBegin;
345   if (am->regswitch != TAO_ADMM_REGULARIZER_SOFT_THRESH) {
346     PetscCheck(am->subsolverX->ops->computejacobianequality, PetscObjectComm((PetscObject)tao), PETSC_ERR_ARG_WRONGSTATE, "Must call TaoADMMSetMisfitConstraintJacobian() first");
347     PetscCheck(am->subsolverZ->ops->computejacobianequality, PetscObjectComm((PetscObject)tao), PETSC_ERR_ARG_WRONGSTATE, "Must call TaoADMMSetRegularizerConstraintJacobian() first");
348     if (am->constraint != NULL) PetscCall(VecNorm(am->constraint, NORM_2, &am->const_norm));
349   }
350   tempL = am->workLeft;
351   PetscCall(VecGetSize(tempL, &N));
352 
353   if (am->Hx && am->ops->misfithess) PetscCall(TaoSetHessian(am->subsolverX, am->Hx, am->Hx, SubHessianUpdate, tao));
354 
355   if (!am->zJI) {
356     /* Currently, B is assumed to be a linear system, i.e., not getting updated*/
357     PetscCall(MatTransposeMatMult(am->JB, am->JB, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &am->BTB));
358   }
359   if (!am->xJI) {
360     /* Currently, A is assumed to be a linear system, i.e., not getting updated*/
361     PetscCall(MatTransposeMatMult(am->subsolverX->jacobian_equality, am->subsolverX->jacobian_equality, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &am->ATA));
362   }
363 
364   is_reg_shell = PETSC_FALSE;
365 
366   PetscCall(PetscObjectTypeCompare((PetscObject)am->subsolverZ, TAOSHELL, &is_reg_shell));
367 
368   if (!is_reg_shell) {
369     switch (am->regswitch) {
370     case TAO_ADMM_REGULARIZER_USER:
371       break;
372     case TAO_ADMM_REGULARIZER_SOFT_THRESH:
373       /* Soft Threshold. */
374       break;
375     }
376     if (am->ops->regobjgrad) PetscCall(TaoSetObjectiveAndGradient(am->subsolverZ, NULL, RegObjGradUpdate, tao));
377     if (am->Hz && am->ops->reghess) PetscCall(TaoSetHessian(am->subsolverZ, am->Hz, am->Hzpre, RegHessianUpdate, tao));
378   }
379 
380   switch (am->update) {
381   case TAO_ADMM_UPDATE_BASIC:
382     if (am->subsolverX->hessian) {
383       /* In basic case, Hessian does not get updated w.r.t. to spectral penalty
384        * Here, when A is set, i.e., am->xJI, add mu*ATA to Hessian*/
385       if (!am->xJI) {
386         PetscCall(MatAXPY(am->subsolverX->hessian, am->mu, am->ATA, DIFFERENT_NONZERO_PATTERN));
387       } else {
388         PetscCall(MatShift(am->subsolverX->hessian, am->mu));
389       }
390     }
391     if (am->subsolverZ->hessian && am->regswitch == TAO_ADMM_REGULARIZER_USER) {
392       if (am->regswitch == TAO_ADMM_REGULARIZER_USER && !am->zJI) {
393         PetscCall(MatAXPY(am->subsolverZ->hessian, am->mu, am->BTB, DIFFERENT_NONZERO_PATTERN));
394       } else {
395         PetscCall(MatShift(am->subsolverZ->hessian, am->mu));
396       }
397     }
398     break;
399   case TAO_ADMM_UPDATE_ADAPTIVE:
400   case TAO_ADMM_UPDATE_ADAPTIVE_RELAXED:
401     break;
402   }
403 
404   PetscCall(PetscCitationsRegister(citation, &cited));
405   tao->reason = TAO_CONTINUE_ITERATING;
406 
407   while (tao->reason == TAO_CONTINUE_ITERATING) {
408     PetscTryTypeMethod(tao, update, tao->niter, tao->user_update);
409     PetscCall(VecCopy(am->Bz, am->Bzold));
410 
411     /* x update */
412     PetscCall(TaoSolve(am->subsolverX));
413     PetscCall(TaoComputeJacobianEquality(am->subsolverX, am->subsolverX->solution, am->subsolverX->jacobian_equality, am->subsolverX->jacobian_equality_pre));
414     PetscCall(MatMult(am->subsolverX->jacobian_equality, am->subsolverX->solution, am->Ax));
415 
416     am->Hxbool = PETSC_TRUE;
417 
418     /* z update */
419     switch (am->regswitch) {
420     case TAO_ADMM_REGULARIZER_USER:
421       PetscCall(TaoSolve(am->subsolverZ));
422       break;
423     case TAO_ADMM_REGULARIZER_SOFT_THRESH:
424       /* L1 assumes A,B jacobians are identity nxn matrix */
425       PetscCall(VecWAXPY(am->workJacobianRight, 1 / am->mu, am->y, am->Ax));
426       PetscCall(TaoSoftThreshold(am->workJacobianRight, -am->lambda / am->mu, am->lambda / am->mu, am->subsolverZ->solution));
427       break;
428     }
429     am->Hzbool = PETSC_TRUE;
430     /* Returns Ax + Bz - c with updated Ax,Bz vectors */
431     PetscCall(ADMMUpdateConstraintResidualVector(tao, am->subsolverX->solution, am->subsolverZ->solution, am->Ax, am->Bz, am->residual));
432     /* Dual variable, y += y + mu*(Ax+Bz-c) */
433     PetscCall(VecWAXPY(am->y, am->mu, am->residual, am->yold));
434 
435     /* stopping tolerance update */
436     PetscCall(TaoADMMToleranceUpdate(tao));
437 
438     /* Updating Spectral Penalty */
439     switch (am->update) {
440     case TAO_ADMM_UPDATE_BASIC:
441       am->muold = am->mu;
442       break;
443     case TAO_ADMM_UPDATE_ADAPTIVE:
444     case TAO_ADMM_UPDATE_ADAPTIVE_RELAXED:
445       if (tao->niter == 0) {
446         PetscCall(VecCopy(am->y, am->y0));
447         PetscCall(VecWAXPY(am->residual, 1., am->Ax, am->Bzold));
448         if (am->constraint) PetscCall(VecAXPY(am->residual, -1., am->constraint));
449         PetscCall(VecWAXPY(am->yhatold, -am->mu, am->residual, am->yold));
450         PetscCall(VecCopy(am->Ax, am->Axold));
451         PetscCall(VecCopy(am->Bz, am->Bz0));
452         am->muold = am->mu;
453       } else if (tao->niter % am->T == 1) {
454         /* we have compute Bzold in a previous iteration, and we computed Ax above */
455         PetscCall(VecWAXPY(am->residual, 1., am->Ax, am->Bzold));
456         if (am->constraint) PetscCall(VecAXPY(am->residual, -1., am->constraint));
457         PetscCall(VecWAXPY(am->yhat, -am->mu, am->residual, am->yold));
458         PetscCall(AdaptiveADMMPenaltyUpdate(tao));
459         PetscCall(VecCopy(am->Ax, am->Axold));
460         PetscCall(VecCopy(am->Bz, am->Bz0));
461         PetscCall(VecCopy(am->yhat, am->yhatold));
462         PetscCall(VecCopy(am->y, am->y0));
463       } else {
464         am->muold = am->mu;
465       }
466       break;
467     default:
468       break;
469     }
470     tao->niter++;
471 
472     /* Calculate original function values. misfit part was done in TaoADMMToleranceUpdate*/
473     switch (am->regswitch) {
474     case TAO_ADMM_REGULARIZER_USER:
475       if (is_reg_shell) {
476         PetscCall(ADMML1EpsilonNorm(tao, am->subsolverZ->solution, am->l1epsilon, &reg_func));
477       } else {
478         PetscCall((*am->ops->regobjgrad)(am->subsolverZ, am->subsolverX->solution, &reg_func, tempL, am->regobjgradP));
479       }
480       break;
481     case TAO_ADMM_REGULARIZER_SOFT_THRESH:
482       PetscCall(ADMML1EpsilonNorm(tao, am->subsolverZ->solution, am->l1epsilon, &reg_func));
483       break;
484     }
485     PetscCall(VecCopy(am->y, am->yold));
486     PetscCall(ADMMUpdateConstraintResidualVector(tao, am->subsolverX->solution, am->subsolverZ->solution, am->Ax, am->Bz, am->residual));
487     PetscCall(VecNorm(am->residual, NORM_2, &am->resnorm));
488     PetscCall(TaoLogConvergenceHistory(tao, am->last_misfit_val + reg_func, am->dualres, am->resnorm, tao->ksp_its));
489 
490     PetscCall(TaoMonitor(tao, tao->niter, am->last_misfit_val + reg_func, am->dualres, am->resnorm, 1.0));
491     PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
492   }
493   /* Update vectors */
494   PetscCall(VecCopy(am->subsolverX->solution, tao->solution));
495   PetscCall(VecCopy(am->subsolverX->gradient, tao->gradient));
496   PetscCall(PetscObjectCompose((PetscObject)am->subsolverX, "TaoGetADMMParentTao_ADMM", NULL));
497   PetscCall(PetscObjectCompose((PetscObject)am->subsolverZ, "TaoGetADMMParentTao_ADMM", NULL));
498   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetRegularizerType_C", NULL));
499   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetRegularizerType_C", NULL));
500   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetUpdateType_C", NULL));
501   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetUpdateType_C", NULL));
502   PetscFunctionReturn(PETSC_SUCCESS);
503 }
504 
TaoSetFromOptions_ADMM(Tao tao,PetscOptionItems PetscOptionsObject)505 static PetscErrorCode TaoSetFromOptions_ADMM(Tao tao, PetscOptionItems PetscOptionsObject)
506 {
507   TAO_ADMM *am = (TAO_ADMM *)tao->data;
508 
509   PetscFunctionBegin;
510   PetscOptionsHeadBegin(PetscOptionsObject, "ADMM problem that solves f(x) in a form of f(x) + g(z) subject to x - z = 0. Norm 1 and 2 are supported. Different subsolver routines can be selected. ");
511   PetscCall(PetscOptionsReal("-tao_admm_regularizer_coefficient", "regularizer constant", "", am->lambda, &am->lambda, NULL));
512   PetscCall(PetscOptionsReal("-tao_admm_spectral_penalty", "Constant for Augmented Lagrangian term.", "", am->mu, &am->mu, NULL));
513   PetscCall(PetscOptionsReal("-tao_admm_relaxation_parameter", "x relaxation parameter for Z update.", "", am->gamma, &am->gamma, NULL));
514   PetscCall(PetscOptionsReal("-tao_admm_tolerance_update_factor", "ADMM dynamic tolerance update factor.", "", am->tol, &am->tol, NULL));
515   PetscCall(PetscOptionsReal("-tao_admm_spectral_penalty_update_factor", "ADMM spectral penalty update curvature safeguard value.", "", am->orthval, &am->orthval, NULL));
516   PetscCall(PetscOptionsReal("-tao_admm_minimum_spectral_penalty", "Set ADMM minimum spectral penalty.", "", am->mumin, &am->mumin, NULL));
517   PetscCall(PetscOptionsEnum("-tao_admm_dual_update", "Lagrangian dual update policy", "TaoADMMUpdateType", TaoADMMUpdateTypes, (PetscEnum)am->update, (PetscEnum *)&am->update, NULL));
518   PetscCall(PetscOptionsEnum("-tao_admm_regularizer_type", "ADMM regularizer update rule", "TaoADMMRegularizerType", TaoADMMRegularizerTypes, (PetscEnum)am->regswitch, (PetscEnum *)&am->regswitch, NULL));
519   PetscOptionsHeadEnd();
520   PetscCall(TaoSetFromOptions(am->subsolverX));
521   if (am->regswitch != TAO_ADMM_REGULARIZER_SOFT_THRESH) PetscCall(TaoSetFromOptions(am->subsolverZ));
522   PetscFunctionReturn(PETSC_SUCCESS);
523 }
524 
TaoView_ADMM(Tao tao,PetscViewer viewer)525 static PetscErrorCode TaoView_ADMM(Tao tao, PetscViewer viewer)
526 {
527   TAO_ADMM *am = (TAO_ADMM *)tao->data;
528 
529   PetscFunctionBegin;
530   PetscCall(PetscViewerASCIIPushTab(viewer));
531   PetscCall(TaoView(am->subsolverX, viewer));
532   PetscCall(TaoView(am->subsolverZ, viewer));
533   PetscCall(PetscViewerASCIIPopTab(viewer));
534   PetscFunctionReturn(PETSC_SUCCESS);
535 }
536 
TaoSetUp_ADMM(Tao tao)537 static PetscErrorCode TaoSetUp_ADMM(Tao tao)
538 {
539   TAO_ADMM *am = (TAO_ADMM *)tao->data;
540   PetscInt  n, N, M;
541 
542   PetscFunctionBegin;
543   PetscCall(VecGetLocalSize(tao->solution, &n));
544   PetscCall(VecGetSize(tao->solution, &N));
545   /* If Jacobian is given as NULL, it means Jacobian is identity matrix with size of solution vector */
546   if (!am->JB) {
547     am->zJI = PETSC_TRUE;
548     PetscCall(MatCreateShell(PetscObjectComm((PetscObject)tao), n, n, PETSC_DETERMINE, PETSC_DETERMINE, NULL, &am->JB));
549     PetscCall(MatShellSetOperation(am->JB, MATOP_MULT, (PetscErrorCodeFn *)JacobianIdentityB));
550     PetscCall(MatShellSetOperation(am->JB, MATOP_MULT_TRANSPOSE, (PetscErrorCodeFn *)JacobianIdentityB));
551     am->JBpre = am->JB;
552   }
553   if (!am->JA) {
554     am->xJI = PETSC_TRUE;
555     PetscCall(MatCreateShell(PetscObjectComm((PetscObject)tao), n, n, PETSC_DETERMINE, PETSC_DETERMINE, NULL, &am->JA));
556     PetscCall(MatShellSetOperation(am->JA, MATOP_MULT, (PetscErrorCodeFn *)JacobianIdentity));
557     PetscCall(MatShellSetOperation(am->JA, MATOP_MULT_TRANSPOSE, (PetscErrorCodeFn *)JacobianIdentity));
558     am->JApre = am->JA;
559   }
560   PetscCall(MatCreateVecs(am->JA, NULL, &am->Ax));
561   if (!tao->gradient) PetscCall(VecDuplicate(tao->solution, &tao->gradient));
562   PetscCall(TaoSetSolution(am->subsolverX, tao->solution));
563   if (!am->z) {
564     PetscCall(VecDuplicate(tao->solution, &am->z));
565     PetscCall(VecSet(am->z, 0.0));
566   }
567   PetscCall(TaoSetSolution(am->subsolverZ, am->z));
568   if (!am->workLeft) PetscCall(VecDuplicate(tao->solution, &am->workLeft));
569   if (!am->Axold) PetscCall(VecDuplicate(am->Ax, &am->Axold));
570   if (!am->workJacobianRight) PetscCall(VecDuplicate(am->Ax, &am->workJacobianRight));
571   if (!am->workJacobianRight2) PetscCall(VecDuplicate(am->Ax, &am->workJacobianRight2));
572   if (!am->Bz) PetscCall(VecDuplicate(am->Ax, &am->Bz));
573   if (!am->Bzold) PetscCall(VecDuplicate(am->Ax, &am->Bzold));
574   if (!am->Bz0) PetscCall(VecDuplicate(am->Ax, &am->Bz0));
575   if (!am->y) {
576     PetscCall(VecDuplicate(am->Ax, &am->y));
577     PetscCall(VecSet(am->y, 0.0));
578   }
579   if (!am->yold) {
580     PetscCall(VecDuplicate(am->Ax, &am->yold));
581     PetscCall(VecSet(am->yold, 0.0));
582   }
583   if (!am->y0) {
584     PetscCall(VecDuplicate(am->Ax, &am->y0));
585     PetscCall(VecSet(am->y0, 0.0));
586   }
587   if (!am->yhat) {
588     PetscCall(VecDuplicate(am->Ax, &am->yhat));
589     PetscCall(VecSet(am->yhat, 0.0));
590   }
591   if (!am->yhatold) {
592     PetscCall(VecDuplicate(am->Ax, &am->yhatold));
593     PetscCall(VecSet(am->yhatold, 0.0));
594   }
595   if (!am->residual) {
596     PetscCall(VecDuplicate(am->Ax, &am->residual));
597     PetscCall(VecSet(am->residual, 0.0));
598   }
599   if (!am->constraint) {
600     am->constraint = NULL;
601   } else {
602     PetscCall(VecGetSize(am->constraint, &M));
603     PetscCheck(M == N, PetscObjectComm((PetscObject)tao), PETSC_ERR_ARG_WRONGSTATE, "Solution vector and constraint vector must be of same size!");
604   }
605 
606   /* Save changed tao tolerance for adaptive tolerance */
607   if (tao->gatol != tao->default_gatol) am->gatol_admm = tao->gatol;
608   if (tao->catol != tao->default_catol) am->catol_admm = tao->catol;
609 
610   /*Update spectral and dual elements to X subsolver */
611   PetscCall(TaoSetObjectiveAndGradient(am->subsolverX, NULL, SubObjGradUpdate, tao));
612   PetscCall(TaoSetJacobianEqualityRoutine(am->subsolverX, am->JA, am->JApre, am->ops->misfitjac, am->misfitjacobianP));
613   PetscCall(TaoSetJacobianEqualityRoutine(am->subsolverZ, am->JB, am->JBpre, am->ops->regjac, am->regjacobianP));
614   PetscFunctionReturn(PETSC_SUCCESS);
615 }
616 
TaoDestroy_ADMM(Tao tao)617 static PetscErrorCode TaoDestroy_ADMM(Tao tao)
618 {
619   TAO_ADMM *am = (TAO_ADMM *)tao->data;
620 
621   PetscFunctionBegin;
622   PetscCall(VecDestroy(&am->z));
623   PetscCall(VecDestroy(&am->Ax));
624   PetscCall(VecDestroy(&am->Axold));
625   PetscCall(VecDestroy(&am->Bz));
626   PetscCall(VecDestroy(&am->Bzold));
627   PetscCall(VecDestroy(&am->Bz0));
628   PetscCall(VecDestroy(&am->residual));
629   PetscCall(VecDestroy(&am->y));
630   PetscCall(VecDestroy(&am->yold));
631   PetscCall(VecDestroy(&am->y0));
632   PetscCall(VecDestroy(&am->yhat));
633   PetscCall(VecDestroy(&am->yhatold));
634   PetscCall(VecDestroy(&am->workLeft));
635   PetscCall(VecDestroy(&am->workJacobianRight));
636   PetscCall(VecDestroy(&am->workJacobianRight2));
637 
638   PetscCall(MatDestroy(&am->JA));
639   PetscCall(MatDestroy(&am->JB));
640   if (!am->xJI) PetscCall(MatDestroy(&am->JApre));
641   if (!am->zJI) PetscCall(MatDestroy(&am->JBpre));
642   if (am->Hx) {
643     PetscCall(MatDestroy(&am->Hx));
644     PetscCall(MatDestroy(&am->Hxpre));
645   }
646   if (am->Hz) {
647     PetscCall(MatDestroy(&am->Hz));
648     PetscCall(MatDestroy(&am->Hzpre));
649   }
650   PetscCall(MatDestroy(&am->ATA));
651   PetscCall(MatDestroy(&am->BTB));
652   PetscCall(TaoDestroy(&am->subsolverX));
653   PetscCall(TaoDestroy(&am->subsolverZ));
654   am->parent = NULL;
655   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetRegularizerType_C", NULL));
656   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetRegularizerType_C", NULL));
657   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetUpdateType_C", NULL));
658   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetUpdateType_C", NULL));
659   PetscCall(PetscFree(tao->data));
660   PetscFunctionReturn(PETSC_SUCCESS);
661 }
662 
663 /*MC
664   TAOADMM - Alternating direction method of multipliers method for solving linear problems with
665             constraints. in a $ \min_x f(x) + g(z)$  s.t. $Ax+Bz=c$.
666             This algorithm employs two sub Tao solvers, of which type can be specified
667             by the user. User need to provide ObjectiveAndGradient routine, and/or HessianRoutine for both subsolvers.
668             Hessians can be given boolean flag determining whether they change with respect to a input vector. This can be set via
669             `TaoADMMSet{Misfit,Regularizer}HessianChangeStatus()`.
670             Second subsolver does support `TAOSHELL`. It should be noted that L1-norm is used for objective value for `TAOSHELL` type.
671             There is option to set regularizer option, and currently soft-threshold is implemented. For spectral penalty update,
672             currently there are basic option and adaptive option.
673             Constraint is set at Ax+Bz=c, and A and B can be set with `TaoADMMSet{Misfit,Regularizer}ConstraintJacobian()`.
674             c can be set with `TaoADMMSetConstraintVectorRHS()`.
675             The user can also provide regularizer weight for second subsolver. {cite}`xu2017adaptive`
676 
677   Options Database Keys:
678 + -tao_admm_regularizer_coefficient        - regularizer constant (default 1.e-6)
679 . -tao_admm_spectral_penalty               - Constant for Augmented Lagrangian term (default 1.)
680 . -tao_admm_relaxation_parameter           - relaxation parameter for Z update (default 1.)
681 . -tao_admm_tolerance_update_factor        - ADMM dynamic tolerance update factor (default 1.e-12)
682 . -tao_admm_spectral_penalty_update_factor - ADMM spectral penalty update curvature safeguard value (default 0.2)
683 . -tao_admm_minimum_spectral_penalty       - Set ADMM minimum spectral penalty (default 0)
684 . -tao_admm_dual_update                    - Lagrangian dual update policy ("basic","adaptive","adaptive-relaxed") (default "basic")
685 - -tao_admm_regularizer_type               - ADMM regularizer update rule ("user","soft-threshold") (default "soft-threshold")
686 
687   Level: beginner
688 
689 .seealso: `TaoADMMSetMisfitHessianChangeStatus()`, `TaoADMMSetRegHessianChangeStatus()`, `TaoADMMGetSpectralPenalty()`,
690           `TaoADMMGetMisfitSubsolver()`, `TaoADMMGetRegularizationSubsolver()`, `TaoADMMSetConstraintVectorRHS()`,
691           `TaoADMMSetMinimumSpectralPenalty()`, `TaoADMMSetRegularizerCoefficient()`, `TaoADMMGetRegularizerCoefficient()`,
692           `TaoADMMSetRegularizerConstraintJacobian()`, `TaoADMMSetMisfitConstraintJacobian()`,
693           `TaoADMMSetMisfitObjectiveAndGradientRoutine()`, `TaoADMMSetMisfitHessianRoutine()`,
694           `TaoADMMSetRegularizerObjectiveAndGradientRoutine()`, `TaoADMMSetRegularizerHessianRoutine()`,
695           `TaoGetADMMParentTao()`, `TaoADMMGetDualVector()`, `TaoADMMSetRegularizerType()`,
696           `TaoADMMGetRegularizerType()`, `TaoADMMSetUpdateType()`, `TaoADMMGetUpdateType()`
697 M*/
698 
TaoCreate_ADMM(Tao tao)699 PETSC_EXTERN PetscErrorCode TaoCreate_ADMM(Tao tao)
700 {
701   TAO_ADMM *am;
702 
703   PetscFunctionBegin;
704   PetscCall(PetscNew(&am));
705 
706   tao->ops->destroy        = TaoDestroy_ADMM;
707   tao->ops->setup          = TaoSetUp_ADMM;
708   tao->ops->setfromoptions = TaoSetFromOptions_ADMM;
709   tao->ops->view           = TaoView_ADMM;
710   tao->ops->solve          = TaoSolve_ADMM;
711 
712   PetscCall(TaoParametersInitialize(tao));
713 
714   tao->data           = (void *)am;
715   am->l1epsilon       = 1e-6;
716   am->lambda          = 1e-4;
717   am->mu              = 1.;
718   am->muold           = 0.;
719   am->mueps           = PETSC_MACHINE_EPSILON;
720   am->mumin           = 0.;
721   am->orthval         = 0.2;
722   am->T               = 2;
723   am->parent          = tao;
724   am->update          = TAO_ADMM_UPDATE_BASIC;
725   am->regswitch       = TAO_ADMM_REGULARIZER_SOFT_THRESH;
726   am->tol             = PETSC_SMALL;
727   am->const_norm      = 0;
728   am->resnorm         = 0;
729   am->dualres         = 0;
730   am->ops->regobjgrad = NULL;
731   am->ops->reghess    = NULL;
732   am->gamma           = 1;
733   am->regobjgradP     = NULL;
734   am->reghessP        = NULL;
735   am->gatol_admm      = 1e-8;
736   am->catol_admm      = 0;
737   am->Hxchange        = PETSC_TRUE;
738   am->Hzchange        = PETSC_TRUE;
739   am->Hzbool          = PETSC_TRUE;
740   am->Hxbool          = PETSC_TRUE;
741 
742   PetscCall(TaoCreate(PetscObjectComm((PetscObject)tao), &am->subsolverX));
743   PetscCall(TaoSetOptionsPrefix(am->subsolverX, "misfit_"));
744   PetscCall(PetscObjectIncrementTabLevel((PetscObject)am->subsolverX, (PetscObject)tao, 1));
745   PetscCall(TaoCreate(PetscObjectComm((PetscObject)tao), &am->subsolverZ));
746   PetscCall(TaoSetOptionsPrefix(am->subsolverZ, "reg_"));
747   PetscCall(PetscObjectIncrementTabLevel((PetscObject)am->subsolverZ, (PetscObject)tao, 1));
748 
749   PetscCall(TaoSetType(am->subsolverX, TAONLS));
750   PetscCall(TaoSetType(am->subsolverZ, TAONLS));
751   PetscCall(PetscObjectCompose((PetscObject)am->subsolverX, "TaoGetADMMParentTao_ADMM", (PetscObject)tao));
752   PetscCall(PetscObjectCompose((PetscObject)am->subsolverZ, "TaoGetADMMParentTao_ADMM", (PetscObject)tao));
753   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetRegularizerType_C", TaoADMMSetRegularizerType_ADMM));
754   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetRegularizerType_C", TaoADMMGetRegularizerType_ADMM));
755   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMSetUpdateType_C", TaoADMMSetUpdateType_ADMM));
756   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoADMMGetUpdateType_C", TaoADMMGetUpdateType_ADMM));
757   PetscFunctionReturn(PETSC_SUCCESS);
758 }
759 
760 /*@
761   TaoADMMSetMisfitHessianChangeStatus - Set boolean that determines  whether Hessian matrix of misfit subsolver changes with respect to input vector.
762 
763   Collective
764 
765   Input Parameters:
766 + tao - the Tao solver context.
767 - b   - the Hessian matrix change status boolean, `PETSC_FALSE`  when the Hessian matrix does not change, `PETSC_TRUE` otherwise.
768 
769   Level: advanced
770 
771 .seealso: `TAOADMM`
772 @*/
TaoADMMSetMisfitHessianChangeStatus(Tao tao,PetscBool b)773 PetscErrorCode TaoADMMSetMisfitHessianChangeStatus(Tao tao, PetscBool b)
774 {
775   TAO_ADMM *am = (TAO_ADMM *)tao->data;
776 
777   PetscFunctionBegin;
778   am->Hxchange = b;
779   PetscFunctionReturn(PETSC_SUCCESS);
780 }
781 
782 /*@
783   TaoADMMSetRegHessianChangeStatus - Set boolean that determines whether Hessian matrix of regularization subsolver changes with respect to input vector.
784 
785   Collective
786 
787   Input Parameters:
788 + tao - the `Tao` solver context
789 - b   - the Hessian matrix change status boolean, `PETSC_FALSE` when the Hessian matrix does not change, `PETSC_TRUE` otherwise.
790 
791   Level: advanced
792 
793 .seealso: `TAOADMM`
794 @*/
TaoADMMSetRegHessianChangeStatus(Tao tao,PetscBool b)795 PetscErrorCode TaoADMMSetRegHessianChangeStatus(Tao tao, PetscBool b)
796 {
797   TAO_ADMM *am = (TAO_ADMM *)tao->data;
798 
799   PetscFunctionBegin;
800   am->Hzchange = b;
801   PetscFunctionReturn(PETSC_SUCCESS);
802 }
803 
804 /*@
805   TaoADMMSetSpectralPenalty - Set the spectral penalty (mu) value
806 
807   Collective
808 
809   Input Parameters:
810 + tao - the `Tao` solver context
811 - mu  - spectral penalty
812 
813   Level: advanced
814 
815 .seealso: `TaoADMMSetMinimumSpectralPenalty()`, `TAOADMM`
816 @*/
TaoADMMSetSpectralPenalty(Tao tao,PetscReal mu)817 PetscErrorCode TaoADMMSetSpectralPenalty(Tao tao, PetscReal mu)
818 {
819   TAO_ADMM *am = (TAO_ADMM *)tao->data;
820 
821   PetscFunctionBegin;
822   am->mu = mu;
823   PetscFunctionReturn(PETSC_SUCCESS);
824 }
825 
826 /*@
827   TaoADMMGetSpectralPenalty - Get the spectral penalty (mu) value
828 
829   Collective
830 
831   Input Parameter:
832 . tao - the `Tao` solver context
833 
834   Output Parameter:
835 . mu - spectral penalty
836 
837   Level: advanced
838 
839 .seealso: `TaoADMMSetMinimumSpectralPenalty()`, `TaoADMMSetSpectralPenalty()`, `TAOADMM`
840 @*/
TaoADMMGetSpectralPenalty(Tao tao,PetscReal * mu)841 PetscErrorCode TaoADMMGetSpectralPenalty(Tao tao, PetscReal *mu)
842 {
843   TAO_ADMM *am = (TAO_ADMM *)tao->data;
844 
845   PetscFunctionBegin;
846   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
847   PetscAssertPointer(mu, 2);
848   *mu = am->mu;
849   PetscFunctionReturn(PETSC_SUCCESS);
850 }
851 
852 /*@
853   TaoADMMGetMisfitSubsolver - Get the pointer to the misfit subsolver inside `TAOADMM`
854 
855   Collective
856 
857   Input Parameter:
858 . tao - the `Tao` solver context
859 
860   Output Parameter:
861 . misfit - the `Tao` subsolver context
862 
863   Level: advanced
864 
865 .seealso: `TAOADMM`, `Tao`
866 @*/
TaoADMMGetMisfitSubsolver(Tao tao,Tao * misfit)867 PetscErrorCode TaoADMMGetMisfitSubsolver(Tao tao, Tao *misfit)
868 {
869   TAO_ADMM *am = (TAO_ADMM *)tao->data;
870 
871   PetscFunctionBegin;
872   *misfit = am->subsolverX;
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 /*@
877   TaoADMMGetRegularizationSubsolver - Get the pointer to the regularization subsolver inside `TAOADMM`
878 
879   Collective
880 
881   Input Parameter:
882 . tao - the `Tao` solver context
883 
884   Output Parameter:
885 . reg - the `Tao` subsolver context
886 
887   Level: advanced
888 
889 .seealso: `TAOADMM`, `Tao`
890 @*/
TaoADMMGetRegularizationSubsolver(Tao tao,Tao * reg)891 PetscErrorCode TaoADMMGetRegularizationSubsolver(Tao tao, Tao *reg)
892 {
893   TAO_ADMM *am = (TAO_ADMM *)tao->data;
894 
895   PetscFunctionBegin;
896   *reg = am->subsolverZ;
897   PetscFunctionReturn(PETSC_SUCCESS);
898 }
899 
900 /*@
901   TaoADMMSetConstraintVectorRHS - Set the RHS constraint vector for `TAOADMM`
902 
903   Collective
904 
905   Input Parameters:
906 + tao - the `Tao` solver context
907 - c   - RHS vector
908 
909   Level: advanced
910 
911 .seealso: `TAOADMM`
912 @*/
TaoADMMSetConstraintVectorRHS(Tao tao,Vec c)913 PetscErrorCode TaoADMMSetConstraintVectorRHS(Tao tao, Vec c)
914 {
915   TAO_ADMM *am = (TAO_ADMM *)tao->data;
916 
917   PetscFunctionBegin;
918   am->constraint = c;
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 /*@
923   TaoADMMSetMinimumSpectralPenalty - Set the minimum value for the spectral penalty
924 
925   Collective
926 
927   Input Parameters:
928 + tao - the `Tao` solver context
929 - mu  - minimum spectral penalty value
930 
931   Level: advanced
932 
933 .seealso: `TaoADMMGetSpectralPenalty()`, `TAOADMM`
934 @*/
TaoADMMSetMinimumSpectralPenalty(Tao tao,PetscReal mu)935 PetscErrorCode TaoADMMSetMinimumSpectralPenalty(Tao tao, PetscReal mu)
936 {
937   TAO_ADMM *am = (TAO_ADMM *)tao->data;
938 
939   PetscFunctionBegin;
940   am->mumin = mu;
941   PetscFunctionReturn(PETSC_SUCCESS);
942 }
943 
944 /*@
945   TaoADMMSetRegularizerCoefficient - Set the regularization coefficient lambda for L1 norm regularization case
946 
947   Collective
948 
949   Input Parameters:
950 + tao    - the `Tao` solver context
951 - lambda - L1-norm regularizer coefficient
952 
953   Level: advanced
954 
955 .seealso: `TaoADMMSetMisfitConstraintJacobian()`, `TaoADMMSetRegularizerConstraintJacobian()`, `TAOADMM`
956 @*/
TaoADMMSetRegularizerCoefficient(Tao tao,PetscReal lambda)957 PetscErrorCode TaoADMMSetRegularizerCoefficient(Tao tao, PetscReal lambda)
958 {
959   TAO_ADMM *am = (TAO_ADMM *)tao->data;
960 
961   PetscFunctionBegin;
962   am->lambda = lambda;
963   PetscFunctionReturn(PETSC_SUCCESS);
964 }
965 
966 /*@
967   TaoADMMGetRegularizerCoefficient - Get the regularization coefficient lambda for L1 norm regularization case
968 
969   Collective
970 
971   Input Parameter:
972 . tao - the `Tao` solver context
973 
974   Output Parameter:
975 . lambda - L1-norm regularizer coefficient
976 
977   Level: advanced
978 
979 .seealso: `TaoADMMSetMisfitConstraintJacobian()`, `TaoADMMSetRegularizerConstraintJacobian()`, `TAOADMM`
980 @*/
TaoADMMGetRegularizerCoefficient(Tao tao,PetscReal * lambda)981 PetscErrorCode TaoADMMGetRegularizerCoefficient(Tao tao, PetscReal *lambda)
982 {
983   TAO_ADMM *am = (TAO_ADMM *)tao->data;
984 
985   PetscFunctionBegin;
986   *lambda = am->lambda;
987   PetscFunctionReturn(PETSC_SUCCESS);
988 }
989 
990 /*@C
991   TaoADMMSetMisfitConstraintJacobian - Set the constraint matrix B for the `TAOADMM` algorithm. Matrix B constrains the z variable.
992 
993   Collective
994 
995   Input Parameters:
996 + tao  - the Tao solver context
997 . J    - user-created regularizer constraint Jacobian matrix
998 . Jpre - user-created regularizer Jacobian constraint matrix for constructing the preconditioner, often this is `J`
999 . func - function pointer for the regularizer constraint Jacobian update function
1000 - ctx  - user context for the regularizer Hessian
1001 
1002   Level: advanced
1003 
1004 .seealso: `TaoADMMSetRegularizerCoefficient()`, `TaoADMMSetRegularizerConstraintJacobian()`, `TAOADMM`
1005 @*/
TaoADMMSetMisfitConstraintJacobian(Tao tao,Mat J,Mat Jpre,PetscErrorCode (* func)(Tao,Vec,Mat,Mat,void *),PetscCtx ctx)1006 PetscErrorCode TaoADMMSetMisfitConstraintJacobian(Tao tao, Mat J, Mat Jpre, PetscErrorCode (*func)(Tao, Vec, Mat, Mat, void *), PetscCtx ctx)
1007 {
1008   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1009 
1010   PetscFunctionBegin;
1011   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1012   if (J) {
1013     PetscValidHeaderSpecific(J, MAT_CLASSID, 2);
1014     PetscCheckSameComm(tao, 1, J, 2);
1015   }
1016   if (Jpre) {
1017     PetscValidHeaderSpecific(Jpre, MAT_CLASSID, 3);
1018     PetscCheckSameComm(tao, 1, Jpre, 3);
1019   }
1020   if (ctx) am->misfitjacobianP = ctx;
1021   if (func) am->ops->misfitjac = func;
1022 
1023   if (J) {
1024     PetscCall(PetscObjectReference((PetscObject)J));
1025     PetscCall(MatDestroy(&am->JA));
1026     am->JA = J;
1027   }
1028   if (Jpre) {
1029     PetscCall(PetscObjectReference((PetscObject)Jpre));
1030     PetscCall(MatDestroy(&am->JApre));
1031     am->JApre = Jpre;
1032   }
1033   PetscFunctionReturn(PETSC_SUCCESS);
1034 }
1035 
1036 /*@C
1037   TaoADMMSetRegularizerConstraintJacobian - Set the constraint matrix B for `TAOADMM` algorithm. Matrix B constraints z variable.
1038 
1039   Collective
1040 
1041   Input Parameters:
1042 + tao  - the `Tao` solver context
1043 . J    - user-created regularizer constraint Jacobian matrix
1044 . Jpre - user-created regularizer Jacobian constraint matrix for constructing the preconditioner, often this is `J`
1045 . func - function pointer for the regularizer constraint Jacobian update function
1046 - ctx  - user context for the regularizer Hessian
1047 
1048   Level: advanced
1049 
1050 .seealso: `TaoADMMSetRegularizerCoefficient()`, `TaoADMMSetMisfitConstraintJacobian()`, `TAOADMM`
1051 @*/
TaoADMMSetRegularizerConstraintJacobian(Tao tao,Mat J,Mat Jpre,PetscErrorCode (* func)(Tao,Vec,Mat,Mat,void *),PetscCtx ctx)1052 PetscErrorCode TaoADMMSetRegularizerConstraintJacobian(Tao tao, Mat J, Mat Jpre, PetscErrorCode (*func)(Tao, Vec, Mat, Mat, void *), PetscCtx ctx)
1053 {
1054   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1055 
1056   PetscFunctionBegin;
1057   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1058   if (J) {
1059     PetscValidHeaderSpecific(J, MAT_CLASSID, 2);
1060     PetscCheckSameComm(tao, 1, J, 2);
1061   }
1062   if (Jpre) {
1063     PetscValidHeaderSpecific(Jpre, MAT_CLASSID, 3);
1064     PetscCheckSameComm(tao, 1, Jpre, 3);
1065   }
1066   if (ctx) am->regjacobianP = ctx;
1067   if (func) am->ops->regjac = func;
1068 
1069   if (J) {
1070     PetscCall(PetscObjectReference((PetscObject)J));
1071     PetscCall(MatDestroy(&am->JB));
1072     am->JB = J;
1073   }
1074   if (Jpre) {
1075     PetscCall(PetscObjectReference((PetscObject)Jpre));
1076     PetscCall(MatDestroy(&am->JBpre));
1077     am->JBpre = Jpre;
1078   }
1079   PetscFunctionReturn(PETSC_SUCCESS);
1080 }
1081 
1082 /*@C
1083   TaoADMMSetMisfitObjectiveAndGradientRoutine - Sets the user-defined misfit call-back function
1084 
1085   Collective
1086 
1087   Input Parameters:
1088 + tao  - the `Tao` context
1089 . func - function pointer for the misfit value and gradient evaluation
1090 - ctx  - user context for the misfit
1091 
1092   Level: advanced
1093 
1094 .seealso: `TAOADMM`
1095 @*/
TaoADMMSetMisfitObjectiveAndGradientRoutine(Tao tao,PetscErrorCode (* func)(Tao,Vec,PetscReal *,Vec,void *),PetscCtx ctx)1096 PetscErrorCode TaoADMMSetMisfitObjectiveAndGradientRoutine(Tao tao, PetscErrorCode (*func)(Tao, Vec, PetscReal *, Vec, void *), PetscCtx ctx)
1097 {
1098   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1099 
1100   PetscFunctionBegin;
1101   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1102   am->misfitobjgradP     = ctx;
1103   am->ops->misfitobjgrad = func;
1104   PetscFunctionReturn(PETSC_SUCCESS);
1105 }
1106 
1107 /*@C
1108   TaoADMMSetMisfitHessianRoutine - Sets the user-defined misfit Hessian call-back
1109   function into the algorithm, to be used for subsolverX.
1110 
1111   Collective
1112 
1113   Input Parameters:
1114 + tao  - the `Tao` context
1115 . H    - user-created matrix for the Hessian of the misfit term
1116 . Hpre - user-created matrix for the preconditioner of Hessian of the misfit term
1117 . func - function pointer for the misfit Hessian evaluation
1118 - ctx  - user context for the misfit Hessian
1119 
1120   Level: advanced
1121 
1122 .seealso: `TAOADMM`
1123 @*/
TaoADMMSetMisfitHessianRoutine(Tao tao,Mat H,Mat Hpre,PetscErrorCode (* func)(Tao,Vec,Mat,Mat,void *),PetscCtx ctx)1124 PetscErrorCode TaoADMMSetMisfitHessianRoutine(Tao tao, Mat H, Mat Hpre, PetscErrorCode (*func)(Tao, Vec, Mat, Mat, void *), PetscCtx ctx)
1125 {
1126   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1127 
1128   PetscFunctionBegin;
1129   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1130   if (H) {
1131     PetscValidHeaderSpecific(H, MAT_CLASSID, 2);
1132     PetscCheckSameComm(tao, 1, H, 2);
1133   }
1134   if (Hpre) {
1135     PetscValidHeaderSpecific(Hpre, MAT_CLASSID, 3);
1136     PetscCheckSameComm(tao, 1, Hpre, 3);
1137   }
1138   if (ctx) am->misfithessP = ctx;
1139   if (func) am->ops->misfithess = func;
1140   if (H) {
1141     PetscCall(PetscObjectReference((PetscObject)H));
1142     PetscCall(MatDestroy(&am->Hx));
1143     am->Hx = H;
1144   }
1145   if (Hpre) {
1146     PetscCall(PetscObjectReference((PetscObject)Hpre));
1147     PetscCall(MatDestroy(&am->Hxpre));
1148     am->Hxpre = Hpre;
1149   }
1150   PetscFunctionReturn(PETSC_SUCCESS);
1151 }
1152 
1153 /*@C
1154   TaoADMMSetRegularizerObjectiveAndGradientRoutine - Sets the user-defined regularizer call-back function
1155 
1156   Collective
1157 
1158   Input Parameters:
1159 + tao  - the Tao context
1160 . func - function pointer for the regularizer value and gradient evaluation
1161 - ctx  - user context for the regularizer
1162 
1163   Level: advanced
1164 
1165 .seealso: `TAOADMM`
1166 @*/
TaoADMMSetRegularizerObjectiveAndGradientRoutine(Tao tao,PetscErrorCode (* func)(Tao,Vec,PetscReal *,Vec,void *),PetscCtx ctx)1167 PetscErrorCode TaoADMMSetRegularizerObjectiveAndGradientRoutine(Tao tao, PetscErrorCode (*func)(Tao, Vec, PetscReal *, Vec, void *), PetscCtx ctx)
1168 {
1169   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1170 
1171   PetscFunctionBegin;
1172   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1173   am->regobjgradP     = ctx;
1174   am->ops->regobjgrad = func;
1175   PetscFunctionReturn(PETSC_SUCCESS);
1176 }
1177 
1178 /*@C
1179   TaoADMMSetRegularizerHessianRoutine - Sets the user-defined regularizer Hessian call-back
1180   function, to be used for subsolverZ.
1181 
1182   Collective
1183 
1184   Input Parameters:
1185 + tao  - the `Tao` context
1186 . H    - user-created matrix for the Hessian of the regularization term
1187 . Hpre - user-created matrix for the preconditioner of Hessian of the regularization term
1188 . func - function pointer for the regularizer Hessian evaluation
1189 - ctx  - user context for the regularizer Hessian
1190 
1191   Level: advanced
1192 
1193 .seealso: `TAOADMM`
1194 @*/
TaoADMMSetRegularizerHessianRoutine(Tao tao,Mat H,Mat Hpre,PetscErrorCode (* func)(Tao,Vec,Mat,Mat,void *),PetscCtx ctx)1195 PetscErrorCode TaoADMMSetRegularizerHessianRoutine(Tao tao, Mat H, Mat Hpre, PetscErrorCode (*func)(Tao, Vec, Mat, Mat, void *), PetscCtx ctx)
1196 {
1197   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1198 
1199   PetscFunctionBegin;
1200   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1201   if (H) {
1202     PetscValidHeaderSpecific(H, MAT_CLASSID, 2);
1203     PetscCheckSameComm(tao, 1, H, 2);
1204   }
1205   if (Hpre) {
1206     PetscValidHeaderSpecific(Hpre, MAT_CLASSID, 3);
1207     PetscCheckSameComm(tao, 1, Hpre, 3);
1208   }
1209   if (ctx) am->reghessP = ctx;
1210   if (func) am->ops->reghess = func;
1211   if (H) {
1212     PetscCall(PetscObjectReference((PetscObject)H));
1213     PetscCall(MatDestroy(&am->Hz));
1214     am->Hz = H;
1215   }
1216   if (Hpre) {
1217     PetscCall(PetscObjectReference((PetscObject)Hpre));
1218     PetscCall(MatDestroy(&am->Hzpre));
1219     am->Hzpre = Hpre;
1220   }
1221   PetscFunctionReturn(PETSC_SUCCESS);
1222 }
1223 
1224 /*@
1225   TaoGetADMMParentTao - Gets pointer to parent `TAOADMM`, used by inner subsolver.
1226 
1227   Collective
1228 
1229   Input Parameter:
1230 . tao - the `Tao` context
1231 
1232   Output Parameter:
1233 . admm_tao - the parent `Tao` context
1234 
1235   Level: advanced
1236 
1237 .seealso: `TAOADMM`
1238 @*/
TaoGetADMMParentTao(Tao tao,Tao * admm_tao)1239 PetscErrorCode TaoGetADMMParentTao(Tao tao, Tao *admm_tao)
1240 {
1241   PetscFunctionBegin;
1242   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1243   PetscCall(PetscObjectQuery((PetscObject)tao, "TaoGetADMMParentTao_ADMM", (PetscObject *)admm_tao));
1244   PetscFunctionReturn(PETSC_SUCCESS);
1245 }
1246 
1247 /*@
1248   TaoADMMGetDualVector - Returns the dual vector associated with the current `TAOADMM` state
1249 
1250   Not Collective
1251 
1252   Input Parameter:
1253 . tao - the `Tao` context
1254 
1255   Output Parameter:
1256 . Y - the current solution
1257 
1258   Level: intermediate
1259 
1260 .seealso: `TAOADMM`
1261 @*/
TaoADMMGetDualVector(Tao tao,Vec * Y)1262 PetscErrorCode TaoADMMGetDualVector(Tao tao, Vec *Y)
1263 {
1264   TAO_ADMM *am = (TAO_ADMM *)tao->data;
1265 
1266   PetscFunctionBegin;
1267   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1268   *Y = am->y;
1269   PetscFunctionReturn(PETSC_SUCCESS);
1270 }
1271 
1272 /*@
1273   TaoADMMSetRegularizerType - Set regularizer type for `TAOADMM` routine
1274 
1275   Not Collective
1276 
1277   Input Parameters:
1278 + tao  - the `Tao` context
1279 - type - regularizer type
1280 
1281   Options Database Key:
1282 . -tao_admm_regularizer_type <admm_regularizer_user,admm_regularizer_soft_thresh> - select the regularizer
1283 
1284   Level: intermediate
1285 
1286 .seealso: `TaoADMMGetRegularizerType()`, `TaoADMMRegularizerType`, `TAOADMM`
1287 @*/
TaoADMMSetRegularizerType(Tao tao,TaoADMMRegularizerType type)1288 PetscErrorCode TaoADMMSetRegularizerType(Tao tao, TaoADMMRegularizerType type)
1289 {
1290   PetscFunctionBegin;
1291   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1292   PetscValidLogicalCollectiveEnum(tao, type, 2);
1293   PetscTryMethod(tao, "TaoADMMSetRegularizerType_C", (Tao, TaoADMMRegularizerType), (tao, type));
1294   PetscFunctionReturn(PETSC_SUCCESS);
1295 }
1296 
1297 /*@
1298   TaoADMMGetRegularizerType - Gets the type of regularizer routine for `TAOADMM`
1299 
1300   Not Collective
1301 
1302   Input Parameter:
1303 . tao - the `Tao` context
1304 
1305   Output Parameter:
1306 . type - the type of regularizer
1307 
1308   Level: intermediate
1309 
1310 .seealso: `TaoADMMSetRegularizerType()`, `TaoADMMRegularizerType`, `TAOADMM`
1311 @*/
TaoADMMGetRegularizerType(Tao tao,TaoADMMRegularizerType * type)1312 PetscErrorCode TaoADMMGetRegularizerType(Tao tao, TaoADMMRegularizerType *type)
1313 {
1314   PetscFunctionBegin;
1315   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1316   PetscUseMethod(tao, "TaoADMMGetRegularizerType_C", (Tao, TaoADMMRegularizerType *), (tao, type));
1317   PetscFunctionReturn(PETSC_SUCCESS);
1318 }
1319 
1320 /*@
1321   TaoADMMSetUpdateType - Set update routine for `TAOADMM` routine
1322 
1323   Not Collective
1324 
1325   Input Parameters:
1326 + tao  - the `Tao` context
1327 - type - spectral parameter update type
1328 
1329   Level: intermediate
1330 
1331 .seealso: `TaoADMMGetUpdateType()`, `TaoADMMUpdateType`, `TAOADMM`
1332 @*/
TaoADMMSetUpdateType(Tao tao,TaoADMMUpdateType type)1333 PetscErrorCode TaoADMMSetUpdateType(Tao tao, TaoADMMUpdateType type)
1334 {
1335   PetscFunctionBegin;
1336   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1337   PetscValidLogicalCollectiveEnum(tao, type, 2);
1338   PetscTryMethod(tao, "TaoADMMSetUpdateType_C", (Tao, TaoADMMUpdateType), (tao, type));
1339   PetscFunctionReturn(PETSC_SUCCESS);
1340 }
1341 
1342 /*@
1343   TaoADMMGetUpdateType - Gets the type of spectral penalty update routine for `TAOADMM`
1344 
1345   Not Collective
1346 
1347   Input Parameter:
1348 . tao - the `Tao` context
1349 
1350   Output Parameter:
1351 . type - the type of spectral penalty update routine
1352 
1353   Level: intermediate
1354 
1355 .seealso: `TaoADMMSetUpdateType()`, `TaoADMMUpdateType`, `TAOADMM`
1356 @*/
TaoADMMGetUpdateType(Tao tao,TaoADMMUpdateType * type)1357 PetscErrorCode TaoADMMGetUpdateType(Tao tao, TaoADMMUpdateType *type)
1358 {
1359   PetscFunctionBegin;
1360   PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
1361   PetscUseMethod(tao, "TaoADMMGetUpdateType_C", (Tao, TaoADMMUpdateType *), (tao, type));
1362   PetscFunctionReturn(PETSC_SUCCESS);
1363 }
1364