OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
LMDif.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: LMDif.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.1.1.1 $
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: LMDif
10 // This class encapsulates a minimisation according to the method used
11 // by Levenberg and Marquardt.
12 //
13 // ------------------------------------------------------------------------
14 //
15 // $Date: 2000/03/27 09:33:44 $
16 // $Author: Andreas Adelmann $
17 //
18 // ------------------------------------------------------------------------
19 
20 #include "Match/LMDif.h"
21 #include "Algebra/Matrix.h"
22 #include "Algebra/QRSolver.h"
23 #include "Algebra/Vector.h"
24 #include "Attributes/Attributes.h"
25 #include "Match/Match.h"
26 #include "Utilities/Round.h"
27 #include <algorithm>
28 #include <cmath>
29 #include <cfloat>
30 
31 using std::max;
32 using std::min;
33 
34 
35 // Class LMDif
36 // ------------------------------------------------------------------------
37 
38 // The attributes of class LMDif.
39 namespace {
40  enum {
41  TOLERANCE, // The desired tolerance.
42  CALLS, // The maximum number of calls to the matching functions.
43  SIZE
44  };
45 }
46 
47 
49  Action(SIZE, "LMDIF",
50  "The \"LMDIF\" sub-command adjusts parameters according to the "
51  "Levenberg-Marquardt method.") {
52  itsAttr[TOLERANCE] = Attributes::makeReal
53  ("TOLERANCE", "The desired tolerance", 1.0e-6);
55  ("CALLS", "Maximum number of calls to the matching functions", 1000.);
56 
58 }
59 
60 
61 LMDif::LMDif(const std::string &name, LMDif *parent):
62  Action(name, parent) {
63  fraction = 1.0e-6;
64  delta = 10.0;
65 }
66 
67 
69 {}
70 
71 
72 LMDif *LMDif::clone(const std::string &name) {
73  return new LMDif(name, this);
74 }
75 
76 
78  // Built-in constants.
79  static const char method[] = "LMDIF";
80 
81  // Fetch command attributes.
82  double tol = max(DBL_EPSILON, Attributes::getReal(itsAttr[TOLERANCE]));
83  int nfcnmax = int(Round(Attributes::getReal(itsAttr[CALLS])));
84 
85  // Initialize.
86  double fnorm = 0.0;
87  MatchState state = START;
88 
89  // Working storage.
90  Vector<double> x, f;
92  int m = Match::block->countFunctions();
93  int n = Match::block->countVariables();
95 
96  // Initial function evaluation.
98  if(! Match::block->evaluate(x, f)) {
99  state = FAILED;
100  } else if((fnorm = euclidean_norm(f)) <= tol) {
101  state = CONVERGED;
102  } else {
103  // Initialize iterations.
104  mu = 0.0;
105  double dxnorm = 0.0;
106  double ratio = 1.0;
107  int iter = 1;
108  state = START;
109 
110  // Outer iteration.
111  do {
112  // Initial print-out.
113  Match::block->print(method, state);
114 
115  // Recalculate the Jacobian matrix.
116  if(! findJacobian(x, f)) {
117  state = FAILED;
118  break;
119  }
120 
121  // Compute the QR factorization of the Jacobian.
122  // Pivoting is enforced.
123  QRSolver solver(jacobian, f, true);
124  solver.getColNorm(D);
125 
126  // On the first iteration calculate the norm of the scaled x
127  // and initialize the step bound delta.
128  if(iter == 1) {
129  for(int j = 0; j < D.size(); ++j) if(D(j) == 0.0) D(j) = 1.0;
130  double dxnorm = scaled_norm(D, x);
131  delta = dxnorm ? dxnorm : 1.0;
132  }
133 
134  // Inner iteration.
135  do {
136  // Inner iteration: Apply Levenberg-Marquardt parameter.
137  state = INTERNAL;
138  Vector<double> h;
139  lmpar(solver, D, f, h);
140  double dhnorm = scaled_norm(D, h);
141 
142  // On the first iteration, adjust the initial step bound.
143  if(iter == 1) delta = min(delta, dhnorm);
144 
145  // Evaluate the function at x + h and calculate its norm.
146  Vector<double> f1;
147  double fnorm1;
148 
149  if(Match::block->evaluate(x - h, f1)) {
150  fnorm1 = euclidean_norm(f1);
151  } else {
152  fnorm1 = 2.0 * fnorm;
153  }
154 
155  // Compute the scaled actual reduction.
156  double actred = - 1.0;
157  if(0.1 * fnorm1 < fnorm) {
158  actred = 1.0 - (fnorm1 * fnorm1) / (fnorm * fnorm);
159  }
160 
161  // Compute the scaled predicted reduction
162  // and the scaled directional derivative.
163  double gnorm = euclidean_norm(jacobian * h);
164  double temp1 = gnorm / fnorm;
165  double temp2 = sqrt(mu) * dhnorm / fnorm;
166  double prered = temp1 * temp1 + 2.0 * temp2 * temp2;
167  double dirder = - (temp1 * temp1 + temp2 * temp2);
168 
169  // Compute the ratio of the actual to the predicted reduction.
170  ratio = prered ? actred / prered : 0.0;
171 
172  // Update the step bound.
173  if(ratio <= 0.25) {
174  double temp = (actred >= 0.0) ? 0.5 : dirder / (2.0 * dirder + actred);
175  if(0.1 * fnorm1 >= fnorm || temp < 0.1) temp = 0.1;
176  delta = temp * min(delta, 10.0 * dhnorm);
177  mu /= temp;
178  } else if(mu == 0.0 || ratio >= 0.75) {
179  delta = 2.0 * dhnorm;
180  mu = 0.5 * mu;
181  }
182 
183  // Successive tests for successful iteration,
184  // for convergence, and for abnormal termination.
185  if(ratio >= 0.0001) {
186  x = x - h;
187  dxnorm = scaled_norm(D, x);
188  f = f1;
189  fnorm = fnorm1;
190  iter++;
191  state = PROGRESS;
192  }
193 
194  if((std::abs(actred) <= tol && prered <= tol && ratio <= 2.0) ||
195  (delta <= tol * dxnorm) ||
196  (fnorm *fnorm <= tol)) {
197  state = CONVERGED;
198  } else if(Match::block->getCallCount() >= nfcnmax) {
199  state = CALL_LIMIT;
200  } else if((std::abs(actred) <= DBL_EPSILON && prered <= DBL_EPSILON &&
201  ratio <= 2.0) ||
202  (delta <= DBL_EPSILON * dxnorm) ||
203  (fnorm *fnorm <= DBL_EPSILON) ||
204  (gnorm <= DBL_EPSILON * fnorm)) {
205  state = ACCURACY_LIMIT;
206  }
207  } while(state < CONVERGED && ratio < 0.0001);
208  } while(state < CONVERGED);
209  }
210 
211  // Final evaluation and printout.
212  Match::block->evaluate(x, f);
213  Match::block->print(method, state);
214 }
215 
216 
217 void LMDif::lmpar(QRSolver &solver, const Array1D<double> &D,
218  const Vector<double> &f, Vector<double> &h) {
219  // The iteration limit.
220  static const int limit = 10;
221 
222  // Evaluate and test for acceptance.
223  solver.solveR(h);
224  double dhnorm = scaled_norm(D, h);
225  double lm = dhnorm - delta;
226 
227  // Accept the estimate, if the function is small enough.
228  if(lm <= 0.1 * delta) {
229  mu = 0.0;
230  return;
231  }
232 
233  // Start iterations.
234  int n = D.size();
235  for(int iter = 0; iter < limit; ++iter) {
236  if(mu <= 0.0) mu = max(DBL_EPSILON, 0.001 * delta);
237  solver.solveS(D, mu, h);
238  dhnorm = scaled_norm(D, h);
239  lm = dhnorm - delta;
240 
241  // If the goal function lm is negative or small enough, return.
242  if(lm <= 0.1 * delta) break;
243 
244  // Compute new parameter mu.
245  Vector<double> temp(n);
246  for(int i = 0; i < n; ++i) temp[i] = D[i] * D[i] * h[i] / dhnorm;
247 
248  solver.solveST(temp);
249  double dmu = lm / (delta * (temp * temp));
250  mu += dmu;
251 
252  if(mu < 0.0) {
253  mu = 0.0;
254  break;
255  } else if(std::abs(dmu) < 1.0e-6 * mu) {
256  break;
257  }
258  }
259 }
260 
261 
263  Vector<double> temp_x(x);
264  Vector<double> temp_f(f);
265  int n = x.size();
266  int m = f.size();
267  bool flag = true;
268 
269  for(int j = 0; j < n; ++j) {
270  double step = (temp_x(j) == 0) ? fraction : fraction * std::abs(temp_x(j));
271  temp_x(j) += step;
272  flag = Match::block->evaluate(temp_x, temp_f);
273  temp_x(j) = x(j);
274 
275  if(! flag) break;
276  for(int i = 0; i < m; ++i) {
277  jacobian(i, j) = (temp_f(i) - f(i)) / step;
278  }
279  }
280 
281  return flag;
282 }
double Round(double value)
Round the double argument.
Definition: Round.cpp:23
Matrix< double > jacobian
Definition: LMDif.h:90
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
constexpr double e
The value of .
Definition: Physics.h:40
MatchState
The possible states of a matching process.
Definition: MatchState.h:29
The LMDIF command.
Definition: LMDif.h:45
The base class for all OPAL actions.
Definition: Action.h:30
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:123
static Match * block
The block of match data.
Definition: Match.h:100
int countFunctions() const
Return total number of functions.
Definition: Match.cpp:140
std::vector< Attribute > itsAttr
The object attributes (see Attribute.hh).
Definition: Object.h:214
void getColNorm(Array1D< double > &) const
Return the original column norms of the matrix $A$.
Definition: QRSolver.cpp:174
void solveST(Vector< double > &V) const
Pre-multiply the vector $V$ by $S.transpose()^{-1}$.
Definition: QRSolver.cpp:169
int size() const
Get array size.
Definition: Array1D.h:228
void print(const char *method, MatchState state)
Print the results of minimisation.
Definition: Match.cpp:145
int countVariables() const
Get total number of variables.
Definition: Match.cpp:106
Least-square solution of systems of linear equations.
Definition: QRSolver.h:60
void solveR(Vector< double > &X) const
Solution of $A*X = B$ in the least-squares sense.
Definition: QRSolver.cpp:148
double mu
Definition: LMDif.h:87
double fraction
Definition: LMDif.h:81
void registerOwnership(const AttributeHandler::OwnerType &itsClass) const
Definition: Object.cpp:194
bool evaluate(const Vector< double > &x, Vector< double > &f)
Evaluate the matching functions.
Definition: Match.cpp:117
LMDif()
Exemplar constructor.
Definition: LMDif.cpp:48
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
virtual void execute()
Execute the command.
Definition: LMDif.cpp:77
virtual LMDif * clone(const std::string &name)
Make clone.
Definition: LMDif.cpp:72
bool findJacobian(Vector< double > &X, Vector< double > &F)
Definition: LMDif.cpp:262
double delta
Definition: LMDif.h:84
T euclidean_norm(const Vector< T > &)
Euclidean norm.
Definition: Vector.h:243
const std::string name
virtual ~LMDif()
Definition: LMDif.cpp:68
T scaled_norm(const Array1D< T > D, const Vector< T > &V)
Euclidean norm of diagonal matrix D times vector V.
Definition: Vector.h:248
void lmpar(QRSolver &solver, const Array1D< double > &D, const Vector< double > &F, Vector< double > &P)
Definition: LMDif.cpp:217
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:217
void solveS(const Array1D< double > &D, double p, Vector< double > &X)
Solution of $A*X = B, D*X = 0$ in the least-squares sense.
Definition: QRSolver.cpp:153
void getVariables(Vector< double > &x) const
Get values of matching variables.
Definition: Match.cpp:82
Attribute makeReal(const std::string &name, const std::string &help)
Make real attribute.
Definition: Attributes.cpp:205
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:95