BISICLES AMR ice sheet model  0.9
CGOptimize.H
Go to the documentation of this file.
1 #ifdef CH_LANG_CC
2 /*
3 * _______ __
4 * / ___/ / ___ __ _ / / ___
5 * / /__/ _ \/ _ \/ V \/ _ \/ _ \
6 * \___/_//_/\___/_/_/_/_.__/\___/
7 * Please refer to Copyright.txt, in Chombo's root directory.
8 */
9 #endif
10 
11 // SLC, Nov 9, 2011
12 
13 #ifndef _CGOPTIMIZE_H_
14 #define _CGOPTIMIZE_H_
15 
16 
17 #include "parstream.H"
18 #include "REAL.H"
19 #include <cmath>
20 #include "NamespaceHeader.H"
21 #include "CH_assert.H"
22 
24 inline Real sqr(const Real& a)
25 {
26  return a*a;
27 }
28 
29 
31 template <class X>
33 
34 public:
35 
38 
43  virtual Real dotProduct(X& a , const X& b) = 0;
44 
45 
56  virtual void computeObjectiveAndGradient(Real& fm, Real& fp ,X& g, const X& x , bool inner) = 0;
57 
59  virtual void assign(X& y, const X& x) = 0;
60 
62  virtual void incr(X& y, const X& x, Real s) = 0;
63 
65  virtual void create (X &a, const X &b) = 0;
66 
68  virtual void free (X &a) = 0;
69 
71  virtual void scale(X &a, const Real s) = 0;
72 
74  virtual void preCond(X &a, const X &b) = 0;
75 
77  virtual void restart() = 0;
78 
80  virtual int nDoF(const X& x) = 0;
81 
82 };
83 
84 
85 
87 
96 template < class X>
98 (ObjectiveWithGradient<X>& a_F, X& x, X& r, X& d, X& y, X& p, Real a_fx,
99  int a_maxIter, Real a_initialStep, Real a_stepMaxGrow, Real a_tol)
100 {
101 
102  //secant line search
103  Real deltaD = a_F.dotProduct(r,d);
104  Real eta = -deltaD;
105  Real initialStep = a_initialStep;
106  Real alpha = -initialStep;
107  a_F.assign(y,x);
108  a_F.incr(y,d,initialStep);
109  Real fms,fps;
110  a_F.computeObjectiveAndGradient(fms,fps,p,y,true);
111  Real etaPrev = a_F.dotProduct(p,d);
112 
113  pout() << " ... initial secant ||f'(x+sd).f'(x)||^2 = "
114  << -etaPrev << " s = " << initialStep << " f(x+sd) = " << fms << std::endl;
115 
116  while ( (fms + fps < a_fx) && (-etaPrev > -eta))
117  {
118  //we have a problem, because although we have a descent direction in the objective
119  //function f , we have an ascent direction for f'(x+sd).f'(x). For now, just walk along d till
120  // we do have descent in both, then start the secant method offset by alpha + a_initialStep
121  pout() << " d is a descent direction for f(x+sd) but not for f'(x+sd).f'(x) "
122  << " so walking the line (remind SLC to look up a better method) " << std::endl;
123  alpha -= initialStep;
124  a_F.incr(y,d,initialStep);
125  a_F.computeObjectiveAndGradient(fms,fps,p,y,true);
126  eta = etaPrev;
127  etaPrev = a_F.dotProduct(p,d);
128  pout() << " ... initial secant f'(x+ sd).f'(x) = "
129  << -etaPrev << " s = " << alpha << " f(x+ sd) = " << a_fx << std::endl;
130  }
131 
132  while ( (fms + fps > a_fx) && initialStep > 0.015624 * a_initialStep )
133  {
134  // here we have an ascent direction the objective function. Try reducing the step.
135  // this can of course be a happy failure
136  pout() << " d is a not descent direction for f(x+sd), reducing s ";
137  a_F.incr(y,d,-0.5*initialStep);
138  initialStep *= 0.5;
139  alpha = - initialStep;
140  a_F.computeObjectiveAndGradient(fms,fps,p,y,true);
141  eta = etaPrev;
142  etaPrev = a_F.dotProduct(p,d);
143  pout() << " ... initial secant f'(x+ sd).f'(x) = "
144  << -etaPrev << " s = " << initialStep << " f(x+ sd) = " << a_fx << std::endl;
145  }
146 
147  int j = 0;
148  Real q = -(alpha + a_initialStep); // normally alpha = - a_initialStep, but sometimes we needed to walk the line
149  if (q > 0.0)
150  a_F.incr(x,d,q);
151 
152  alpha = -a_initialStep;
153 
154  do {
155 
156  if (j > 0)
157  {
158  a_F.computeObjectiveAndGradient(fms,fps,r,x,true);
159  eta = a_F.dotProduct(r,d);
160  }
161 
162  pout() << " ... secant iteration j = " << j << " f'(x[0]+qd).f'(x[0]) = " << -eta
163  << " a = " << alpha << " q = " << q << std::endl;
164 
165  Real absAlphaPrev = std::abs(alpha);
166  alpha = alpha * eta / (etaPrev-eta);
167 
168  //limit the rate at which alpha grows
169  if ( (a_stepMaxGrow > 0.0) && (std::abs(alpha) > a_stepMaxGrow * absAlphaPrev) )
170  {
171  alpha = ((alpha > 0.0)?(1.0):(-1.0)) * a_stepMaxGrow * absAlphaPrev;
172  }
173 
174  q += alpha;
175  pout() << " ... testing a = " << alpha << " q = " << q << std::endl;
176 
177  a_F.incr(x,d,alpha);
178  etaPrev = eta;
179  j++;
180  } while (j < a_maxIter
181  && sqr(alpha) * deltaD > sqr(a_tol));
182 
183 
184 }
185 
186 
187 
191 template <class X>
193  int a_maxIter,
194  Real a_tol,
195  Real a_hang,
196  Real a_secantParameter,
197  Real a_secantStepMaxGrow,
198  int a_secantMaxIter,
199  Real a_secantTol,
200  int a_iter = 0)
201 {
202 
203  //pout() << "CGOptimize -- a_iter = " << a_iter << endl;
204  //Preconditioned Nonlinear Conjugate Gradients
205  //with Secant line search and Polak-Ribiere updates
206  //from J. R. Shewchuk, 1994
207  //"An Introduction to the Conjugate Gradient Method
208  // Without the Agonizing Pain"
209 
210  X& x = a_x;
211  X r,s,d,y,p;
212  a_F.create(r,x);
213  a_F.create(s,x);
214  a_F.create(d,x);
215  a_F.create(y,x);
216  a_F.create(p,x);
217 
218  Real fm,fp, fmOld;// fpOld; // these are just for retreiving the the misfit norm and penalty norm,
219  // and play no part in the method
220  a_F.computeObjectiveAndGradient(fm,fp,r,x,false);
221  fmOld = fm;
222  //fpOld = fp;
223  a_F.scale(r,-1.0);
224  a_F.preCond(s,r);
225  a_F.assign(d,s);
226  Real deltaNew = a_F.dotProduct(r,s);
227  Real deltaZero = deltaNew;
228  int iter = a_iter; int k = 0;
229 
230  // if we have a_iter > a_maxIter, we have presumably restarted
231  int niter = a_maxIter;
232  if ( (iter > a_maxIter) && (a_maxIter > 0)) niter += (iter/a_maxIter) * a_maxIter;
233 
234  while ( (iter < niter)
235  && (deltaNew > sqr(a_tol) * deltaZero)
236  && (fm > 1.0e-16)
237  && ( (iter - a_iter < 1 ) || (fm/fmOld < a_hang) ) )
238  {
239 
240  pout() << "CGOptimize iteration " << iter
241  << " ||f'(x)||^2 = " << deltaNew
242  << " ||fm(x)||^2 = " << fm
243  << " ||fp(x)||^2 = " << fp
244  << " ||fm(x)||^2 + ||fp(x)||^ = " << fm + fp;
245  if (iter - a_iter > 0)
246  {
247  pout() << " ||fm(x)||^2/||fm_old(x)||^2 = " << fm/fmOld;
248  }
249  pout() << std::endl;
250 
251  fmOld = fm;
252  //fpOld = fp;
253 
254  //find the (approximate) root of f'(y).f'(x)
255  secantLineSearch(a_F, x, r, d, y, p, fm+fp, a_secantMaxIter,
256  a_secantParameter, a_secantStepMaxGrow, a_secantTol);
257 
258  a_F.computeObjectiveAndGradient(fm,fp,r,x,false);
259  a_F.scale(r,-1.0);
260  Real deltaOld = deltaNew;
261  Real deltaMid = a_F.dotProduct(r,s);
262  a_F.preCond(s,r);
263  deltaNew = a_F.dotProduct(r,s);
264  Real beta = (deltaNew - deltaMid)/deltaOld;
265  k++;
266  if (k == a_F.nDoF(x) || beta <= 0.0 )
267  {
268  pout() << "CGOptimize restart k = " << k
269  << " beta = " << beta << std::endl;
270 
271  a_F.assign(d,s);
272  a_F.restart();
273  k = 0;
274  }
275  else
276  {
277  a_F.scale(d,beta);
278  a_F.incr(d,s,1.0);
279  }
280  iter++;
281  }
282 
283  pout() << "CGOptimize iteration " << iter
284  << " ||f'(x)||^2 = " << deltaNew
285  << " ||fm(x)||^2 = " << fm
286  << " ||fp(x)||^2 = " << fp
287  << " ||fm(x)||^2 + ||fp(x)||^ = " << fm + fp
288  << " ||fm(x)||^2/||fm_old(x)||^2 = " << fm/fmOld
289  << std::endl;
290 
291 
292  a_F.free(r);
293  a_F.free(s);
294  a_F.free(d);
295  a_F.free(y);
296  a_F.free(p);
297 
298  return iter;
299 
300 }
301 
302 
303 #include "NamespaceFooter.H"
304 #endif /*_CGOPTIM_H_*/
virtual void restart()=0
carry out whatver operations are required prior to a restart
int CGOptimize(ObjectiveWithGradient< X > &a_F, X &a_x, int a_maxIter, Real a_tol, Real a_hang, Real a_secantParameter, Real a_secantStepMaxGrow, int a_secantMaxIter, Real a_secantTol, int a_iter=0)
Definition: CGOptimize.H:192
virtual ~ObjectiveWithGradient()
virtual destructor
Definition: CGOptimize.H:37
virtual void computeObjectiveAndGradient(Real &fm, Real &fp, X &g, const X &x, bool inner)=0
virtual void preCond(X &a, const X &b)=0
apply precoditioner K, so a = Kb
Specify operations required to carry out gradient based optimization.
Definition: CGOptimize.H:32
virtual void scale(X &a, const Real s)=0
set a = a * s
void secantLineSearch(ObjectiveWithGradient< X > &a_F, X &x, X &r, X &d, X &y, X &p, Real a_fx, int a_maxIter, Real a_initialStep, Real a_stepMaxGrow, Real a_tol)
find y = x + s*d such that f&#39;(y).f&#39;(x) << f&#39;(x).f&#39;(x)
Definition: CGOptimize.H:98
virtual void free(X &a)=0
free storage
Real sqr(const Real &a)
Definition: CGOptimize.H:24
virtual void incr(X &y, const X &x, Real s)=0
Set y = y + s * x.
virtual Real dotProduct(X &a, const X &b)=0
virtual void create(X &a, const X &b)=0
resize a / allocate storage to conform with b
virtual void assign(X &y, const X &x)=0
Copy x to y.
virtual int nDoF(const X &x)=0
number of degrees-of-freedom (maximum number of iterations before restart)