1// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2// Copyright (C) 2008-2009 - INRIA - Michael Baudin
3// Copyright (C) 2011 - DIGITEO - Michael Baudin
4//
5// Copyright (C) 2012 - 2016 - Scilab Enterprises
6//
7// This file is hereby licensed under the terms of the GNU GPL v2.0,
8// pursuant to article 5.3.4 of the CeCILL v.2.1.
9// This file was originally licensed under the terms of the CeCILL v2.1,
10// and continues to be available under such terms.
11// For more information, see the COPYING file which you should have received
12// along with this program.
13
14// <-- CLI SHELL MODE -->
15// <-- ENGLISH IMPOSED -->
16
17
18
19
20//
21//  Reference:
22//
23//    An extension of the simplex method to constrained
24//    nonlinear optimization
25//    M.B. Subrahmanyam
26//    Journal of optimization theory and applications
27//    Vol. 62, August 1989
28//
29//    Gould F.J.
30//    Nonlinear Tolerance Programming
31//    Numerical methods for Nonlinear optimization
32//    Edited by F.A. Lootsma, pp 349-366, 1972
33
34//
35// optimtestcase --
36//   Non linear inequality constraints are positive.
37//
38// Arguments
39//   x: the point where to compute the function
40//   index : the stuff to compute
41//
42function [ f , c , index ] = optimtestcase ( x , index )
43  f = []
44  c = []
45  if ( ( index == 2 ) | ( index == 6 ) ) then
46    f = x(1)^2 + x(2)^2 + 2.0 * x(3)^2 + x(4)^2 ...
47      - 5.0 * x(1) - 5.0 * x(2) - 21.0 * x(3) + 7.0 * x(4)
48  end
49  if ( ( index == 5 ) | ( index == 6 ) ) then
50    c1 = - x(1)^2 - x(2)^2 - x(3)^2 - x(4)^2 ...
51              - x(1) + x(2) - x(3) + x(4) + 8
52    c2 = - x(1)^2 - 2.0 * x(2)^2 - x(3)^2 - 2.0 * x(4)^2 ...
53              + x(1) + x(4) + 10.0
54    c3 = - 2.0 * x(1)^2 - x(2)^2 - x(3)^2 - 2.0 * x(1) ...
55              + x(2) + x(4) + 5.0
56    c = [c1 c2 c3]
57  end
58endfunction
59//
60// neldermead_constraints --
61//   The Nelder-Mead algorithm, with variable-size simplex
62//   and modifications for bounds and
63//   inequality constraints.
64//
65function this = neldermead_constraints ( this )
66  // Check settings correspond to algo
67  [ this.optbase , hascons ] = optimbase_hasconstraints ( this.optbase );
68  if ( ~hascons ) then
69      errmsg = msprintf(gettext("%s: Problem has no constraints, but variable algorithm is designed for them."), "neldermead_constraints")
70      error(errmsg)
71  end
72  verbose = optimbase_cget ( this.optbase , "-verbose" )
73  //
74  // Order the vertices for the first time
75  //
76  simplex = this.simplex0;
77  n = optimbase_cget ( this.optbase , "-numberofvariables" );
78  fvinitial = optimbase_get ( this.optbase , "-fx0" );
79  // Sort function values and x points by increasing function value order
80  this = neldermead_log (this,"Step #1 : order");
81  simplex = optimsimplex_sort ( simplex );
82  currentcenter = optimsimplex_center ( simplex );
83  currentxopt = optimbase_cget ( this.optbase , "-x0" );
84  newfvmean = optimsimplex_fvmean ( simplex );
85  nbve = optimsimplex_getnbve ( simplex );
86  ihigh = nbve;
87  inext = ihigh - 1
88  ilow = 1
89  [ this.optbase , hasbounds ] = optimbase_hasbounds ( this.optbase );
90  nbnlc = optimbase_cget ( this.optbase , "-nbineqconst" )
91  //
92  // Initialize
93  //
94  terminate = %f;
95  iter = 0;
96  step = "init";
97  //
98  // Nelder-Mead Loop
99  //
100  while ( ~terminate )
101    this.optbase = optimbase_incriter ( this.optbase );
102    iter = iter + 1;
103    xlow = optimsimplex_getx ( simplex , ilow )
104    flow = optimsimplex_getfv ( simplex , ilow )
105    xhigh = optimsimplex_getx ( simplex , ihigh )
106    fhigh = optimsimplex_getfv ( simplex , ihigh )
107    xn = optimsimplex_getx ( simplex , inext )
108    fn = optimsimplex_getfv ( simplex , inext )
109    //
110    // Store history
111    //
112    xcoords = optimsimplex_getallx ( simplex )
113    this = neldermead_storehistory ( this , n , flow , xlow , xcoords );
114    currentfopt = flow;
115    previousxopt = currentxopt;
116    currentxopt = xlow;
117    previouscenter = currentcenter;
118    currentcenter = optimsimplex_center ( simplex );
119    oldfvmean = newfvmean;
120    newfvmean = optimsimplex_fvmean ( simplex );
121    if ( verbose == 1 ) then
122      deltafv = abs(optimsimplex_deltafvmax ( simplex ));
123      totaliter = optimbase_get ( this.optbase , "-iterations" );
124      funevals = optimbase_get ( this.optbase , "-funevals" );
125      ssize = optimsimplex_size ( simplex )
126      this = neldermead_log (this,sprintf("================================================================="));
127      this = neldermead_log (this,sprintf("Iteration #%d (total = %d)",iter,totaliter));
128      this = neldermead_log (this,sprintf("Function Eval #%d",funevals));
129      this = neldermead_log (this,sprintf("Xopt : [%s]",_strvec(xlow)));
130      this = neldermead_log (this,sprintf("Fopt : %e",flow));
131      this = neldermead_log (this,sprintf("DeltaFv : %e",deltafv));
132      this = neldermead_log (this,sprintf("Center : [%s]",_strvec(currentcenter)));
133      this = neldermead_log (this,sprintf("Size : %e",ssize));
134      str = optimsimplex_tostring ( simplex )
135      for i = 1:nbve
136        this = neldermead_log (this,str(i));
137      end
138    end
139    neldermead_outputcmd ( this, "iter" , simplex , step )
140
141    //
142    // Update termination flag
143    //
144    if ( iter > 1 ) then
145      [ this , terminate , status] = neldermead_termination (this , ...
146        fvinitial , oldfvmean , newfvmean , previouscenter , currentcenter , simplex );
147      if ( terminate ) then
148        if ( verbose == 1 ) then
149          this = neldermead_log (this,sprintf("Terminate with status : %s",status));
150        end
151        break
152      end
153    end
154    //
155    // Compute xbar, center of better vertices
156    //
157    if ( verbose == 1 ) then
158      this = neldermead_log (this,sprintf("Reflect"));
159    end
160    xbar = optimsimplex_xbar ( simplex );
161    if ( verbose == 1 ) then
162      this = neldermead_log (this,sprintf("xbar=[%s]" , _strvec(xbar)));
163    end
164    //
165    // Reflect the worst point with respect to center
166    //
167    xr = neldermead_interpolate ( xbar , xhigh , this.rho );
168    if ( verbose == 1 ) then
169      this = neldermead_log (this,sprintf("xr=[%s]" , _strvec(xr)));
170    end
171    // Adjust point to satisfy bounds and nonlinear inequality constraints
172    if ( hasbounds | nbnlc > 0 ) then
173      [ this , status , xr ] = _scaleinboundsandcons ( this , xr , xbar );
174      if ( ~status ) then
175        status = "impossibleconstr"
176        break
177      end
178    end
179    [ this.optbase , fr , cr , index ] = optimbase_function ( this.optbase , xr , 2 );
180    if ( verbose == 1 ) then
181      this = neldermead_log (this,sprintf("xr=[%s], f(xr)=%f", _strvec(xr) , fr));
182    end
183    if ( fr >= flow & fr < fn ) then
184      if ( verbose == 1 ) then
185        this = neldermead_log (this,sprintf("  > Perform reflection"));
186      end
187      simplex = optimsimplex_setve ( simplex , ihigh , fr , xr )
188      step = "reflection";
189    elseif ( fr < flow ) then
190      // Expand
191      if ( verbose == 1 ) then
192        this = neldermead_log (this,sprintf("Expand"));
193      end
194      xe = neldermead_interpolate ( xbar , xhigh , this.rho*this.chi );
195      // Adjust point to satisfy bounds and nonlinear inequality constraints
196      if ( hasbounds | nbnlc > 0 ) then
197        [ this , status , xe ] = _scaleinboundsandcons ( this , xe , xbar );
198        if ( ~status ) then
199          status = "impossibleconstr"
200          break
201        end
202      end
203      [ this.optbase , fe , ce , index ] = optimbase_function ( this.optbase , xe , 2 );
204      if ( verbose == 1 ) then
205        this = neldermead_log (this,sprintf("xe=[%s], f(xe)=%f", strcat(string(xe)," ") , fe ));
206      end
207      if (fe < fr) then
208        if ( verbose == 1 ) then
209          this = neldermead_log (this,sprintf("  > Perform Expansion"));
210        end
211        simplex = optimsimplex_setve ( simplex , ihigh , fe , xe )
212        step = "expansion";
213      else
214        if ( verbose == 1 ) then
215          this = neldermead_log (this,sprintf("  > Perform reflection"));
216        end
217        simplex = optimsimplex_setve ( simplex , ihigh , fr , xr )
218        step = "reflection";
219      end
220    elseif ( fr >= fn & fr < fhigh ) then
221      // Outside contraction
222      if ( verbose == 1 ) then
223        this = neldermead_log (this,sprintf("Contract - outside"));
224      end
225      xc = neldermead_interpolate ( xbar , xhigh , this.rho*this.gamma );
226      // Adjust point to satisfy bounds and nonlinear inequality constraints
227      if ( hasbounds | nbnlc > 0 ) then
228        [ this , status , xc ] = _scaleinboundsandcons ( this , xc , xbar );
229        if ( ~status ) then
230          status = "impossibleconstr"
231          break
232        end
233      end
234      [ this.optbase , fc , cc , index ] = optimbase_function ( this.optbase , xc , 2 );
235      if ( verbose == 1 ) then
236        this = neldermead_log (this,sprintf("xc=[%s], f(xc)=%f", strcat(string(xc)," ") , fc));
237      end
238      if ( fc <= fr ) then
239        if ( verbose == 1 ) then
240          this = neldermead_log (this,sprintf("  > Perform Outside Contraction"));
241        end
242        simplex = optimsimplex_setve ( simplex , ihigh , fc , xc )
243        step = "outsidecontraction";
244      else
245        //  Shrink
246        if ( verbose == 1 ) then
247          this = neldermead_log (this,sprintf("  > Perform Shrink"));
248        end
249        [ simplex , this ] = optimsimplex_shrink ( simplex , neldermead_costf , this.sigma , this );
250        step = "shrink";
251      end
252    else
253      // ( fr >= fn & fr >= fhigh )
254      // Inside contraction
255      if ( verbose == 1 ) then
256        this = neldermead_log (this,sprintf("Contract - inside"));
257      end
258      xc = neldermead_interpolate ( xbar , xhigh , -this.gamma );
259      // Adjust point to satisfy bounds and nonlinear inequality constraints
260      if ( hasbounds | nbnlc > 0 ) then
261        [ this , status , xc ] = _scaleinboundsandcons ( this , xc , xbar );
262        if ( ~status ) then
263          status = "impossibleconstr"
264          break
265        end
266      end
267      [ this.optbase , fc , cc , index ] = optimbase_function ( this.optbase , xc , 2 );
268      if ( verbose == 1 ) then
269        this = neldermead_log (this,sprintf("xc=[%s], f(xc)=%f", strcat(string(xc)," ") , fc));
270      end
271      if ( fc < fhigh ) then
272        if ( verbose == 1 ) then
273          this = neldermead_log (this,sprintf("  > Perform Inside Contraction"));
274        end
275        simplex = optimsimplex_setve ( simplex , ihigh , fc , xc )
276        step = "insidecontraction";
277      else
278        //  Shrink
279        if ( verbose == 1 ) then
280          this = neldermead_log (this,sprintf("  > Perform Shrink"));
281        end
282        [ simplex , this ] = optimsimplex_shrink ( simplex , neldermead_costf , this.sigma , this )
283        step = "shrink";
284      end
285    end
286    //
287    // Sort simplex
288    //
289    if ( verbose == 1 ) then
290      this = neldermead_log (this,sprintf("Sort"));
291    end
292    simplex  = optimsimplex_sort ( simplex );
293  end
294  this.optbase = optimbase_set ( this.optbase , "-xopt" , xlow.' );
295  this.optbase = optimbase_set ( this.optbase , "-fopt" , flow );
296  this.optbase = optimbase_set ( this.optbase , "-status" , status );
297  this.simplexopt = simplex;
298endfunction
299  //
300  // _scaleinboundsandcons --
301  //   Given a point to scale and a reference point which satisfies the constraints,
302  //   scale the point towards the reference point until it satisfies all the constraints,
303  //   including boun constraints.
304  //   Returns isscaled = %T if the procedure has succeded before -boxnbnlloops
305  //   Returns isscaled = %F if the procedure has failed after -boxnbnlloops
306  //   iterations.
307  // Arguments
308  //   x : the point to scale
309  //   xref : the reference point
310  //   isscaled : %T or %F
311  //   p : scaled point
312  //
313function [ this , isscaled , p ] = _scaleinboundsandcons ( this , x , xref )
314  p = x
315  [ this.optbase , hasbounds ] = optimbase_hasbounds ( this.optbase );
316  nbnlc = optimbase_cget ( this.optbase , "-nbineqconst" )
317  //
318  // 1. No bounds, no nonlinear inequality constraints
319  // => no problem
320  //
321  if ( ( hasbounds == %f ) & ( nbnlc == 0 ) ) then
322    isscaled = %T
323    return;
324  end
325  isscaled = %F
326  //
327  // 2. Scale into bounds
328  //
329  if ( hasbounds ) then
330    [ this.optbase , p ] = optimbase_proj2bnds ( this.optbase ,  p );
331    this = neldermead_log (this,sprintf(" > After projection into bounds p = [%s]" , ...
332      _strvec(p)));
333  end
334  //
335  // 2. Scale into nonlinear constraints
336  // Try the current point and see if the constraints are satisfied.
337  // If not, move the point "halfway" to the centroid,
338  // which should satisfy the constraints, if
339  // the constraints are convex.
340  // Perform this loop until the constraints are satisfied.
341  // If all loops have been performed without success, the scaling
342  // has failed.
343  //
344  alpha = 1.0
345  p0 = p
346  while ( alpha > this.guinalphamin )
347      [ this.optbase , feasible ] = optimbase_isinnonlincons ( this.optbase , p );
348      if ( feasible ) then
349        isscaled = %T;
350        break;
351      end
352      alpha = alpha / 2.0
353      this = neldermead_log (this,sprintf("Scaling inequality constraint with alpha = %e", ...
354        alpha ));
355      p = ( 1.0 - alpha ) * xref + alpha * p0;
356  end
357  this = neldermead_log (this,sprintf(" > After scaling into inequality constraints p = [%s]" , ...
358    _strvec(p) ) );
359  if ( ~isscaled ) then
360    this = neldermead_log (this,sprintf(" > Impossible to scale into constraints." ));
361  end
362endfunction
363
364//
365// Test with my own algorithm,
366// the "Mega Super Ultra Modified Simplex Method" !!!
367//
368nm = neldermead_new ();
369nm = neldermead_configure(nm,"-numberofvariables",4);
370nm = neldermead_configure(nm,"-function",optimtestcase);
371nm = neldermead_configure(nm,"-x0",[0.0 0.0 0.0 0.0]');
372nm = neldermead_configure(nm,"-maxiter",200);
373nm = neldermead_configure(nm,"-maxfunevals",400);
374nm = neldermead_configure(nm,"-tolsimplexizerelative",1.e-3);
375nm = neldermead_configure(nm,"-simplex0method","axes");
376nm = neldermead_configure(nm,"-nbineqconst",3);
377nm = neldermead_configure(nm,"-method","mine");
378nm = neldermead_configure(nm,"-mymethod",neldermead_constraints);
379nm = neldermead_search(nm);
380// Check optimum point
381xopt = neldermead_get(nm,"-xopt");
382assert_checkalmostequal ( xopt , [0.0 1.0 2.0 -1.0]', 1e-3, 1e-3 );
383// Check optimum point value
384fopt = neldermead_get(nm,"-fopt");
385assert_checkalmostequal ( fopt , -44.0 , 1e-5 );
386// Check status
387status = neldermead_get(nm,"-status");
388assert_checkequal ( status , "tolsize" );
389nm = neldermead_destroy(nm);
390
391