xref: /petsc/src/tao/unconstrained/impls/owlqn/owlqn.c (revision ccb4e88a40f0b86eaeca07ff64c64e4de2fae686)
1 #include <petsctaolinesearch.h>
2 #include <../src/tao/unconstrained/impls/owlqn/owlqn.h>
3 
4 #define OWLQN_BFGS                0
5 #define OWLQN_SCALED_GRADIENT     1
6 #define OWLQN_GRADIENT            2
7 
8 static PetscErrorCode ProjDirect_OWLQN(Vec d, Vec g)
9 {
10   PetscErrorCode  ierr;
11   const PetscReal *gptr;
12   PetscReal       *dptr;
13   PetscInt        low,high,low1,high1,i;
14 
15   PetscFunctionBegin;
16   ierr = VecGetOwnershipRange(d,&low,&high);CHKERRQ(ierr);
17   ierr = VecGetOwnershipRange(g,&low1,&high1);CHKERRQ(ierr);
18 
19   ierr = VecGetArrayRead(g,&gptr);CHKERRQ(ierr);
20   ierr = VecGetArray(d,&dptr);CHKERRQ(ierr);
21   for (i = 0; i < high-low; i++) {
22     if (dptr[i] * gptr[i] <= 0.0) {
23       dptr[i] = 0.0;
24     }
25   }
26   ierr = VecRestoreArray(d,&dptr);CHKERRQ(ierr);
27   ierr = VecRestoreArrayRead(g,&gptr);CHKERRQ(ierr);
28   PetscFunctionReturn(0);
29 }
30 
31 static PetscErrorCode ComputePseudoGrad_OWLQN(Vec x, Vec gv, PetscReal lambda)
32 {
33   PetscErrorCode  ierr;
34   const PetscReal *xptr;
35   PetscReal       *gptr;
36   PetscInt        low,high,low1,high1,i;
37 
38   PetscFunctionBegin;
39   ierr = VecGetOwnershipRange(x,&low,&high);CHKERRQ(ierr);
40   ierr = VecGetOwnershipRange(gv,&low1,&high1);CHKERRQ(ierr);
41 
42   ierr = VecGetArrayRead(x,&xptr);CHKERRQ(ierr);
43   ierr = VecGetArray(gv,&gptr);CHKERRQ(ierr);
44   for (i = 0; i < high-low; i++) {
45     if (xptr[i] < 0.0)               gptr[i] = gptr[i] - lambda;
46     else if (xptr[i] > 0.0)          gptr[i] = gptr[i] + lambda;
47     else if (gptr[i] + lambda < 0.0) gptr[i] = gptr[i] + lambda;
48     else if (gptr[i] - lambda > 0.0) gptr[i] = gptr[i] - lambda;
49     else                             gptr[i] = 0.0;
50   }
51   ierr = VecRestoreArray(gv,&gptr);CHKERRQ(ierr);
52   ierr = VecRestoreArrayRead(x,&xptr);CHKERRQ(ierr);
53   PetscFunctionReturn(0);
54 }
55 
56 static PetscErrorCode TaoSolve_OWLQN(Tao tao)
57 {
58   TAO_OWLQN                    *lmP = (TAO_OWLQN *)tao->data;
59   PetscReal                    f, fold, gdx, gnorm;
60   PetscReal                    step = 1.0;
61   PetscReal                    delta;
62   PetscErrorCode               ierr;
63   PetscInt                     stepType;
64   PetscInt                     iter = 0;
65   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
66 
67   PetscFunctionBegin;
68   if (tao->XL || tao->XU || tao->ops->computebounds) {
69     ierr = PetscInfo(tao,"WARNING: Variable bounds have been set but will be ignored by owlqn algorithm\n");CHKERRQ(ierr);
70   }
71 
72   /* Check convergence criteria */
73   ierr = TaoComputeObjectiveAndGradient(tao, tao->solution, &f, tao->gradient);CHKERRQ(ierr);
74   ierr = VecCopy(tao->gradient, lmP->GV);CHKERRQ(ierr);
75   ierr = ComputePseudoGrad_OWLQN(tao->solution,lmP->GV,lmP->lambda);CHKERRQ(ierr);
76   ierr = VecNorm(lmP->GV,NORM_2,&gnorm);CHKERRQ(ierr);
77   if (PetscIsInfOrNanReal(f) || PetscIsInfOrNanReal(gnorm)) SETERRQ(PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
78 
79   tao->reason = TAO_CONTINUE_ITERATING;
80   ierr = TaoLogConvergenceHistory(tao,f,gnorm,0.0,tao->ksp_its);CHKERRQ(ierr);
81   ierr = TaoMonitor(tao,iter,f,gnorm,0.0,step);CHKERRQ(ierr);
82   ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr);
83   if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
84 
85   /* Set initial scaling for the function */
86   delta = 2.0 * PetscMax(1.0, PetscAbsScalar(f)) / (gnorm*gnorm);
87   ierr = MatLMVMSetJ0Scale(lmP->M, delta);CHKERRQ(ierr);
88 
89   /* Set counter for gradient/reset steps */
90   lmP->bfgs = 0;
91   lmP->sgrad = 0;
92   lmP->grad = 0;
93 
94   /* Have not converged; continue with Newton method */
95   while (tao->reason == TAO_CONTINUE_ITERATING) {
96     /* Call general purpose update function */
97     if (tao->ops->update) {
98       ierr = (*tao->ops->update)(tao, tao->niter, tao->user_update);CHKERRQ(ierr);
99     }
100 
101     /* Compute direction */
102     ierr = MatLMVMUpdate(lmP->M,tao->solution,tao->gradient);CHKERRQ(ierr);
103     ierr = MatSolve(lmP->M, lmP->GV, lmP->D);CHKERRQ(ierr);
104 
105     ierr = ProjDirect_OWLQN(lmP->D,lmP->GV);CHKERRQ(ierr);
106 
107     ++lmP->bfgs;
108 
109     /* Check for success (descent direction) */
110     ierr = VecDot(lmP->D, lmP->GV , &gdx);CHKERRQ(ierr);
111     if ((gdx <= 0.0) || PetscIsInfOrNanReal(gdx)) {
112 
113       /* Step is not descent or direction produced not a number
114          We can assert bfgsUpdates > 1 in this case because
115          the first solve produces the scaled gradient direction,
116          which is guaranteed to be descent
117 
118          Use steepest descent direction (scaled) */
119       ++lmP->grad;
120 
121       delta = 2.0 * PetscMax(1.0, PetscAbsScalar(f)) / (gnorm*gnorm);
122       ierr = MatLMVMSetJ0Scale(lmP->M, delta);CHKERRQ(ierr);
123       ierr = MatLMVMReset(lmP->M, PETSC_FALSE);CHKERRQ(ierr);
124       ierr = MatLMVMUpdate(lmP->M, tao->solution, tao->gradient);CHKERRQ(ierr);
125       ierr = MatSolve(lmP->M,lmP->GV, lmP->D);CHKERRQ(ierr);
126 
127       ierr = ProjDirect_OWLQN(lmP->D,lmP->GV);CHKERRQ(ierr);
128 
129       lmP->bfgs = 1;
130       ++lmP->sgrad;
131       stepType = OWLQN_SCALED_GRADIENT;
132     } else {
133       if (1 == lmP->bfgs) {
134         /* The first BFGS direction is always the scaled gradient */
135         ++lmP->sgrad;
136         stepType = OWLQN_SCALED_GRADIENT;
137       } else {
138         ++lmP->bfgs;
139         stepType = OWLQN_BFGS;
140       }
141     }
142 
143     ierr = VecScale(lmP->D, -1.0);CHKERRQ(ierr);
144 
145     /* Perform the linesearch */
146     fold = f;
147     ierr = VecCopy(tao->solution, lmP->Xold);CHKERRQ(ierr);
148     ierr = VecCopy(tao->gradient, lmP->Gold);CHKERRQ(ierr);
149 
150     ierr = TaoLineSearchApply(tao->linesearch, tao->solution, &f, lmP->GV, lmP->D, &step,&ls_status);CHKERRQ(ierr);
151     ierr = TaoAddLineSearchCounts(tao);CHKERRQ(ierr);
152 
153     while (((int)ls_status < 0) && (stepType != OWLQN_GRADIENT)) {
154 
155       /* Reset factors and use scaled gradient step */
156       f = fold;
157       ierr = VecCopy(lmP->Xold, tao->solution);CHKERRQ(ierr);
158       ierr = VecCopy(lmP->Gold, tao->gradient);CHKERRQ(ierr);
159       ierr = VecCopy(tao->gradient, lmP->GV);CHKERRQ(ierr);
160 
161       ierr = ComputePseudoGrad_OWLQN(tao->solution,lmP->GV,lmP->lambda);CHKERRQ(ierr);
162 
163       switch(stepType) {
164       case OWLQN_BFGS:
165         /* Failed to obtain acceptable iterate with BFGS step
166            Attempt to use the scaled gradient direction */
167 
168         delta = 2.0 * PetscMax(1.0, PetscAbsScalar(f)) / (gnorm*gnorm);
169         ierr = MatLMVMSetJ0Scale(lmP->M, delta);CHKERRQ(ierr);
170         ierr = MatLMVMReset(lmP->M, PETSC_FALSE);CHKERRQ(ierr);
171         ierr = MatLMVMUpdate(lmP->M, tao->solution, tao->gradient);CHKERRQ(ierr);
172         ierr = MatSolve(lmP->M, lmP->GV, lmP->D);CHKERRQ(ierr);
173 
174         ierr = ProjDirect_OWLQN(lmP->D,lmP->GV);CHKERRQ(ierr);
175 
176         lmP->bfgs = 1;
177         ++lmP->sgrad;
178         stepType = OWLQN_SCALED_GRADIENT;
179         break;
180 
181       case OWLQN_SCALED_GRADIENT:
182         /* The scaled gradient step did not produce a new iterate;
183            attempt to use the gradient direction.
184            Need to make sure we are not using a different diagonal scaling */
185         ierr = MatLMVMSetJ0Scale(lmP->M, 1.0);CHKERRQ(ierr);
186         ierr = MatLMVMReset(lmP->M, PETSC_FALSE);CHKERRQ(ierr);
187         ierr = MatLMVMUpdate(lmP->M, tao->solution, tao->gradient);CHKERRQ(ierr);
188         ierr = MatSolve(lmP->M, lmP->GV, lmP->D);CHKERRQ(ierr);
189 
190         ierr = ProjDirect_OWLQN(lmP->D,lmP->GV);CHKERRQ(ierr);
191 
192         lmP->bfgs = 1;
193         ++lmP->grad;
194         stepType = OWLQN_GRADIENT;
195         break;
196       }
197       ierr = VecScale(lmP->D, -1.0);CHKERRQ(ierr);
198 
199       /* Perform the linesearch */
200       ierr = TaoLineSearchApply(tao->linesearch, tao->solution, &f, lmP->GV, lmP->D, &step, &ls_status);CHKERRQ(ierr);
201       ierr = TaoAddLineSearchCounts(tao);CHKERRQ(ierr);
202     }
203 
204     if ((int)ls_status < 0) {
205       /* Failed to find an improving point*/
206       f = fold;
207       ierr = VecCopy(lmP->Xold, tao->solution);CHKERRQ(ierr);
208       ierr = VecCopy(lmP->Gold, tao->gradient);CHKERRQ(ierr);
209       ierr = VecCopy(tao->gradient, lmP->GV);CHKERRQ(ierr);
210       step = 0.0;
211     } else {
212       /* a little hack here, because that gv is used to store g */
213       ierr = VecCopy(lmP->GV, tao->gradient);CHKERRQ(ierr);
214     }
215 
216     ierr = ComputePseudoGrad_OWLQN(tao->solution,lmP->GV,lmP->lambda);CHKERRQ(ierr);
217 
218     /* Check for termination */
219 
220     ierr = VecNorm(lmP->GV,NORM_2,&gnorm);CHKERRQ(ierr);
221 
222     iter++;
223     ierr = TaoLogConvergenceHistory(tao,f,gnorm,0.0,tao->ksp_its);CHKERRQ(ierr);
224     ierr = TaoMonitor(tao,iter,f,gnorm,0.0,step);CHKERRQ(ierr);
225     ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr);
226 
227     if ((int)ls_status < 0) break;
228   }
229   PetscFunctionReturn(0);
230 }
231 
232 static PetscErrorCode TaoSetUp_OWLQN(Tao tao)
233 {
234   TAO_OWLQN      *lmP = (TAO_OWLQN *)tao->data;
235   PetscInt       n,N;
236   PetscErrorCode ierr;
237 
238   PetscFunctionBegin;
239   /* Existence of tao->solution checked in TaoSetUp() */
240   if (!tao->gradient) {ierr = VecDuplicate(tao->solution,&tao->gradient);CHKERRQ(ierr);  }
241   if (!tao->stepdirection) {ierr = VecDuplicate(tao->solution,&tao->stepdirection);CHKERRQ(ierr);  }
242   if (!lmP->D) {ierr = VecDuplicate(tao->solution,&lmP->D);CHKERRQ(ierr);  }
243   if (!lmP->GV) {ierr = VecDuplicate(tao->solution,&lmP->GV);CHKERRQ(ierr);  }
244   if (!lmP->Xold) {ierr = VecDuplicate(tao->solution,&lmP->Xold);CHKERRQ(ierr);  }
245   if (!lmP->Gold) {ierr = VecDuplicate(tao->solution,&lmP->Gold);CHKERRQ(ierr);  }
246 
247   /* Create matrix for the limited memory approximation */
248   ierr = VecGetLocalSize(tao->solution,&n);CHKERRQ(ierr);
249   ierr = VecGetSize(tao->solution,&N);CHKERRQ(ierr);
250   ierr = MatCreateLMVMBFGS(((PetscObject)tao)->comm,n,N,&lmP->M);CHKERRQ(ierr);
251   ierr = MatLMVMAllocate(lmP->M,tao->solution,tao->gradient);CHKERRQ(ierr);
252   PetscFunctionReturn(0);
253 }
254 
255 /* ---------------------------------------------------------- */
256 static PetscErrorCode TaoDestroy_OWLQN(Tao tao)
257 {
258   TAO_OWLQN      *lmP = (TAO_OWLQN *)tao->data;
259   PetscErrorCode ierr;
260 
261   PetscFunctionBegin;
262   if (tao->setupcalled) {
263     ierr = VecDestroy(&lmP->Xold);CHKERRQ(ierr);
264     ierr = VecDestroy(&lmP->Gold);CHKERRQ(ierr);
265     ierr = VecDestroy(&lmP->D);CHKERRQ(ierr);
266     ierr = MatDestroy(&lmP->M);CHKERRQ(ierr);
267     ierr = VecDestroy(&lmP->GV);CHKERRQ(ierr);
268   }
269   ierr = PetscFree(tao->data);CHKERRQ(ierr);
270   PetscFunctionReturn(0);
271 }
272 
273 /*------------------------------------------------------------*/
274 static PetscErrorCode TaoSetFromOptions_OWLQN(PetscOptionItems *PetscOptionsObject,Tao tao)
275 {
276   TAO_OWLQN      *lmP = (TAO_OWLQN *)tao->data;
277   PetscErrorCode ierr;
278 
279   PetscFunctionBegin;
280   ierr = PetscOptionsHead(PetscOptionsObject,"Orthant-Wise Limited-memory method for Quasi-Newton unconstrained optimization");CHKERRQ(ierr);
281   ierr = PetscOptionsReal("-tao_owlqn_lambda", "regulariser weight","", 100,&lmP->lambda,NULL);CHKERRQ(ierr);
282   ierr = PetscOptionsTail();CHKERRQ(ierr);
283   ierr = TaoLineSearchSetFromOptions(tao->linesearch);CHKERRQ(ierr);
284   PetscFunctionReturn(0);
285 }
286 
287 /*------------------------------------------------------------*/
288 static PetscErrorCode TaoView_OWLQN(Tao tao, PetscViewer viewer)
289 {
290   TAO_OWLQN      *lm = (TAO_OWLQN *)tao->data;
291   PetscBool      isascii;
292   PetscErrorCode ierr;
293 
294   PetscFunctionBegin;
295   ierr = PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii);CHKERRQ(ierr);
296   if (isascii) {
297     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
298     ierr = PetscViewerASCIIPrintf(viewer, "BFGS steps: %D\n", lm->bfgs);CHKERRQ(ierr);
299     ierr = PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %D\n", lm->sgrad);CHKERRQ(ierr);
300     ierr = PetscViewerASCIIPrintf(viewer, "Gradient steps: %D\n", lm->grad);CHKERRQ(ierr);
301     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
302   }
303   PetscFunctionReturn(0);
304 }
305 
306 /* ---------------------------------------------------------- */
307 /*MC
308   TAOOWLQN - orthant-wise limited memory quasi-newton algorithm
309 
310 . - tao_owlqn_lambda - regulariser weight
311 
312   Level: beginner
313 M*/
314 
315 PETSC_EXTERN PetscErrorCode TaoCreate_OWLQN(Tao tao)
316 {
317   TAO_OWLQN      *lmP;
318   const char     *owarmijo_type = TAOLINESEARCHOWARMIJO;
319   PetscErrorCode ierr;
320 
321   PetscFunctionBegin;
322   tao->ops->setup = TaoSetUp_OWLQN;
323   tao->ops->solve = TaoSolve_OWLQN;
324   tao->ops->view = TaoView_OWLQN;
325   tao->ops->setfromoptions = TaoSetFromOptions_OWLQN;
326   tao->ops->destroy = TaoDestroy_OWLQN;
327 
328   ierr = PetscNewLog(tao,&lmP);CHKERRQ(ierr);
329   lmP->D = NULL;
330   lmP->M = NULL;
331   lmP->GV = NULL;
332   lmP->Xold = NULL;
333   lmP->Gold = NULL;
334   lmP->lambda = 1.0;
335 
336   tao->data = (void*)lmP;
337   /* Override default settings (unless already changed) */
338   if (!tao->max_it_changed) tao->max_it = 2000;
339   if (!tao->max_funcs_changed) tao->max_funcs = 4000;
340 
341   ierr = TaoLineSearchCreate(((PetscObject)tao)->comm,&tao->linesearch);CHKERRQ(ierr);
342   ierr = PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1);CHKERRQ(ierr);
343   ierr = TaoLineSearchSetType(tao->linesearch,owarmijo_type);CHKERRQ(ierr);
344   ierr = TaoLineSearchUseTaoRoutines(tao->linesearch,tao);CHKERRQ(ierr);
345   ierr = TaoLineSearchSetOptionsPrefix(tao->linesearch,tao->hdr.prefix);CHKERRQ(ierr);
346   PetscFunctionReturn(0);
347 }
348