Actual source code: lmvm.c

  1: /*$Id$*/

  3: #include "lmvm.h"

  5: #define LMM_BFGS                0
  6: #define LMM_SCALED_GRADIENT     1
  7: #define LMM_GRADIENT            2

 11: static int TaoSolve_LMVM(TAO_SOLVER tao, void *solver)
 12: {
 13:   TAO_LMVM *lm = (TAO_LMVM *)solver;
 14:   TaoVec *X, *G = lm->G, *D = lm->D, *W = lm->W;
 15:   TaoVec *Xold = lm->Xold, *Gold = lm->Gold;
 16:   TaoLMVMMat *M = lm->M;

 18:   TaoTerminateReason reason;
 19:   TaoTruth success;

 21:   double f, f_full, fold, gdx, gnorm;
 22:   double step = 1.0;

 24:   double delta;

 26:   int stepType;
 27:   int iter = 0, status = 0, info;
 28:   int bfgsUpdates = 0;

 30:   TaoFunctionBegin;

 32:   // Get vectors we will need
 33:   info = TaoGetSolution(tao, &X); CHKERRQ(info);

 35:   // Check convergence criteria
 36:   info = TaoComputeFunctionGradient(tao, X, &f, G); CHKERRQ(info);
 37:   info = G->Norm2(&gnorm); CHKERRQ(info);
 38:   if (TaoInfOrNaN(f) || TaoInfOrNaN(gnorm)) {
 39:     SETERRQ(1, "User provided compute function generated Inf or NaN");
 40:   }

 42:   info = TaoMonitor(tao, iter, f, gnorm, 0.0, step, &reason); CHKERRQ(info);
 43:   if (reason != TAO_CONTINUE_ITERATING) {
 44:     TaoFunctionReturn(0);
 45:   }

 47:   // Set initial scaling for the function
 48:   if (f != 0.0) {
 49:     delta = 2.0 * TaoAbsDouble(f) / (gnorm*gnorm);
 50:   }
 51:   else {
 52:     delta = 2.0 / (gnorm*gnorm);
 53:   }
 54:   info = M->SetDelta(delta); CHKERRQ(info);

 56:   // Set counter for gradient/reset steps
 57:   lm->bfgs = 0;
 58:   lm->sgrad = 0;
 59:   lm->grad = 0;

 61:   // Have not converged; continue with Newton method
 62:   while (reason == TAO_CONTINUE_ITERATING) {
 63:     // Compute direction
 64:     info = M->Update(X, G); CHKERRQ(info);
 65:     info = M->Solve(G, D, &success); CHKERRQ(info);
 66:     ++bfgsUpdates;

 68:     // Check for success (descent direction)
 69:     info = D->Dot(G, &gdx); CHKERRQ(info);
 70:     if ((gdx <= 0.0) || TaoInfOrNaN(gdx)) {
 71:       // Step is not descent or direction produced not a number
 72:       // We can assert bfgsUpdates > 1 in this case because
 73:       // the first solve produces the scaled gradient direction,
 74:       // which is guaranteed to be descent
 75:       //
 76:       // Use steepest descent direction (scaled)
 77:       ++lm->grad;

 79:       if (f != 0.0) {
 80:         delta = 2.0 * TaoAbsDouble(f) / (gnorm*gnorm);
 81:       }
 82:       else {
 83:         delta = 2.0 / (gnorm*gnorm);
 84:       }
 85:       info = M->SetDelta(delta); CHKERRQ(info);
 86:       info = M->Reset(); CHKERRQ(info);
 87:       info = M->Update(X, G); CHKERRQ(info);
 88:       info = M->Solve(G, D, &success); CHKERRQ(info);

 90:       // On a reset, the direction cannot be not a number; it is a 
 91:       // scaled gradient step.  No need to check for this condition.
 92:       // info = D->Norm2(&dnorm); CHKERRQ(info);
 93:       // if (TaoInfOrNaN(dnorm)) {
 94:       //   SETERRQ(1, "Direction generated Not-a-Number");
 95:       // }

 97:       bfgsUpdates = 1;
 98:       ++lm->sgrad;
 99:       stepType = LMM_SCALED_GRADIENT;
100:     }
101:     else {
102:       if (1 == bfgsUpdates) {
103:         // The first BFGS direction is always the scaled gradient
104:         ++lm->sgrad;
105:         stepType = LMM_SCALED_GRADIENT;
106:       }
107:       else {
108:         ++lm->bfgs;
109:         stepType = LMM_BFGS;
110:       }
111:     }
112:     info = D->Negate(); CHKERRQ(info);
113:     
114:     // Perform the linesearch
115:     fold = f;
116:     info = Xold->CopyFrom(X); CHKERRQ(info);
117:     info = Gold->CopyFrom(G); CHKERRQ(info);

119:     step = 1.0;
120:     info = TaoLineSearchApply(tao, X, G, D, W, &f, &f_full, &step, &status); CHKERRQ(info);

122:     while (status && stepType != LMM_GRADIENT) {
123:       // Linesearch failed
124:       // Reset factors and use scaled gradient step
125:       f = fold;
126:       info = X->CopyFrom(Xold); CHKERRQ(info);
127:       info = G->CopyFrom(Gold); CHKERRQ(info);
128:         
129:       switch(stepType) {
130:       case LMM_BFGS:
131:         // Failed to obtain acceptable iterate with BFGS step
132:         // Attempt to use the scaled gradient direction

134:         if (f != 0.0) {
135:           delta = 2.0 * TaoAbsDouble(f) / (gnorm*gnorm);
136:         }
137:         else {
138:           delta = 2.0 / (gnorm*gnorm);
139:         }
140:         info = M->SetDelta(delta); CHKERRQ(info);
141:         info = M->Reset(); CHKERRQ(info);
142:         info = M->Update(X, G); CHKERRQ(info);
143:         info = M->Solve(G, D, &success); CHKERRQ(info);

145:         // On a reset, the direction cannot be not a number; it is a 
146:         // scaled gradient step.  No need to check for this condition.
147:         // info = D->Norm2(&dnorm); CHKERRQ(info);
148:         // if (TaoInfOrNaN(dnorm)) {
149:         //   SETERRQ(1, "Direction generated Not-a-Number");
150:         // }
151:   
152:         bfgsUpdates = 1;
153:         ++lm->sgrad;
154:         stepType = LMM_SCALED_GRADIENT;
155:         break;

157:       case LMM_SCALED_GRADIENT:
158:         // The scaled gradient step did not produce a new iterate;
159:         // attemp to use the gradient direction.
160:         // Need to make sure we are not using a different diagonal scaling
161:         info = M->SetDelta(1.0); CHKERRQ(info);
162:         info = M->Reset(); CHKERRQ(info);
163:         info = M->Update(X, G); CHKERRQ(info);
164:         info = M->Solve(G, D, &success); CHKERRQ(info);

166:         bfgsUpdates = 1;
167:         ++lm->grad;
168:         stepType = LMM_GRADIENT;
169:         break;
170:       }
171:       info = D->Negate(); CHKERRQ(info);
172:         
173:       // Perform the linesearch
174:       step = 1.0;
175:       info = TaoLineSearchApply(tao, X, G, D, W, &f, &f_full, &step, &status); CHKERRQ(info);
176:     }

178:     if (status) {
179:       // Failed to find an improving point
180:       f = fold;
181:       info = X->CopyFrom(Xold); CHKERRQ(info);
182:       info = G->CopyFrom(Gold); CHKERRQ(info);
183:       step = 0.0;
184:     }

186:     // Check for termination
187:     info = G->Norm2(&gnorm); CHKERRQ(info);
188:     if (TaoInfOrNaN(f) || TaoInfOrNaN(gnorm)) {
189:       SETERRQ(1, "User provided compute function generated Inf or NaN");
190:     }
191:     info = TaoMonitor(tao, ++iter, f, gnorm, 0.0, step, &reason); CHKERRQ(info);
192:   }
193:   TaoFunctionReturn(0);
194: }

198: static int TaoSetUp_LMVM(TAO_SOLVER tao, void *solver)
199: {
200:   TAO_LMVM *lm = (TAO_LMVM *)solver;
201:   TaoVec *X;
202:   int info;

204:   TaoFunctionBegin;

206:   info = TaoGetSolution(tao, &X); CHKERRQ(info);
207:   info = X->Clone(&lm->G); CHKERRQ(info);
208:   info = X->Clone(&lm->D); CHKERRQ(info);
209:   info = X->Clone(&lm->W); CHKERRQ(info);

211:   // Create vectors we will need for linesearch
212:   info = X->Clone(&lm->Xold); CHKERRQ(info);
213:   info = X->Clone(&lm->Gold); CHKERRQ(info);

215:   info = TaoSetLagrangianGradientVector(tao, lm->G); CHKERRQ(info);
216:   info = TaoSetStepDirectionVector(tao, lm->D); CHKERRQ(info);

218:   // Create matrix for the limited memory approximation
219:   lm->M = new TaoLMVMMat(X);

221:   info = TaoCheckFG(tao); CHKERRQ(info);
222:   TaoFunctionReturn(0);
223: }

225: /* ---------------------------------------------------------- */
228: static int TaoSetDown_LMVM(TAO_SOLVER tao, void *solver)
229: {
230:   TAO_LMVM *lm = (TAO_LMVM *)solver;
231:   int info;

233:   TaoFunctionBegin;
234:   info = TaoVecDestroy(lm->G); CHKERRQ(info);
235:   info = TaoVecDestroy(lm->D); CHKERRQ(info);
236:   info = TaoVecDestroy(lm->W); CHKERRQ(info);

238:   info = TaoVecDestroy(lm->Xold); CHKERRQ(info);
239:   info = TaoVecDestroy(lm->Gold); CHKERRQ(info);

241:   info = TaoMatDestroy(lm->M); CHKERRQ(info);

243:   info = TaoSetLagrangianGradientVector(tao, 0); CHKERRQ(info);
244:   info = TaoSetStepDirectionVector(tao, 0); CHKERRQ(info);
245:   TaoFunctionReturn(0);
246: }

248: /*------------------------------------------------------------*/
251: static int TaoSetOptions_LMVM(TAO_SOLVER tao, void *solver)
252: {
253:   int info;

255:   TaoFunctionBegin;
256:   info = TaoOptionsHead("Limited-memory variable-metric method for unconstrained optimization"); CHKERRQ(info);
257:   info = TaoLineSearchSetFromOptions(tao); CHKERRQ(info);
258:   info = TaoOptionsTail(); CHKERRQ(info);
259:   TaoFunctionReturn(0);
260: }

262: /*------------------------------------------------------------*/
265: static int TaoView_LMVM(TAO_SOLVER tao, void *solver)
266: {
267:   TAO_LMVM *lm = (TAO_LMVM *)solver;
268:   int info;

270:   TaoFunctionBegin;
271:   info = TaoPrintInt(tao, "  Rejected matrix updates: %d\n", lm->M->GetRejects()); CHKERRQ(info);
272:   info = TaoPrintInt(tao, "  BFGS steps: %d\n", lm->bfgs); CHKERRQ(info);
273:   info = TaoPrintInt(tao, "  Scaled gradient steps: %d\n", lm->sgrad); CHKERRQ(info);
274:   info = TaoPrintInt(tao, "  Gradient steps: %d\n", lm->grad); CHKERRQ(info);
275:   info = TaoLineSearchView(tao); CHKERRQ(info);
276:   TaoFunctionReturn(0);
277: }

279: /* ---------------------------------------------------------- */

284: int TaoCreate_LMVM(TAO_SOLVER tao)
285: {
286:   TAO_LMVM *lm;
287:   int info;

289:   TaoFunctionBegin;

291:   info = TaoNew(TAO_LMVM, &lm); CHKERRQ(info);
292:   info = PetscLogObjectMemory(tao, sizeof(TAO_LMVM)); CHKERRQ(info);

294:   info = TaoSetTaoSolveRoutine(tao, TaoSolve_LMVM, (void *)lm); CHKERRQ(info);
295:   info = TaoSetTaoSetUpDownRoutines(tao, TaoSetUp_LMVM, TaoSetDown_LMVM); CHKERRQ(info);
296:   info = TaoSetTaoOptionsRoutine(tao, TaoSetOptions_LMVM); CHKERRQ(info);
297:   info = TaoSetTaoViewRoutine(tao, TaoView_LMVM); CHKERRQ(info);

299:   info = TaoSetMaximumIterates(tao, 2000); CHKERRQ(info);
300:   info = TaoSetMaximumFunctionEvaluations(tao, 4000); CHKERRQ(info);
301:   info = TaoSetTolerances(tao, 1e-4, 1e-4, 0, 0); CHKERRQ(info);
302:   
303:   info = TaoCreateMoreThuenteLineSearch(tao, 0, 0); CHKERRQ(info);
304:   TaoFunctionReturn(0);
305: }

308: // Todd: do not delete; they are needed for the component version
309: // of the code.

311: /* ---------------------------------------------------------- */
314: int TaoLMVMGetX0(TAO_SOLVER tao, TaoVec *x0)
315: {
316:   TAO_LMVM *lm;
317:   int info;

319:   TaoFunctionBegin;
320:   info=TaoGetSolverContext(tao, "tao_lmvm", (void **)&lm); CHKERRQ(info);
321:   if (lm && lm->M) {
322:     info=lm->M->GetX0(x0); CHKERRQ(info);
323:   }
324:   TaoFunctionReturn(0);
325: }

327: /* ---------------------------------------------------------- */
330: int TaoInitializeLMVMmatrix(TAO_SOLVER tao, TaoVec *HV)
331: {
332:   TAO_LMVM *lm;
333:   int info;
334:   
335:   TaoFunctionBegin;
336:   info = TaoGetSolverContext(tao, "tao_lmvm", (void **)&lm); CHKERRQ(info);
337:   if (lm && lm->M) {
338:     info = lm->M->InitialApproximation(HV); CHKERRQ(info);
339:   }
340:   TaoFunctionReturn(0);
341: }