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: }