/************************************************************************* Clears request fileds (to be sure that we don't forget to clear something) *************************************************************************/ private static void clearrequestfields(minnsstate state) { state.needfi = false; state.needfij = false; state.xupdated = false; }
/************************************************************************* Buffered implementation of minnsresults() which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresultsbuf(minnsstate state, ref double[] x, minnsreport rep) { int i = 0; int i_ = 0; if( alglib.ap.len(x)<state.n ) { x = new double[state.n]; } rep.iterationscount = state.repinneriterationscount; rep.nfev = state.repnfev; rep.varidx = state.repvaridx; rep.funcidx = state.repfuncidx; rep.terminationtype = state.repterminationtype; rep.cerr = Math.Max(state.replcerr, state.repnlcerr); rep.lcerr = state.replcerr; rep.nlcerr = state.repnlcerr; if( state.repterminationtype>0 ) { for(i_=0; i_<=state.n-1;i_++) { x[i_] = state.xc[i_]; } } else { for(i=0; i<=state.n-1; i++) { x[i] = Double.NaN; } } }
/************************************************************************* This subroutine restarts algorithm from new point. All optimization parameters (including constraints) are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. X - new starting point. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrestartfrom(minnsstate state, double[] x) { int n = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSRestartFrom: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNSRestartFrom: X contains infinite or NaN values!"); // // Set XC // for(i_=0; i_<=n-1;i_++) { state.xstart[i_] = x[i_]; } // // prepare RComm facilities // state.rstate.ia = new int[4+1]; state.rstate.ra = new double[2+1]; state.rstate.stage = -1; clearrequestfields(state); }
/************************************************************************* NOTES: 1. This function has two different implementations: one which uses exact (analytical) user-supplied Jacobian, and one which uses only function vector and numerically differentiates function in order to obtain gradient. Depending on the specific function used to create optimizer object you should choose appropriate variant of minnsoptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of minnsoptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to minnsoptimize() and specific function used to create optimizer. | USER PASSED TO minnsoptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ minnscreatef() | works FAILS minnscreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and minnsoptimize() version. Attemps to use such combination will lead to exception. Either you did not pass gradient when it WAS needed or you passed gradient when it was NOT needed. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static bool minnsiteration(minnsstate state) { bool result = new bool(); int i = 0; int k = 0; int n = 0; int ng = 0; int nh = 0; double v = 0; double xp = 0; double xm = 0; int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { i = state.rstate.ia[0]; k = state.rstate.ia[1]; n = state.rstate.ia[2]; ng = state.rstate.ia[3]; nh = state.rstate.ia[4]; v = state.rstate.ra[0]; xp = state.rstate.ra[1]; xm = state.rstate.ra[2]; } else { i = -983; k = -989; n = -834; ng = 900; nh = -287; v = 364; xp = 214; xm = -338; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } // // Routine body // // // Init // state.replcerr = 0.0; state.repnlcerr = 0.0; state.repterminationtype = 0; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repnfev = 0; state.repvaridx = 0; state.repfuncidx = 0; state.userterminationneeded = false; state.dbgncholesky = 0; n = state.n; ng = state.ng; nh = state.nh; clearrequestfields(state); // // AGS solver // if( state.solvertype!=0 ) { goto lbl_4; } if( (double)(state.diffstep)!=(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.xbase, n); apserv.rvectorsetlengthatleast(ref state.fm, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fp, 1+ng+nh); } state.rstateags.ia = new int[13+1]; state.rstateags.ba = new bool[3+1]; state.rstateags.ra = new double[9+1]; state.rstateags.stage = -1; lbl_6: if( !agsiteration(state) ) { goto lbl_7; } // // Numerical differentiation (if needed) - intercept NeedFiJ // request and replace it by sequence of NeedFi requests // if( !((double)(state.diffstep)!=(double)(0) && state.needfij) ) { goto lbl_8; } state.needfij = false; state.needfi = true; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.x[i_]; } k = 0; lbl_10: if( k>n-1 ) { goto lbl_12; } v = state.xbase[k]; xm = v-state.diffstep*state.s[k]; xp = v+state.diffstep*state.s[k]; if( state.hasbndl[k] && (double)(xm)<(double)(state.bndl[k]) ) { xm = state.bndl[k]; } if( state.hasbndu[k] && (double)(xp)>(double)(state.bndu[k]) ) { xp = state.bndu[k]; } alglib.ap.assert((double)(xm)<=(double)(xp), "MinNS: integrity check failed"); if( (double)(xm)==(double)(xp) ) { goto lbl_13; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = xm; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: for(i_=0; i_<=ng+nh;i_++) { state.fm[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = xp; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: for(i_=0; i_<=ng+nh;i_++) { state.fp[i_] = state.fi[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = state.fp[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = state.j[i_,k] - state.fm[i_]; } v = 1/(xp-xm); for(i_=0; i_<=ng+nh;i_++) { state.j[i_,k] = v*state.j[i_,k]; } state.repnfev = state.repnfev+2; goto lbl_14; lbl_13: for(i=0; i<=ng+nh; i++) { state.j[i,k] = 0.0; } lbl_14: k = k+1; goto lbl_10; lbl_12: for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 2; goto lbl_rcomm; lbl_2: // // Restore previous values of fields and continue // state.needfi = false; state.needfij = true; goto lbl_6; lbl_8: // // Forward request to caller // state.rstate.stage = 3; goto lbl_rcomm; lbl_3: apserv.inc(ref state.repnfev); goto lbl_6; lbl_7: result = false; return result; lbl_4: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = i; state.rstate.ia[1] = k; state.rstate.ia[2] = n; state.rstate.ia[3] = ng; state.rstate.ia[4] = nh; state.rstate.ra[0] = v; state.rstate.ra[1] = xp; state.rstate.ra[2] = xm; return result; }
/************************************************************************* MinNS results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report. You should check Rep.TerminationType in order to distinguish successful termination from unsuccessful one: * -8 internal integrity control detected infinite or NAN values in function/gradient. Abnormal termination signalled. * -3 box constraints are inconsistent * -1 inconsistent parameters were passed: * penalty parameter for minnssetalgoags() is zero, but we have nonlinear constraints set by minnssetnlc() * 2 sampling radius decreased below epsx * 7 stopping conditions are too stringent, further improvement is impossible, X contains best point found so far. * 8 User requested termination via minnsrequesttermination() -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsresults(minnsstate state, ref double[] x, minnsreport rep) { x = new double[0]; minnsresultsbuf(state, ref x, rep); }
/************************************************************************* This function calculates merit function (target function + penalties for violation of non-box constraints), using State.X (unscaled), State.Fi, State.J (unscaled) and State.SampleX (scaled) as inputs. Results are loaded: * target function value - to State.SampleF0[SampleIdx] * merit function value - to State.SampleF[SampleIdx] * gradient of merit function - to State.SampleGM[SampleIdx] -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void generatemeritfunction(minnsstate state, int sampleidx) { int n = 0; int i = 0; int j = 0; int nec = 0; int nic = 0; int ng = 0; int nh = 0; double v = 0; double s = 0; n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Integrity check // for(i=0; i<=n-1; i++) { alglib.ap.assert(!state.hasbndl[i] || (double)(state.x[i])>=(double)(state.bndl[i]), "MinNS: integrity error"); alglib.ap.assert(!state.hasbndu[i] || (double)(state.x[i])<=(double)(state.bndu[i]), "MinNS: integrity error"); } // // Prepare "raw" function // state.samplef[sampleidx] = state.fi[0]; state.samplef0[sampleidx] = state.fi[0]; for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.j[0,j]*state.s[j]; } // // Modify merit function with linear constraints // for(i=0; i<=nec+nic-1; i++) { v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.samplex[sampleidx,j]; } if( i>=nec && (double)(v)<(double)(0) ) { continue; } state.samplef[sampleidx] = state.samplef[sampleidx]+state.rholinear[i]*Math.Abs(v); s = Math.Sign(v); for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.samplegm[sampleidx,j]+state.rholinear[i]*s*state.scaledcleic[i,j]; } } // // Modify merit function with nonlinear constraints // for(i=1; i<=ng+nh; i++) { v = state.fi[i]; if( i<=ng && (double)(v)==(double)(0) ) { continue; } if( i>ng && (double)(v)<=(double)(0) ) { continue; } state.samplef[sampleidx] = state.samplef[sampleidx]+state.agsrhononlinear*Math.Abs(v); s = Math.Sign(v); for(j=0; j<=n-1; j++) { state.samplegm[sampleidx,j] = state.samplegm[sampleidx,j]+state.agsrhononlinear*s*state.j[i,j]*state.s[j]; } } }
/************************************************************************* NONSMOOTH NONCONVEX OPTIMIZATION SUBJECT TO BOX/LINEAR/NONLINEAR-NONSMOOTH CONSTRAINTS DESCRIPTION: The subroutine minimizes function F(x) of N arguments subject to any combination of: * bound constraints * linear inequality constraints * linear equality constraints * nonlinear equality constraints Gi(x)=0 * nonlinear inequality constraints Hi(x)<=0 IMPORTANT: see MinNSSetAlgoAGS for important information on performance restrictions of AGS solver. REQUIREMENTS: * starting point X0 must be feasible or not too far away from the feasible set * F(), G(), H() are continuous, locally Lipschitz and continuously (but not necessarily twice) differentiable in an open dense subset of R^N. Functions F(), G() and H() may be nonsmooth and non-convex. Informally speaking, it means that functions are composed of large differentiable "patches" with nonsmoothness having place only at the boundaries between these "patches". Most real-life nonsmooth functions satisfy these requirements. Say, anything which involves finite number of abs(), min() and max() is very likely to pass the test. Say, it is possible to optimize anything of the following: * f=abs(x0)+2*abs(x1) * f=max(x0,x1) * f=sin(max(x0,x1)+abs(x2)) * for nonlinearly constrained problems: F() must be bounded from below without nonlinear constraints (this requirement is due to the fact that, contrary to box and linear constraints, nonlinear ones require special handling). * user must provide function value and gradient for F(), H(), G() at all points where function/gradient can be calculated. If optimizer requires value exactly at the boundary between "patches" (say, at x=0 for f=abs(x)), where gradient is not defined, user may resolve tie arbitrarily (in our case - return +1 or -1 at its discretion). * NS solver supports numerical differentiation, i.e. it may differentiate your function for you, but it results in 2N increase of function evaluations. Not recommended unless you solve really small problems. See minnscreatef() for more information on this functionality. USAGE: 1. User initializes algorithm state with MinNSCreate() call and chooses what NLC solver to use. There is some solver which is used by default, with default settings, but you should NOT rely on default choice. It may change in future releases of ALGLIB without notice, and no one can guarantee that new solver will be able to solve your problem with default settings. From the other side, if you choose solver explicitly, you can be pretty sure that it will work with new ALGLIB releases. In the current release following solvers can be used: * AGS solver (activated with MinNSSetAlgoAGS() function) 2. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) MinNSSetBC() for boundary constraints b) MinNSSetLC() for linear constraints c) MinNSSetNLC() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 3. User sets scale of the variables with MinNSSetScale() function. It is VERY important to set scale of the variables, because nonlinearly constrained problems are hard to solve when variables are badly scaled. 4. User sets stopping conditions with MinNSSetCond(). 5. Finally, user calls MinNSOptimize() function which takes algorithm state and pointer (delegate, etc) to callback function which calculates F/G/H. 7. User calls MinNSResults() to get solution 8. Optionally user may call MinNSRestartFrom() to solve another problem with same N but another starting point. MinNSRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. OUTPUT PARAMETERS: State - structure stores algorithm state NOTE: minnscreatef() function may be used if you do not have analytic gradient. This function creates solver which uses numerical differentiation with user-specified step. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreate(int n, double[] x, minnsstate state) { alglib.ap.assert(n>=1, "MinNSCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSCreate: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNSCreate: X contains infinite or NaN values"); minnsinitinternal(n, x, 0.0, state); }
/************************************************************************* This function sets scaling coefficients for NLC optimizer. ALGLIB optimizers use scaling matrices to test stopping conditions (step size and gradient are scaled before comparison with tolerances). Scale of the I-th variable is a translation invariant measure of: a) "how large" the variable is b) how large the step should be to make significant changes in the function Scaling is also used by finite difference variant of the optimizer - step along I-th axis is equal to DiffStep*S[I]. INPUT PARAMETERS: State - structure stores algorithm state S - array[N], non-zero scaling coefficients S[i] may be negative, sign doesn't matter. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetscale(minnsstate state, double[] s) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinNSSetScale: Length(S)<N"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(s[i]), "MinNSSetScale: S contains infinite or NAN elements"); alglib.ap.assert((double)(s[i])!=(double)(0), "MinNSSetScale: S contains zero elements"); state.s[i] = Math.Abs(s[i]); } }
/************************************************************************* This function tells MinNS unit to use AGS (adaptive gradient sampling) algorithm for nonsmooth constrained optimization. This algorithm is a slight modification of one described in "An Adaptive Gradient Sampling Algorithm for Nonsmooth Optimization" by Frank E. Curtisy and Xiaocun Quez. This optimizer has following benefits and drawbacks: + robustness; it can be used with nonsmooth and nonconvex functions. + relatively easy tuning; most of the metaparameters are easy to select. - it has convergence of steepest descent, slower than CG/LBFGS. - each iteration involves evaluation of ~2N gradient values and solution of 2Nx2N quadratic programming problem, which limits applicability of algorithm by small-scale problems (up to 50-100). IMPORTANT: this algorithm has convergence guarantees, i.e. it will steadily move towards some stationary point of the function. However, "stationary point" does not always mean "solution". Nonsmooth problems often have "flat spots", i.e. areas where function do not change at all. Such "flat spots" are stationary points by definition, and algorithm may be caught here. Nonsmooth CONVEX tasks are not prone to this problem. Say, if your function has form f()=MAX(f0,f1,...), and f_i are convex, then f() is convex too and you have guaranteed convergence to solution. INPUT PARAMETERS: State - structure which stores algorithm state Radius - initial sampling radius, >=0. Internally multiplied by vector of per-variable scales specified by minnssetscale()). You should select relatively large sampling radius, roughly proportional to scaled length of the first steps of the algorithm. Something close to 0.1 in magnitude should be good for most problems. AGS solver can automatically decrease radius, so too large radius is not a problem (assuming that you won't choose so large radius that algorithm will sample function in too far away points, where gradient value is irrelevant). Too small radius won't cause algorithm to fail, but it may slow down algorithm (it may have to perform too short steps). Penalty - penalty coefficient for nonlinear constraints: * for problem with nonlinear constraints should be some problem-specific positive value, large enough that penalty term changes shape of the function. Starting from some problem-specific value penalty coefficient becomes large enough to exactly enforce nonlinear constraints; larger values do not improve precision. Increasing it too much may slow down convergence, so you should choose it carefully. * can be zero for problems WITHOUT nonlinear constraints (i.e. for unconstrained ones or ones with just box or linear constraints) * if you specify zero value for problem with at least one nonlinear constraint, algorithm will terminate with error code -1. ALGORITHM OUTLINE The very basic outline of unconstrained AGS algorithm is given below: 0. If sampling radius is below EpsX or we performed more then MaxIts iterations - STOP. 1. sample O(N) gradient values at random locations around current point; informally speaking, this sample is an implicit piecewise linear model of the function, although algorithm formulation does not mention that explicitly 2. solve quadratic programming problem in order to find descent direction 3. if QP solver tells us that we are near solution, decrease sampling radius and move to (0) 4. perform backtracking line search 5. after moving to new point, goto (0) As for the constraints: * box constraints are handled exactly by modification of the function being minimized * linear/nonlinear constraints are handled by adding L1 penalty. Because our solver can handle nonsmoothness, we can use L1 penalty function, which is an exact one (i.e. exact solution is returned under such penalty). * penalty coefficient for linear constraints is chosen automatically; however, penalty coefficient for nonlinear constraints must be specified by user. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetalgoags(minnsstate state, double radius, double penalty) { alglib.ap.assert(math.isfinite(radius), "MinNSSetAlgoAGS: Radius is not finite"); alglib.ap.assert((double)(radius)>(double)(0), "MinNSSetAlgoAGS: Radius<=0"); alglib.ap.assert(math.isfinite(penalty), "MinNSSetAlgoAGS: Penalty is not finite"); alglib.ap.assert((double)(penalty)>=(double)(0.0), "MinNSSetAlgoAGS: Penalty<0"); state.agsrhononlinear = penalty; state.agsradius = radius; state.solvertype = 0; }
/************************************************************************* This function sets nonlinear constraints. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to minnsoptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] correspond to function being minimized * next NLEC components of F[] (and rows of J) correspond to nonlinear equality constraints G_i(x)=0 * next NLIC components of F[] (and rows of J) correspond to nonlinear inequality constraints H_i(x)<=0 NOTE: you may combine nonlinear constraints with linear/boundary ones. If your problem has mixed constraints, you may explicitly specify some of them as linear ones. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of the feasible area! NOTE 2: algorithm scales variables according to scale specified by minnssetscale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, there is no way to automatically scale nonlinear constraints Gi(x) and Hi(x). Inappropriate scaling of Gi/Hi may ruin convergence. Solving problem with constraint "1000*G0(x)=0" is NOT same as solving it with constraint "0.001*G0(x)=0". It means that YOU are the one who is responsible for correct scaling of nonlinear constraints Gi(x) and Hi(x). We recommend you to scale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has approximately unit magnitude (for problems with unit scale) or has magnitude approximately equal to 1/S[i] (where S is a scale set by minnssetscale() function). NOTE 3: nonlinear constraints are always hard to handle, no matter what algorithm you try to use. Even basic box/linear constraints modify function curvature by adding valleys and ridges. However, nonlinear constraints add valleys which are very hard to follow due to their "curved" nature. It means that optimization with single nonlinear constraint may be significantly slower than optimization with multiple linear ones. It is normal situation, and we recommend you to carefully choose Rho parameter of minnssetalgoags(), because too large value may slow down convergence. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetnlc(minnsstate state, int nlec, int nlic) { alglib.ap.assert(nlec>=0, "MinNSSetNLC: NLEC<0"); alglib.ap.assert(nlic>=0, "MinNSSetNLC: NLIC<0"); state.ng = nlec; state.nh = nlic; state.fi = new double[1+state.ng+state.nh]; state.j = new double[1+state.ng+state.nh, state.n]; }
/************************************************************************* This function sets stopping conditions for iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsX - >=0 The AGS solver finishes its work if on k+1-th iteration sampling radius decreases below EpsX. MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. We do not recommend you to rely on default choice in production code. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetcond(minnsstate state, double epsx, int maxits) { alglib.ap.assert(math.isfinite(epsx), "MinNSSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinNSSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinNSSetCond: negative MaxIts!"); if( (double)(epsx)==(double)(0) && maxits==0 ) { epsx = 1.0E-6; } state.epsx = epsx; state.maxits = maxits; }
/************************************************************************* This function sets linear constraints. Linear constraints are inactive by default (after initial creation). They are preserved after algorithm restart with minnsrestartfrom(). INPUT PARAMETERS: State - structure previously allocated with minnscreate() call. C - linear constraints, array[K,N+1]. Each row of C represents one constraint, either equality or inequality (see below): * first N elements correspond to coefficients, * last element corresponds to the right part. All elements of C (including right part) must be finite. CT - type of constraints, array[K]: * if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1] * if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1] * if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1] K - number of equality/inequality constraints, K>=0: * if given, only leading K elements of C/CT are used * if not given, automatically determined from sizes of C/CT NOTE: linear (non-bound) constraints are satisfied only approximately: * there always exists some minor violation (about current sampling radius in magnitude during optimization, about EpsX in the solution) due to use of penalty method to handle constraints. * numerical differentiation, if used, may lead to function evaluations outside of the feasible area, because algorithm does NOT change numerical differentiation formula according to linear constraints. If you want constraints to be satisfied exactly, try to reformulate your problem in such manner that all constraints will become boundary ones (this kind of constraints is always satisfied exactly, both in the final solution and in all intermediate points). -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetlc(minnsstate state, double[,] c, int[] ct, int k) { int n = 0; int i = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinNSSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinNSSetLC: Cols(C)<N+1"); alglib.ap.assert(alglib.ap.rows(c)>=k, "MinNSSetLC: Rows(C)<K"); alglib.ap.assert(alglib.ap.len(ct)>=k, "MinNSSetLC: Length(CT)<K"); alglib.ap.assert(apserv.apservisfinitematrix(c, k, n+1), "MinNSSetLC: C contains infinite or NaN values!"); // // Handle zero K // if( k==0 ) { state.nec = 0; state.nic = 0; return; } // // Equality constraints are stored first, in the upper // NEC rows of State.CLEIC matrix. Inequality constraints // are stored in the next NIC rows. // // NOTE: we convert inequality constraints to the form // A*x<=b before copying them. // apserv.rmatrixsetlengthatleast(ref state.cleic, k, n+1); state.nec = 0; state.nic = 0; for(i=0; i<=k-1; i++) { if( ct[i]==0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec,i_] = c[i,i_]; } state.nec = state.nec+1; } } for(i=0; i<=k-1; i++) { if( ct[i]!=0 ) { if( ct[i]>0 ) { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = -c[i,i_]; } } else { for(i_=0; i_<=n;i_++) { state.cleic[state.nec+state.nic,i_] = c[i,i_]; } } state.nic = state.nic+1; } } }
/************************************************************************* This function sets boundary constraints. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with minnsrestartfrom(). INPUT PARAMETERS: State - structure stores algorithm state BndL - lower bounds, array[N]. If some (all) variables are unbounded, you may specify very small number or -INF. BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF. NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. NOTE 2: AGS solver has following useful properties: * bound constraints are always satisfied exactly * function is evaluated only INSIDE area specified by bound constraints, even when numerical differentiation is used (algorithm adjusts nodes according to boundary constraints) -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnssetbc(minnsstate state, double[] bndl, double[] bndu) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinNSSetBC: Length(BndL)<N"); alglib.ap.assert(alglib.ap.len(bndu)>=n, "MinNSSetBC: Length(BndU)<N"); for(i=0; i<=n-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "MinNSSetBC: BndL contains NAN or +INF"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "MinNSSetBC: BndL contains NAN or -INF"); state.bndl[i] = bndl[i]; state.hasbndl[i] = math.isfinite(bndl[i]); state.bndu[i] = bndu[i]; state.hasbndu[i] = math.isfinite(bndu[i]); } }
/************************************************************************* Version of minnscreatef() which uses numerical differentiation. I.e., you do not have to calculate derivatives yourself. However, this version needs 2N times more function evaluations. 2-point differentiation formula is used, because more precise 4-point formula is unstable when used on non-smooth functions. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size of X X - starting point, array[N]: * it is better to set X to a feasible point * but X can be infeasible, in which case algorithm will try to find feasible point first, using X as initial approximation. DiffStep- differentiation step, DiffStep>0. Algorithm performs numerical differentiation with step for I-th variable being equal to DiffStep*S[I] (here S[] is a scale vector, set by minnssetscale() function). Do not use too small steps, because it may lead to catastrophic cancellation during intermediate calculations. OUTPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnscreatef(int n, double[] x, double diffstep, minnsstate state) { alglib.ap.assert(n>=1, "MinNSCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNSCreateF: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNSCreateF: X contains infinite or NaN values"); alglib.ap.assert(math.isfinite(diffstep), "MinNSCreateF: DiffStep is infinite or NaN!"); alglib.ap.assert((double)(diffstep)>(double)(0), "MinNSCreateF: DiffStep is non-positive!"); minnsinitinternal(n, x, diffstep, state); }
/************************************************************************* Internal initialization subroutine. Sets default NLC solver with default criteria. *************************************************************************/ private static void minnsinitinternal(int n, double[] x, double diffstep, minnsstate state) { int i = 0; double[,] c = new double[0,0]; int[] ct = new int[0]; state.agsinitstp = 0.2; state.agsstattold = 1.0E-10; state.agsshortstpabs = 1.0E-10; state.agsshortstprel = 0.75; state.agsshortf = 10*math.machineepsilon; state.agsrhononlinear = 0.0; state.agsraddecay = 0.2; state.agsalphadecay = 0.5; state.agsdecrease = 0.1; state.agsmaxraddecays = 50; state.agsmaxbacktrack = 20; state.agsmaxbacktracknonfull = 8; state.agspenaltylevel = 10.0; state.agspenaltyincrease = 20.0; state.agsminupdate = Math.Max(5, n/2); state.agssamplesize = Math.Max(2*n+1, state.agsminupdate+1); state.agsshortlimit = 4+state.agssamplesize/state.agsminupdate; // // Initialize other params // state.n = n; state.diffstep = diffstep; state.bndl = new double[n]; state.hasbndl = new bool[n]; state.bndu = new double[n]; state.hasbndu = new bool[n]; state.s = new double[n]; state.xstart = new double[n]; state.xc = new double[n]; state.xn = new double[n]; state.d = new double[n]; state.x = new double[n]; for(i=0; i<=n-1; i++) { state.bndl[i] = Double.NegativeInfinity; state.hasbndl[i] = false; state.bndu[i] = Double.PositiveInfinity; state.hasbndu[i] = false; state.s[i] = 1.0; state.xstart[i] = x[i]; state.xc[i] = x[i]; } minnssetlc(state, c, ct, 0); minnssetnlc(state, 0, 0); minnssetcond(state, 0.0, 0); minnssetxrep(state, false); minnssetalgoags(state, 0.1, 1000.0); minnsrestartfrom(state, x); }
/************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to minnsoptimize(). -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnssetxrep(minnsstate state, bool needxrep) { state.xrep = needxrep; }
/************************************************************************* This function performs actual processing for AUL algorith. It expects that caller redirects its reverse communication requests NeedFiJ/XUpdated to external user who will provide analytic derivative (or handle reports about progress). In case external user does not have analytic derivative, it is responsibility of caller to intercept NeedFiJ request and replace it with appropriate numerical differentiation scheme. -- ALGLIB -- Copyright 06.06.2015 by Bochkanov Sergey *************************************************************************/ private static bool agsiteration(minnsstate state) { bool result = new bool(); int n = 0; int nec = 0; int nic = 0; int ng = 0; int nh = 0; int i = 0; int j = 0; int k = 0; double radius0 = 0; double radius = 0; int radiusdecays = 0; double alpha = 0; double recommendedstep = 0; double dnrm = 0; double dg = 0; double v = 0; double vv = 0; int maxsamplesize = 0; int cursamplesize = 0; double v0 = 0; double v1 = 0; bool restartneeded = new bool(); bool b = new bool(); bool alphadecreased = new bool(); int shortstepscnt = 0; int backtrackits = 0; int maxbacktrackits = 0; bool fullsample = new bool(); int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstateags.stage>=0 ) { n = state.rstateags.ia[0]; nec = state.rstateags.ia[1]; nic = state.rstateags.ia[2]; ng = state.rstateags.ia[3]; nh = state.rstateags.ia[4]; i = state.rstateags.ia[5]; j = state.rstateags.ia[6]; k = state.rstateags.ia[7]; radiusdecays = state.rstateags.ia[8]; maxsamplesize = state.rstateags.ia[9]; cursamplesize = state.rstateags.ia[10]; shortstepscnt = state.rstateags.ia[11]; backtrackits = state.rstateags.ia[12]; maxbacktrackits = state.rstateags.ia[13]; restartneeded = state.rstateags.ba[0]; b = state.rstateags.ba[1]; alphadecreased = state.rstateags.ba[2]; fullsample = state.rstateags.ba[3]; radius0 = state.rstateags.ra[0]; radius = state.rstateags.ra[1]; alpha = state.rstateags.ra[2]; recommendedstep = state.rstateags.ra[3]; dnrm = state.rstateags.ra[4]; dg = state.rstateags.ra[5]; v = state.rstateags.ra[6]; vv = state.rstateags.ra[7]; v0 = state.rstateags.ra[8]; v1 = state.rstateags.ra[9]; } else { n = -686; nec = 912; nic = 585; ng = 497; nh = -271; i = -581; j = 745; k = -533; radiusdecays = -77; maxsamplesize = 678; cursamplesize = -293; shortstepscnt = 316; backtrackits = 647; maxbacktrackits = -756; restartneeded = false; b = true; alphadecreased = false; fullsample = false; radius0 = 13; radius = -740; alpha = 262; recommendedstep = 439; dnrm = 327; dg = 222; v = -589; vv = 274; v0 = 845; v1 = 456; } if( state.rstateags.stage==0 ) { goto lbl_0; } if( state.rstateags.stage==1 ) { goto lbl_1; } if( state.rstateags.stage==2 ) { goto lbl_2; } if( state.rstateags.stage==3 ) { goto lbl_3; } // // Routine body // alglib.ap.assert(state.solvertype==0, "MinNS: internal error"); n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Check consistency of parameters // if( ng+nh>0 && (double)(state.agsrhononlinear)==(double)(0) ) { state.repterminationtype = -1; result = false; return result; } // // Allocate arrays. // apserv.rvectorsetlengthatleast(ref state.colmax, n); apserv.rvectorsetlengthatleast(ref state.diagh, n); apserv.rvectorsetlengthatleast(ref state.signmin, n); apserv.rvectorsetlengthatleast(ref state.signmax, n); maxsamplesize = state.agssamplesize; apserv.rmatrixsetlengthatleast(ref state.samplex, maxsamplesize+1, n); apserv.rmatrixsetlengthatleast(ref state.samplegm, maxsamplesize+1, n); apserv.rmatrixsetlengthatleast(ref state.samplegmbc, maxsamplesize+1, n); apserv.rvectorsetlengthatleast(ref state.samplef, maxsamplesize+1); apserv.rvectorsetlengthatleast(ref state.samplef0, maxsamplesize+1); apserv.rvectorsetlengthatleast(ref state.grs, n); // // Prepare optimizer // apserv.rvectorsetlengthatleast(ref state.tmp0, maxsamplesize); apserv.rvectorsetlengthatleast(ref state.tmp1, maxsamplesize); apserv.ivectorsetlengthatleast(ref state.tmp3, 1); apserv.rmatrixsetlengthatleast(ref state.tmp2, 1, maxsamplesize+1); for(i=0; i<=maxsamplesize-1; i++) { state.tmp0[i] = 0.0; state.tmp1[i] = Double.PositiveInfinity; } // // Prepare RNG, seed it with fixed values so // that each run on same problem yeilds same results // hqrnd.hqrndseed(7235, 98532, state.agsrs); // // Prepare initial point subject to current bound constraints and // perform scaling of bound constraints, linear constraints, point itself // apserv.rvectorsetlengthatleast(ref state.scaledbndl, n); apserv.rvectorsetlengthatleast(ref state.scaledbndu, n); for(i=0; i<=n-1; i++) { // // Check and scale constraints // if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndu[i])<(double)(state.bndl[i]) ) { state.repterminationtype = -3; result = false; return result; } if( state.hasbndl[i] ) { state.scaledbndl[i] = state.bndl[i]/state.s[i]; } else { state.scaledbndl[i] = Double.NegativeInfinity; } if( state.hasbndu[i] ) { state.scaledbndu[i] = state.bndu[i]/state.s[i]; } else { state.scaledbndu[i] = Double.PositiveInfinity; } if( state.hasbndl[i] && state.hasbndu[i] ) { alglib.ap.assert((double)(state.scaledbndl[i])<=(double)(state.scaledbndu[i]), "MinNS: integrity check failed"); } if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { alglib.ap.assert((double)(state.scaledbndl[i])==(double)(state.scaledbndu[i]), "MinNS: integrity check failed"); } // // Scale and constrain point // state.xc[i] = state.xstart[i]; if( state.hasbndl[i] && (double)(state.xc[i])<=(double)(state.bndl[i]) ) { state.xc[i] = state.scaledbndl[i]; continue; } if( state.hasbndu[i] && (double)(state.xc[i])>=(double)(state.bndu[i]) ) { state.xc[i] = state.scaledbndu[i]; continue; } state.xc[i] = state.xc[i]/state.s[i]; if( state.hasbndl[i] && (double)(state.xc[i])<=(double)(state.scaledbndl[i]) ) { state.xc[i] = state.scaledbndl[i]; } if( state.hasbndu[i] && (double)(state.xc[i])>=(double)(state.scaledbndu[i]) ) { state.xc[i] = state.scaledbndu[i]; } } apserv.rmatrixsetlengthatleast(ref state.scaledcleic, nec+nic, n+1); apserv.rvectorsetlengthatleast(ref state.rholinear, nec+nic); for(i=0; i<=nec+nic-1; i++) { // // Initial value of penalty coefficient is zero // state.rholinear[i] = 0.0; // // Scale and normalize linear constraints // vv = 0.0; for(j=0; j<=n-1; j++) { v = state.cleic[i,j]*state.s[j]; state.scaledcleic[i,j] = v; vv = vv+v*v; } vv = Math.Sqrt(vv); state.scaledcleic[i,n] = state.cleic[i,n]; if( (double)(vv)>(double)(0) ) { for(j=0; j<=n; j++) { state.scaledcleic[i,j] = state.scaledcleic[i,j]/vv; } } } // // Main cycle // // We maintain several variables during iteration: // * RecommendedStep- current estimate of recommended step length; // must be Radius0 on first entry // * Radius - current sampling radius // * CurSampleSize - current sample size (may change in future versions) // * FullSample - whether we have full sample, or only partial one // * RadiusDecays - total number of decreases performed for sampling radius // radius = state.agsradius; radius0 = radius; recommendedstep = Math.Min(radius0, state.agsinitstp); cursamplesize = 1; radiusdecays = 0; shortstepscnt = 0; fullsample = false; lbl_4: if( false ) { goto lbl_5; } // // First phase of iteration - central point: // // 1. evaluate function at central point - first entry in sample. // Its status is ignored, it is always recalculated. // 2. report point and check gradient/function value for NAN/INF // 3. check penalty coefficients for linear terms; increase them // if directional derivative of function being optimized (not // merit function!) is larger than derivative of penalty. // 4. update report on constraint violation // cursamplesize = Math.Max(cursamplesize, 1); for(i_=0; i_<=n-1;i_++) { state.samplex[0,i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } unscalepointbc(state, state.x); clearrequestfields(state); state.needfij = true; state.rstateags.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; state.replcerr = 0.0; for(i=0; i<=nec+nic-1; i++) { v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.xc[j]; } if( i>=nec && (double)(v)<=(double)(0) ) { continue; } state.replcerr = Math.Max(state.replcerr, Math.Abs(v)); } state.repnlcerr = 0.0; for(i=1; i<=ng+nh; i++) { v = state.fi[i]; if( i>ng && (double)(v)<=(double)(0) ) { continue; } state.repnlcerr = Math.Max(state.repnlcerr, Math.Abs(v)); } for(j=0; j<=n-1; j++) { state.grs[j] = state.j[0,j]*state.s[j]; } generatemeritfunction(state, 0); if( !state.xrep ) { goto lbl_6; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xc[i_]; } state.f = state.samplef0[0]; unscalepointbc(state, state.x); clearrequestfields(state); state.xupdated = true; state.rstateags.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_6: if( state.userterminationneeded ) { // // User requested termination // state.repterminationtype = 8; goto lbl_5; } v = 0; for(i=0; i<=n-1; i++) { v = v+math.sqr(state.samplegm[0,i]); } if( !math.isfinite(v) || !math.isfinite(state.samplef[0]) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; goto lbl_5; } restartneeded = false; for(i=0; i<=nec+nic-1; i++) { // // Evaluate penalty function. // // Skip update if penalty is satisfied exactly (this check // also covers situations when I-th row is exactly zero). // v = -state.scaledcleic[i,n]; for(j=0; j<=n-1; j++) { v = v+state.scaledcleic[i,j]*state.xc[j]; } if( i<nec && (double)(v)==(double)(0) ) { continue; } if( i>=nec && (double)(v)<=(double)(0) ) { continue; } // // Calculate directional derivative, compare it with threshold. // // NOTE: we rely on the fact that ScaledCLEIC is normalized // alglib.ap.assert((double)(state.agspenaltylevel)>(double)(1.0), "MinNS: integrity error"); alglib.ap.assert((double)(state.agspenaltyincrease)>(double)(state.agspenaltylevel), "MinNS: integrity error"); v = 0.0; for(j=0; j<=n-1; j++) { v = v+state.grs[j]*state.scaledcleic[i,j]; } v = Math.Abs(v); if( (double)(v*state.agspenaltylevel)>(double)(state.rholinear[i]) ) { state.rholinear[i] = v*state.agspenaltyincrease; restartneeded = true; } } if( restartneeded ) { cursamplesize = 0; goto lbl_4; } // // Check stopping conditions. // if( radiusdecays>=state.agsmaxraddecays ) { // // Too many attempts to decrease radius // state.repterminationtype = 7; goto lbl_5; } if( state.repinneriterationscount>=state.maxits && state.maxits>0 ) { // // Too many iterations // state.repterminationtype = 5; goto lbl_5; } if( (double)(radius)<=(double)(state.epsx*state.agsraddecay) ) { // // Radius is smaller than required step tolerance multiplied by radius decay. // // Additional decay is required in order to make sure that optimization session // with radius equal to EpsX was successfully done. // state.repterminationtype = 2; goto lbl_5; } // // Update sample: // // 1. invalidate entries which are too far away from XC // and move all valid entries to beginning of the sample. // 2. add new entries until we have AGSSampleSize // items in our sample. We remove oldest entries from // sample until we have enough place to add at least // AGSMinUpdate items. // 3. prepare "modified" gradient sample with respect to // boundary constraints. // alglib.ap.assert(cursamplesize>=1, "MinNS: integrity check failed"); k = 1; for(i=1; i<=cursamplesize-1; i++) { // // If entry is outside of Radius-ball around XC, discard it. // v = 0.0; for(j=0; j<=n-1; j++) { v = Math.Max(v, Math.Abs(state.samplex[i,j]-state.xc[j])); } if( (double)(v)>(double)(radius) ) { continue; } // // If central point is exactly at boundary, and corresponding // component of entry is OUT of boundary, entry is discarded. // b = false; for(j=0; j<=n-1; j++) { b = b || ((state.hasbndl[j] && (double)(state.xc[j])==(double)(state.scaledbndl[j])) && (double)(state.samplex[i,j])!=(double)(state.scaledbndl[j])); b = b || ((state.hasbndu[j] && (double)(state.xc[j])==(double)(state.scaledbndu[j])) && (double)(state.samplex[i,j])!=(double)(state.scaledbndu[j])); } if( b ) { continue; } // // Move to the beginning // for(i_=0; i_<=n-1;i_++) { state.samplex[k,i_] = state.samplex[i,i_]; } for(i_=0; i_<=n-1;i_++) { state.samplegm[k,i_] = state.samplegm[i,i_]; } state.samplef[k] = state.samplef[i]; state.samplef0[k] = state.samplef0[i]; k = k+1; } cursamplesize = k; if( state.agssamplesize-cursamplesize<state.agsminupdate ) { // // Remove oldest entries // k = state.agsminupdate-(state.agssamplesize-cursamplesize); alglib.ap.assert(k<=cursamplesize-1, "MinNS: integrity check failed"); for(i=1; i<=cursamplesize-k-1; i++) { for(i_=0; i_<=n-1;i_++) { state.samplex[i,i_] = state.samplex[i+k,i_]; } for(i_=0; i_<=n-1;i_++) { state.samplegm[i,i_] = state.samplegm[i+k,i_]; } state.samplef[i] = state.samplef[i+k]; state.samplef0[i] = state.samplef0[i+k]; } cursamplesize = cursamplesize-k; } k = 0; i = cursamplesize; lbl_8: if( i>Math.Min(cursamplesize+state.agsminupdate, state.agssamplesize)-1 ) { goto lbl_10; } for(j=0; j<=n-1; j++) { // // Undistorted position // state.samplex[i,j] = state.xc[j]; // // Do not apply distortion, if we are exactly at boundary constraint. // if( (state.hasbndl[j] && state.hasbndu[j]) && (double)(state.scaledbndl[j])==(double)(state.scaledbndu[j]) ) { continue; } if( state.hasbndl[j] && (double)(state.samplex[i,j])==(double)(state.scaledbndl[j]) ) { continue; } if( state.hasbndu[j] && (double)(state.samplex[i,j])==(double)(state.scaledbndu[j]) ) { continue; } // // Apply distortion // if( (double)(hqrnd.hqrnduniformr(state.agsrs))>=(double)(0.5) ) { // // Sample at the left side with 50% probability // v0 = state.samplex[i,j]-radius; v1 = state.samplex[i,j]; if( state.hasbndl[j] ) { v0 = Math.Max(state.scaledbndl[j], v0); } } else { // // Sample at the right side with 50% probability // v0 = state.samplex[i,j]; v1 = state.samplex[i,j]+radius; if( state.hasbndu[j] ) { v1 = Math.Min(state.scaledbndu[j], v1); } } alglib.ap.assert((double)(v1)>=(double)(v0), "MinNS: integrity check failed"); state.samplex[i,j] = apserv.boundval(v0+(v1-v0)*hqrnd.hqrnduniformr(state.agsrs), v0, v1); } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.samplex[i,i_]; } unscalepointbc(state, state.x); clearrequestfields(state); state.needfij = true; state.rstateags.stage = 2; goto lbl_rcomm; lbl_2: state.needfij = false; generatemeritfunction(state, i); k = k+1; i = i+1; goto lbl_8; lbl_10: cursamplesize = cursamplesize+k; fullsample = cursamplesize==state.agssamplesize; for(j=0; j<=cursamplesize-1; j++) { // // For J-th element in gradient sample, process all of its components // and modify them according to status of box constraints // for(i=0; i<=n-1; i++) { alglib.ap.assert(!state.hasbndl[i] || (double)(state.xc[i])>=(double)(state.scaledbndl[i]), "MinNS: integrity error"); alglib.ap.assert(!state.hasbndu[i] || (double)(state.xc[i])<=(double)(state.scaledbndu[i]), "MinNS: integrity error"); state.samplegmbc[j,i] = state.samplegm[j,i]; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.scaledbndl[i])==(double)(state.scaledbndu[i]) ) { // // I-th box constraint is of equality type (lower bound matches upper one). // Simplest case, always active. // state.samplegmbc[j,i] = 0.0; continue; } if( state.hasbndl[i] && (double)(state.xc[i])==(double)(state.scaledbndl[i]) ) { // // We are at lower bound. // // A bit more complex: // * first, we have to activate/deactivate constraint depending on gradient at XC // * second, in any case, I-th column of gradient sample must be non-positive // if( (double)(state.samplegm[0,i])>=(double)(0.0) ) { state.samplegmbc[j,i] = 0.0; } state.samplegmbc[j,i] = Math.Min(state.samplegmbc[j,i], 0.0); continue; } if( state.hasbndu[i] && (double)(state.xc[i])==(double)(state.scaledbndu[i]) ) { // // We are at upper bound. // // A bit more complex: // * first, we have to activate/deactivate constraint depending on gradient at XC // * second, in any case, I-th column of gradient sample must be non-negative // if( (double)(state.samplegm[0,i])<=(double)(0.0) ) { state.samplegmbc[j,i] = 0.0; } state.samplegmbc[j,i] = Math.Max(state.samplegmbc[j,i], 0.0); continue; } } } // // Calculate diagonal Hessian. // // This Hessian serves two purposes: // * first, it improves performance of gradient descent step // * second, it improves condition number of QP subproblem // solved to determine step // // The idea is that for each variable we check whether sample // includes entries with alternating sign of gradient: // * if gradients with different signs are present, Hessian // component is set to M/R, where M is a maximum magnitude // of corresponding gradient component, R is a sampling radius. // Note that sign=0 and sign=1 are treated as different ones // * if all gradients have same sign, Hessian component is // set to M/R0, where R0 is initial sampling radius. // for(j=0; j<=n-1; j++) { state.colmax[j] = 0.0; state.signmin[j] = 1; state.signmax[j] = -1; } for(i=0; i<=cursamplesize-1; i++) { for(j=0; j<=n-1; j++) { v = state.samplegmbc[i,j]; state.colmax[j] = Math.Max(state.colmax[j], Math.Abs(v)); state.signmin[j] = Math.Min(state.signmin[j], Math.Sign(v)); state.signmax[j] = Math.Max(state.signmax[j], Math.Sign(v)); } } for(j=0; j<=n-1; j++) { if( (double)(state.signmin[j])!=(double)(state.signmax[j]) ) { // // Alternating signs of gradient - step is proportional to current sampling radius // alglib.ap.assert((double)(state.colmax[j])!=(double)(0), "MinNS: integrity check failed"); alglib.ap.assert((double)(radius)!=(double)(0), "MinNS: integrity check failed"); state.diagh[j] = state.colmax[j]/radius; continue; } if( (double)(state.colmax[j])!=(double)(0) ) { // // Non-alternating sign of gradient, but non-zero. // Step is proportional to initial sampling radius // alglib.ap.assert((double)(radius0)!=(double)(0), "MinNS: integrity check failed"); state.diagh[j] = state.colmax[j]/radius0; continue; } state.diagh[j] = 1; } // // PROJECTION PHASE // // We project zero vector on convex hull of gradient sample. // If projection is small enough, we decrease radius and restart. // Otherwise, this phase returns search direction in State.D. // // NOTE: because we use iterative solver, it may have trouble // dealing with ill-conditioned problems. So we also employ // second, backup test for stationarity - when too many // subsequent backtracking searches resulted in short steps. // solveqp(state.samplegmbc, state.diagh, cursamplesize, n, ref state.tmp0, ref state.dbgncholesky, state.nsqp); for(j=0; j<=n-1; j++) { state.d[j] = 0.0; } for(i=0; i<=cursamplesize-1; i++) { v = state.tmp0[i]; for(i_=0; i_<=n-1;i_++) { state.d[i_] = state.d[i_] + v*state.samplegmbc[i,i_]; } } v = 0.0; for(j=0; j<=n-1; j++) { v = Math.Max(v, Math.Abs(state.d[j]/apserv.coalesce(state.colmax[j], 1.0))); } if( (double)(v)<=(double)(state.agsstattold) ) { // // Stationarity test succeded. // Decrease radius and restart. // // NOTE: we also clear ShortStepsCnt on restart // radius = radius*state.agsraddecay; shortstepscnt = 0; apserv.inc(ref radiusdecays); apserv.inc(ref state.repinneriterationscount); goto lbl_4; } for(i=0; i<=n-1; i++) { state.d[i] = -(state.d[i]/state.diagh[i]); } // // Perform backtracking line search. // Update initial step length depending on search results. // Here we assume that D is non-zero. // // NOTE: if AGSShortLimit subsequent line searches resulted // in steps shorter than AGSStatTolStp, we decrease radius. // dnrm = 0.0; dg = 0.0; for(i=0; i<=n-1; i++) { dnrm = dnrm+math.sqr(state.d[i]); dg = dg+state.d[i]*state.samplegmbc[0,i]; } dnrm = Math.Sqrt(dnrm); alglib.ap.assert((double)(dnrm)>(double)(0), "MinNS: integrity error"); alpha = recommendedstep/dnrm; alphadecreased = false; backtrackits = 0; if( fullsample ) { maxbacktrackits = state.agsmaxbacktrack; } else { maxbacktrackits = state.agsmaxbacktracknonfull; } lbl_11: if( false ) { goto lbl_12; } // // Prepare XN and evaluate merit function at XN // for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xn[i_] + alpha*state.d[i_]; } optserv.enforceboundaryconstraints(state.xn, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, n, 0); for(i_=0; i_<=n-1;i_++) { state.samplex[maxsamplesize,i_] = state.xn[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xn[i_]; } unscalepointbc(state, state.x); clearrequestfields(state); state.needfij = true; state.rstateags.stage = 3; goto lbl_rcomm; lbl_3: state.needfij = false; generatemeritfunction(state, maxsamplesize); // // Check sufficient decrease condition // alglib.ap.assert((double)(dnrm)>(double)(0), "MinNS: integrity error"); if( (double)(state.samplef[maxsamplesize])<=(double)(state.samplef[0]+alpha*state.agsdecrease*dg) ) { goto lbl_12; } // // Decrease Alpha // alpha = alpha*state.agsalphadecay; alphadecreased = true; // // Update and check iterations counter. // apserv.inc(ref backtrackits); if( backtrackits>=maxbacktrackits ) { // // Too many backtracking searches performed without success. // Terminate iterations. // alpha = 0.0; alphadecreased = true; for(i_=0; i_<=n-1;i_++) { state.xn[i_] = state.xc[i_]; } goto lbl_12; } goto lbl_11; lbl_12: if( ((double)(alpha*dnrm)<=(double)(state.agsshortstpabs) || (double)(alpha*dnrm)<=(double)(state.agsshortstprel*radius)) || (double)(Math.Abs(state.samplef[0]-state.samplef[maxsamplesize]))<=(double)(state.agsshortf) ) { apserv.inc(ref shortstepscnt); } else { shortstepscnt = 0; } if( shortstepscnt>=state.agsshortlimit ) { // // Too many subsequent short steps. // // It may be possible that optimizer is unable to find out // that we have to decrease radius because of ill-conditioned // gradients. // // Decrease radius and restart. // radius = radius*state.agsraddecay; shortstepscnt = 0; apserv.inc(ref radiusdecays); apserv.inc(ref state.repinneriterationscount); goto lbl_4; } if( !alphadecreased ) { recommendedstep = recommendedstep*2.0; } if( alphadecreased && fullsample ) { recommendedstep = recommendedstep*0.5; } // // Next iteration // for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } apserv.inc(ref state.repinneriterationscount); goto lbl_4; lbl_5: // // Convert back from scaled to unscaled representation // unscalepointbc(state, state.xc); result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstateags.ia[0] = n; state.rstateags.ia[1] = nec; state.rstateags.ia[2] = nic; state.rstateags.ia[3] = ng; state.rstateags.ia[4] = nh; state.rstateags.ia[5] = i; state.rstateags.ia[6] = j; state.rstateags.ia[7] = k; state.rstateags.ia[8] = radiusdecays; state.rstateags.ia[9] = maxsamplesize; state.rstateags.ia[10] = cursamplesize; state.rstateags.ia[11] = shortstepscnt; state.rstateags.ia[12] = backtrackits; state.rstateags.ia[13] = maxbacktrackits; state.rstateags.ba[0] = restartneeded; state.rstateags.ba[1] = b; state.rstateags.ba[2] = alphadecreased; state.rstateags.ba[3] = fullsample; state.rstateags.ra[0] = radius0; state.rstateags.ra[1] = radius; state.rstateags.ra[2] = alpha; state.rstateags.ra[3] = recommendedstep; state.rstateags.ra[4] = dnrm; state.rstateags.ra[5] = dg; state.rstateags.ra[6] = v; state.rstateags.ra[7] = vv; state.rstateags.ra[8] = v0; state.rstateags.ra[9] = v1; return result; }
/************************************************************************* This subroutine submits request for termination of running optimizer. It should be called from user-supplied callback when user decides that it is time to "smoothly" terminate optimization process. As result, optimizer stops at point which was "current accepted" when termination request was submitted and returns error code 8 (successful termination). INPUT PARAMETERS: State - optimizer structure NOTE: after request for termination optimizer may perform several additional calls to user-supplied callbacks. It does NOT guarantee to stop immediately - it just guarantees that these additional calls will be discarded later. NOTE: calling this function on optimizer which is NOT running will have no effect. NOTE: multiple calls to this function are possible. First call is counted, subsequent calls are silently ignored. -- ALGLIB -- Copyright 18.05.2015 by Bochkanov Sergey *************************************************************************/ public static void minnsrequesttermination(minnsstate state) { state.userterminationneeded = true; }
/************************************************************************* This function performs transformation of X from scaled coordinates to unscaled ones, paying special attention to box constraints: * points which were exactly at the boundary before scaling will be mapped to corresponding boundary after scaling * in any case, unscaled box constraints will be satisfied -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void unscalepointbc(minnsstate state, double[] x) { int i = 0; for(i=0; i<=state.n-1; i++) { if( state.hasbndl[i] && (double)(x[i])<=(double)(state.scaledbndl[i]) ) { x[i] = state.bndl[i]; continue; } if( state.hasbndu[i] && (double)(x[i])>=(double)(state.scaledbndu[i]) ) { x[i] = state.bndu[i]; continue; } x[i] = x[i]*state.s[i]; if( state.hasbndl[i] && (double)(x[i])<=(double)(state.bndl[i]) ) { x[i] = state.bndl[i]; } if( state.hasbndu[i] && (double)(x[i])>=(double)(state.bndu[i]) ) { x[i] = state.bndu[i]; } } }
public override alglib.apobject make_copy() { minnsstate _result = new minnsstate(); _result.solvertype = solvertype; _result.n = n; _result.epsx = epsx; _result.maxits = maxits; _result.xrep = xrep; _result.diffstep = diffstep; _result.s = (double[])s.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.hasbndl = (bool[])hasbndl.Clone(); _result.hasbndu = (bool[])hasbndu.Clone(); _result.nec = nec; _result.nic = nic; _result.cleic = (double[,])cleic.Clone(); _result.ng = ng; _result.nh = nh; _result.x = (double[])x.Clone(); _result.f = f; _result.fi = (double[])fi.Clone(); _result.j = (double[,])j.Clone(); _result.needfij = needfij; _result.needfi = needfi; _result.xupdated = xupdated; _result.rstate = (rcommstate)rstate.make_copy(); _result.rstateags = (rcommstate)rstateags.make_copy(); _result.agsrs = (hqrnd.hqrndstate)agsrs.make_copy(); _result.agsradius = agsradius; _result.agssamplesize = agssamplesize; _result.agsraddecay = agsraddecay; _result.agsalphadecay = agsalphadecay; _result.agsdecrease = agsdecrease; _result.agsinitstp = agsinitstp; _result.agsstattold = agsstattold; _result.agsshortstpabs = agsshortstpabs; _result.agsshortstprel = agsshortstprel; _result.agsshortf = agsshortf; _result.agsshortlimit = agsshortlimit; _result.agsrhononlinear = agsrhononlinear; _result.agsminupdate = agsminupdate; _result.agsmaxraddecays = agsmaxraddecays; _result.agsmaxbacktrack = agsmaxbacktrack; _result.agsmaxbacktracknonfull = agsmaxbacktracknonfull; _result.agspenaltylevel = agspenaltylevel; _result.agspenaltyincrease = agspenaltyincrease; _result.xstart = (double[])xstart.Clone(); _result.xc = (double[])xc.Clone(); _result.xn = (double[])xn.Clone(); _result.grs = (double[])grs.Clone(); _result.d = (double[])d.Clone(); _result.colmax = (double[])colmax.Clone(); _result.diagh = (double[])diagh.Clone(); _result.signmin = (double[])signmin.Clone(); _result.signmax = (double[])signmax.Clone(); _result.userterminationneeded = userterminationneeded; _result.scaledbndl = (double[])scaledbndl.Clone(); _result.scaledbndu = (double[])scaledbndu.Clone(); _result.scaledcleic = (double[,])scaledcleic.Clone(); _result.rholinear = (double[])rholinear.Clone(); _result.samplex = (double[,])samplex.Clone(); _result.samplegm = (double[,])samplegm.Clone(); _result.samplegmbc = (double[,])samplegmbc.Clone(); _result.samplef = (double[])samplef.Clone(); _result.samplef0 = (double[])samplef0.Clone(); _result.nsqp = (minnsqp)nsqp.make_copy(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmp1 = (double[])tmp1.Clone(); _result.tmp2 = (double[,])tmp2.Clone(); _result.tmp3 = (int[])tmp3.Clone(); _result.xbase = (double[])xbase.Clone(); _result.fp = (double[])fp.Clone(); _result.fm = (double[])fm.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repnfev = repnfev; _result.repvaridx = repvaridx; _result.repfuncidx = repfuncidx; _result.repterminationtype = repterminationtype; _result.replcerr = replcerr; _result.repnlcerr = repnlcerr; _result.dbgncholesky = dbgncholesky; return _result; }