vnl_amoeba.cxx
Go to the documentation of this file.
1 // This is core/vnl/algo/vnl_amoeba.cxx
2 //:
3 // \file
4 // \author Andrew W. Fitzgibbon, Oxford RRG
5 // \date 23 Oct 97
6 //-----------------------------------------------------------------------------
7 
8 #include <cstdio>
9 #include <cstdlib>
10 #include <iostream>
11 #include <vector>
12 #include "vnl_amoeba.h"
13 
14 #include <vnl/vnl_math.h>
15 #include <vnl/vnl_cost_function.h>
17 
18 bool vnl_amoeba::default_verbose = false;
19 
21  : fptr(&f), num_evaluations_(0)
22 {
24  maxiter = f.get_number_of_unknowns() * 200;
25  X_tolerance = 1e-8;
26  F_tolerance = 1e-4;
27  relative_diameter = 0.05;
28  zero_term_delta = 0.00025;
29 }
30 
31 
32 struct vnl_amoebaFit : public vnl_amoeba
33 {
34  int cnt;
35 
37  cnt = 0;
38  }
39 
40  //: Initialise the simplex given one corner, x (scale each element to get other corners)
41  void set_up_simplex_relative(std::vector<vnl_amoeba_SimplexCorner>& simplex,
42  const vnl_vector<double>& x);
43 
44  //: Initialise the simplex given one corner, x and displacements of others
45  void set_up_simplex_absolute(std::vector<vnl_amoeba_SimplexCorner>& simplex,
46  const vnl_vector<double>& x,
47  const vnl_vector<double>& dx);
48 
49  //: Perform optimisation. Start simplex defined by scaling elements of x
50  void amoeba(vnl_vector<double>& x);
51 
52  //: Perform optimisation. Start simplex defined by adding dx[i] to each x[i]
53  void amoeba(vnl_vector<double>& x, const vnl_vector<double>& dx);
54 
55  //: Perform optimisation, given simplex to start
56  void amoeba(vnl_vector<double>& x, std::vector<vnl_amoeba_SimplexCorner>& simplex);
57 
58  double f(const vnl_vector<double>& x) {
59  return fptr->f(x);
60  }
61 
63  const vnl_vector<double>& v)
64  {
65  s->v = v;
66  s->fv = f(v);
67  cnt++;
68  }
70  const vnl_vector<double>& vbar,
71  const vnl_vector<double>& v,
72  double lambda)
73  {
74  s->v = (1 - lambda) * vbar + lambda * v;
75  s->fv = f(s->v);
76  cnt++;
77  }
78 };
79 
81  vnl_amoeba_SimplexCorner const& s2)
82 {
83  return vnl_math::sgn(s1.fv - s2.fv);
84 }
85 
86 static
87 int compare_aux(const void * s1, const void * s2)
88 {
90  *(const vnl_amoeba_SimplexCorner*)s2);
91 }
92 
93 static
94 void sort_simplex(std::vector<vnl_amoeba_SimplexCorner>& simplex)
95 {
96  std::qsort(&simplex[0], simplex.size(), sizeof simplex[0], compare_aux);
97 }
98 
99 static
100 double maxabsdiff(const vnl_vector<double>& a, const vnl_vector<double>& b)
101 {
102  double v = 0;
103  for (unsigned i = 0; i < a.size(); ++i) {
104  double ad = vnl_math::abs(a[i] - b[i]);
105  if (ad > v)
106  v = ad;
107  }
108  return v;
109 }
110 
111 static
112 double sorted_simplex_fdiameter(const std::vector<vnl_amoeba_SimplexCorner>& simplex)
113 {
114  return simplex[simplex.size()-1].fv - simplex[0].fv;
115 }
116 
117 static
118 double simplex_diameter(const std::vector<vnl_amoeba_SimplexCorner>& simplex)
119 {
120  double max = 0;
121  for (unsigned i = 0; i < simplex.size() - 1; i++) {
122  double thismax = maxabsdiff(simplex[i].v, simplex[i+1].v);
123  if (thismax > max)
124  max = thismax;
125  }
126  return max;
127 }
128 
130 std::ostream& operator<<(std::ostream& s, const vnl_amoeba_SimplexCorner& simplex)
131 {
132  s << 'S' << simplex.fv << ' ';
133  return s;
134 }
136 std::ostream& operator<<(std::ostream& s, const std::vector<vnl_amoeba_SimplexCorner>& simplex)
137 {
138  for (const auto & i : simplex)
139  s << i.fv << ' ';
140  return s;
141 }
142 
145 {
146  return (&a) == (&b);
147 }
148 
149 //: Initialise the simplex given one corner, x
150 void vnl_amoebaFit::set_up_simplex_relative(std::vector<vnl_amoeba_SimplexCorner>& simplex,
151  const vnl_vector<double>& x)
152 {
153  int n = x.size();
154 
155  simplex[0].v = x;
156  simplex[0].fv = f(x);
157 
158  // Following improvement suggested by L.Pfeffer at Stanford
159  const double usual_delta = relative_diameter; // 5 percent deltas for non-zero terms
160  //const double zero_term_delta = 0.00025; // Even smaller delta for zero elements of x
161 // vnl_vector<double> y(n);
162  for (int j = 0; j < n; ++j) {
163  vnl_amoeba_SimplexCorner *s = &simplex[j+1];
164  s->v = x;
165 
166  // perturb s->v(j)
167  if (vnl_math::abs(s->v[j]) > zero_term_delta)
168  s->v[j] = (1 + usual_delta)*s->v[j];
169  else
170  s->v[j] = zero_term_delta;
171 
172  s->fv = f(s->v);
173  }
174 }
175 
176 //: Initialise the simplex given one corner, x and displacements of others
177 void vnl_amoebaFit::set_up_simplex_absolute(std::vector<vnl_amoeba_SimplexCorner>& simplex,
178  const vnl_vector<double>& x,
179  const vnl_vector<double>& dx)
180 {
181  int n = x.size();
182 
183  simplex[0].v = x;
184  simplex[0].fv = f(x);
185 
186  for (int j = 0; j < n; ++j) {
187  vnl_amoeba_SimplexCorner *s = &simplex[j+1];
188  s->v = x;
189 
190  // perturb s->v(j)
191  s->v[j] = s->v[j] + dx[j];
192 
193  s->fv = f(s->v);
194  }
195 }
196 
197 //: FMINS Minimize a function of several variables.
198 // FMINS('F',X0) attempts to return a vector x which is a local minimizer
199 // of F(x) near the starting vector X0. 'F' is a string containing the
200 // name of the objective function to be minimized. F(x) should be a
201 // scalar valued function of a vector variable.
202 //
203 // FMINS('F',X0,OPTIONS) uses a vector of control parameters.
204 // If OPTIONS(1) is nonzero, intermediate steps in the solution are
205 // displayed; the default is OPTIONS(1) = 0. OPTIONS(2) is the termination
206 // tolerance for x; the default is 1.e-4. OPTIONS(3) is the termination
207 // tolerance for F(x); the default is 1.e-4. OPTIONS(14) is the maximum
208 // number of steps; the default is OPTIONS(14) = 500. The other components
209 // of OPTIONS are not used as input control parameters by FMIN. For more
210 // information, see FOPTIONS.
211 //
212 // FMINS('F',X0,OPTIONS,[],P1,P2,...) provides for up to 10 additional
213 // arguments which are passed to the objective function, F(X,P1,P2,...)
214 //
215 // FMINS uses a simplex search method.
216 //
217 // See also FMIN.
218 //
219 // Reference: J. E. Dennis, Jr. and D. J. Woods, New Computing
220 // Environments: Microcomputers in Large-Scale Computing,
221 // edited by A. Wouk, SIAM, 1987, pp. 116-122.
224 {
225 // Set up a simplex near the initial guess.
226  int n = x.size();
227  std::vector<vnl_amoeba_SimplexCorner> simplex(n+1, vnl_amoeba_SimplexCorner(n));
228 
229  set_up_simplex_relative(simplex,x);
230  amoeba(x,simplex);
231 }
234 {
235 // Set up a simplex near the initial guess.
236  int n = x.size();
237  std::vector<vnl_amoeba_SimplexCorner> simplex(n+1, vnl_amoeba_SimplexCorner(n));
238 
239  set_up_simplex_absolute(simplex,x,dx);
240  amoeba(x,simplex);
241 }
242 
243  //: Perform optimisation, given simplex to start
245  std::vector<vnl_amoeba_SimplexCorner>& simplex)
246 {
247  int n = x.size();
248  sort_simplex(simplex);
249 
250  if (verbose > 1) {
251  std::cerr << "initial\n" << simplex;
252  }
253  else if (verbose) {
254  std::cerr << "initial: " << simplex << '\n';
255  }
256 
257  // Iterate until the diameter of the simplex is less than X_tolerance.
258  vnl_amoeba_SimplexCorner reflect(n);
259  vnl_amoeba_SimplexCorner expand(n);
260  vnl_amoeba_SimplexCorner contract(n);
261  vnl_amoeba_SimplexCorner shrink(n);
263 
264  vnl_vector<double> vbar(n);
265  while (cnt < maxiter) {
266  if (simplex_diameter(simplex) < X_tolerance &&
267  sorted_simplex_fdiameter(simplex) < F_tolerance)
268  break;
269 
270  // One step of the Nelder-Mead simplex algorithm
271  for (int k = 0; k < n; ++k) {
272  vbar[k] = 0;
273  for (int i = 0; i < n; ++i)
274  vbar[k] += simplex[i].v[k];
275  vbar[k] /= n;
276  }
277 
278  set_corner_a_plus_bl(&reflect, vbar, simplex[n].v, -1);
279 
280  next = &reflect;
281  const char *how = "reflect ";
282  if (reflect.fv < simplex[n-1].fv) {
283  // Reflection not totally crap...
284  if (reflect.fv < simplex[0].fv) {
285  // Reflection actually the best, try expanding
286  set_corner_a_plus_bl(&expand, vbar, reflect.v, 2);
287 
288  if (expand.fv < simplex[0].fv) {
289  next = &expand;
290  how = "expand ";
291  }
292  }
293  }
294  else {
295  // Reflection *is* totally crap...
296  {
297  vnl_amoeba_SimplexCorner *tmp = &simplex[n];
298  if (reflect.fv < tmp->fv)
299  // replace simplex[n] by reflection as at least it's better than that
300  tmp = &reflect;
301  set_corner_a_plus_bl(&contract, vbar, tmp->v, 0.5);
302  }
303 
304  if (contract.fv < simplex[0].fv) {
305  // The contraction point was really good, hold it there
306  next = &contract;
307  how = "contract";
308  }
309  else {
310  // The contraction point was only average, shrink the entire simplex.
311  for (int j = 1; j < n; ++j)
312 
313  set_corner_a_plus_bl(&simplex[j], simplex[0].v, simplex[j].v, 0.5);
314  set_corner_a_plus_bl(&shrink, simplex[0].v, simplex[n].v, 0.5);
315 
316  next = &shrink;
317  how = "shrink ";
318  }
319  }
320  simplex[n] = *next;
321 
322  sort_simplex(simplex);
323 
324  // Print debugging info
325  if (verbose) {
326  char buf[16383];
327  std::sprintf(buf, "iter %5d: %s ", cnt, how);
328  std::cerr << buf;
329  if (verbose ==2)
330  std::cerr << "\nFirst corner: " << simplex[0].v;
331  if (verbose > 1)
332  {
333  std::streamsize a = std::cerr.width(10);
334  std::cerr << '\n' << simplex << '\n';
335  std::cerr.width(a);
336  }
337  else if (verbose)
338  std::cerr << simplex << '\n';
339  }
340  }
342  x = simplex[0].v;
343  end_error_ = simplex[0].fv;
344 }
345 
346 //: Modify x to minimise function supplied in constructor
347 // Start simplex defined by scaling elements of x
349 {
350  vnl_amoebaFit af(*this);
351  af.amoeba(x);
353  end_error_ = af.end_error_;
354 }
355 
356 //: Perform optimisation. Start simplex defined by adding dx[i] to each x[i]
358 {
359  vnl_amoebaFit af(*this);
360  af.amoeba(x,dx);
362  end_error_ = af.end_error_;
363 }
364 
365 
366 //: Static method
368 {
369  minimize(f, x, 0);
370 }
371 
372 //: Static method
374 {
375  vnl_amoeba a(f);
377  if (delta != 0)
378  a.relative_diameter = delta;
379  vnl_amoebaFit amoeba(a);
380  amoeba.amoeba(x);
381 }
382 
383 //: Static method
385  const vnl_vector<double>& dx)
386 {
387  vnl_amoeba a(f);
389  vnl_amoebaFit amoeba(a);
390  amoeba.amoeba(x,dx);
391 }
392 
394 class vnl_amoeba_LSCF : public vnl_cost_function
395 {
398  public:
401  ls_(&ls), fx(ls.get_number_of_residuals()) {}
402 
403  ~vnl_amoeba_LSCF() override = default;
405  double f(vnl_vector<double> const& x) override {
406  ls_->f(x, fx);
407  return fx.squared_magnitude();
408  }
409 };
412 {
413  vnl_amoeba_LSCF lsf(f);
414  minimize(lsf, x);
415 }
416 
417 /////////////////////////////////////////////////////////////////////////////
419 
421 
422 //--------------------------------------------------------------------------------
An object that represents a function from R^n -> R.
double f(const vnl_vector< double > &x)
Definition: vnl_amoeba.cxx:57
vnl_vector< double > v
Definition: vnl_amoeba.h:115
vnl_amoeba_SimplexCorner & operator=(const vnl_amoeba_SimplexCorner &that)
static int compare(vnl_amoeba_SimplexCorner const &s1, vnl_amoeba_SimplexCorner const &s2)
Definition: vnl_amoeba.cxx:79
int get_number_of_unknowns() const
Return the number of unknowns.
Nelder-Meade downhill simplex.
Definition: vnl_amoeba.h:43
double F_tolerance
Definition: vnl_amoeba.h:49
Abstract base for minimising functions.
double relative_diameter
Scaling used to select starting vertices relative to initial x0.
Definition: vnl_amoeba.h:67
Vector->Real function.
double end_error_
Definition: vnl_amoeba.h:107
void set_corner(vnl_amoeba_SimplexCorner *s, const vnl_vector< double > &v)
Definition: vnl_amoeba.cxx:61
void set_up_simplex_absolute(std::vector< vnl_amoeba_SimplexCorner > &simplex, const vnl_vector< double > &x, const vnl_vector< double > &dx)
Initialise the simplex given one corner, x and displacements of others.
Definition: vnl_amoeba.cxx:176
size_t size() const
Return the length, number of elements, dimension of this vector.
Definition: vnl_vector.h:126
double f(vnl_vector< double > const &x) override
Apply the function.
Definition: vnl_amoeba.cxx:404
Namespace with standard math functions.
double f(vnl_vector< double > const &x) override
The main function. Given the parameter vector x, compute the value of f(x).
Nelder-Meade downhill simplex.
Abstract base for minimising functions.
abs_t squared_magnitude() const
Return sum of squares of elements.
Definition: vnl_vector.h:279
void set_corner_a_plus_bl(vnl_amoeba_SimplexCorner *s, const vnl_vector< double > &vbar, const vnl_vector< double > &v, double lambda)
Definition: vnl_amoeba.cxx:68
void minimize(vnl_vector< double > &x)
Modify x to minimise function supplied in constructor.
Definition: vnl_amoeba.cxx:347
void amoeba(vnl_vector< double > &x)
Perform optimisation. Start simplex defined by scaling elements of x.
Definition: vnl_amoeba.cxx:222
std::ostream & operator<<(std::ostream &s, vnl_decnum const &r)
decimal output.
Definition: vnl_decnum.h:393
#define v
Definition: vnl_vector.h:42
bool operator==(int r1, vnl_finite_int< N > const &r2)
Definition: vnl_finite.h:385
double zero_term_delta
Definition: vnl_amoeba.h:68
vnl_amoeba(vnl_cost_function &f)
Construct and supply function to be minimized.
Definition: vnl_amoeba.cxx:19
vnl_vector< double > fx
Definition: vnl_amoeba.cxx:396
static bool default_verbose
Definition: vnl_amoeba.h:103
vnl_decnum max(vnl_decnum const &x, vnl_decnum const &y)
Definition: vnl_decnum.h:412
int verbose
Definition: vnl_amoeba.h:46
int maxiter
Definition: vnl_amoeba.h:47
vnl_bignum abs(vnl_bignum const &x)
Definition: vnl_bignum.h:432
vnl_amoeba_LSCF(vnl_least_squares_function &ls)
Definition: vnl_amoeba.cxx:398
vnl_cost_function * fptr
Definition: vnl_amoeba.h:106
~vnl_amoeba_LSCF() override=default
vnl_least_squares_function * ls_
Definition: vnl_amoeba.cxx:395
vnl_amoebaFit(vnl_amoeba &a)
Definition: vnl_amoeba.cxx:35
int sgn(vnl_decnum x)
Definition: vnl_decnum.h:414
int num_evaluations_
Definition: vnl_amoeba.h:108
virtual void f(vnl_vector< double > const &x, vnl_vector< double > &fx)=0
The main function.
double X_tolerance
Definition: vnl_amoeba.h:48
void set_up_simplex_relative(std::vector< vnl_amoeba_SimplexCorner > &simplex, const vnl_vector< double > &x)
Initialise the simplex given one corner, x (scale each element to get other corners).
Definition: vnl_amoeba.cxx:149