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