/************************************************************************* 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 MinNLCOptimize() - one which accepts function AND Jacobian or one which accepts ONLY function. Be careful to choose variant of MinNLCOptimize() which corresponds to your optimization scheme! Table below lists different combinations of callback (function/gradient) passed to MinNLCOptimize() and specific function used to create optimizer. | USER PASSED TO MinNLCOptimize() CREATED WITH | function only | function and gradient ------------------------------------------------------------ MinNLCCreateF() | works FAILS MinNLCCreate() | FAILS works Here "FAILS" denotes inappropriate combinations of optimizer creation function and MinNLCOptimize() 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 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static bool minnlciteration(minnlcstate state) { bool result = new bool(); int i = 0; int k = 0; int n = 0; int ng = 0; int nh = 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]; } else { i = -983; k = -989; n = -834; ng = 900; nh = -287; } 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; } if( state.rstate.stage==4 ) { goto lbl_4; } if( state.rstate.stage==5 ) { goto lbl_5; } if( state.rstate.stage==6 ) { goto lbl_6; } if( state.rstate.stage==7 ) { goto lbl_7; } if( state.rstate.stage==8 ) { goto lbl_8; } // // Routine body // // // Init // state.repterminationtype = 0; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repnfev = 0; state.repvaridx = 0; state.repfuncidx = 0; state.repdbgphase0its = 0; n = state.n; ng = state.ng; nh = state.nh; clearrequestfields(state); // // Test gradient // if( !((double)(state.diffstep)==(double)(0) && (double)(state.teststep)>(double)(0)) ) { goto lbl_9; } apserv.rvectorsetlengthatleast(ref state.xbase, n); apserv.rvectorsetlengthatleast(ref state.fbase, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.dfbase, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fm1, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fp1, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.dfm1, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.dfp1, 1+ng+nh); state.needfij = true; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.xstart[i_]; } k = 0; lbl_11: if( k>n-1 ) { goto lbl_13; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 0; goto lbl_rcomm; lbl_0: for(i_=0; i_<=ng+nh;i_++) { state.fbase[i_] = state.fi[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.dfbase[i_] = state.j[i_,k]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.teststep; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: for(i_=0; i_<=ng+nh;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.dfm1[i_] = state.j[i_,k]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.teststep; state.rstate.stage = 2; goto lbl_rcomm; lbl_2: for(i_=0; i_<=ng+nh;i_++) { state.fp1[i_] = state.fi[i_]; } for(i_=0; i_<=ng+nh;i_++) { state.dfp1[i_] = state.j[i_,k]; } for(i=0; i<=ng+nh; i++) { if( !optserv.derivativecheck(state.fm1[i], state.dfm1[i], state.fp1[i], state.dfp1[i], state.fbase[i], state.dfbase[i], 2*state.s[k]*state.teststep) ) { state.repfuncidx = i; state.repvaridx = k; state.repterminationtype = -7; result = false; return result; } } k = k+1; goto lbl_11; lbl_13: state.needfij = false; lbl_9: // // AUL solver // if( state.solvertype!=0 ) { goto lbl_14; } if( (double)(state.diffstep)!=(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.xbase, n); apserv.rvectorsetlengthatleast(ref state.fbase, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fm2, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fm1, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fp1, 1+ng+nh); apserv.rvectorsetlengthatleast(ref state.fp2, 1+ng+nh); } state.rstateaul.ia = new int[8+1]; state.rstateaul.ra = new double[7+1]; state.rstateaul.stage = -1; lbl_16: if( !auliteration(state) ) { goto lbl_17; } // // 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_18; } state.needfij = false; state.needfi = true; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.x[i_]; } k = 0; lbl_20: if( k>n-1 ) { goto lbl_22; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-state.s[k]*state.diffstep; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: for(i_=0; i_<=ng+nh;i_++) { state.fm2[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]-0.5*state.s[k]*state.diffstep; state.rstate.stage = 4; goto lbl_rcomm; lbl_4: for(i_=0; i_<=ng+nh;i_++) { state.fm1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+0.5*state.s[k]*state.diffstep; state.rstate.stage = 5; goto lbl_rcomm; lbl_5: for(i_=0; i_<=ng+nh;i_++) { state.fp1[i_] = state.fi[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.x[k] = state.x[k]+state.s[k]*state.diffstep; state.rstate.stage = 6; goto lbl_rcomm; lbl_6: for(i_=0; i_<=ng+nh;i_++) { state.fp2[i_] = state.fi[i_]; } for(i=0; i<=ng+nh; i++) { state.j[i,k] = (8*(state.fp1[i]-state.fm1[i])-(state.fp2[i]-state.fm2[i]))/(6*state.diffstep*state.s[i]); } k = k+1; goto lbl_20; lbl_22: for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 7; goto lbl_rcomm; lbl_7: // // Restore previous values of fields and continue // state.needfi = false; state.needfij = true; goto lbl_16; lbl_18: // // Forward request to caller // state.rstate.stage = 8; goto lbl_rcomm; lbl_8: goto lbl_16; lbl_17: result = false; return result; lbl_14: 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; return result; }
/************************************************************************* MinNLC 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. * -7 gradient verification failed. See MinNLCSetGradientCheck() for more information. * 1 relative function improvement is no more than EpsF. * 2 scaled step is no more than EpsX. * 4 scaled gradient norm is no more than EpsG. * 5 MaxIts steps was taken More information about fields of this structure can be found in the comments on MinNLCReport datatype. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcresults(minnlcstate state, ref double[] x, minnlcreport rep) { x = new double[0]; minnlcresultsbuf(state, ref x, rep); }
/************************************************************************* This function tells MinNLC unit to use Augmented Lagrangian algorithm for nonlinearly constrained optimization. This algorithm is a slight modification of one described in "A Modified Barrier-Augmented Lagrangian Method for Constrained Minimization (1999)" by D.GOLDFARB, R.POLYAK, K. SCHEINBERG, I.YUZEFOVICH. Augmented Lagrangian algorithm works by converting problem of minimizing F(x) subject to equality/inequality constraints to unconstrained problem of the form min[ f(x) + + Rho*PENALTY_EQ(x) + SHIFT_EQ(x,Nu1) + + Rho*PENALTY_INEQ(x) + SHIFT_INEQ(x,Nu2) ] where: * Rho is a fixed penalization coefficient * PENALTY_EQ(x) is a penalty term, which is used to APPROXIMATELY enforce equality constraints * SHIFT_EQ(x) is a special "shift" term which is used to "fine-tune" equality constraints, greatly increasing precision * PENALTY_INEQ(x) is a penalty term which is used to approximately enforce inequality constraints * SHIFT_INEQ(x) is a special "shift" term which is used to "fine-tune" inequality constraints, greatly increasing precision * Nu1/Nu2 are vectors of Lagrange coefficients which are fine-tuned during outer iterations of algorithm This version of AUL algorithm uses preconditioner, which greatly accelerates convergence. Because this algorithm is similar to penalty methods, it may perform steps into infeasible area. All kinds of constraints (boundary, linear and nonlinear ones) may be violated in intermediate points - and in the solution. However, properly configured AUL method is significantly better at handling constraints than barrier and/or penalty methods. The very basic outline of algorithm is given below: 1) first outer iteration is performed with "default" values of Lagrange multipliers Nu1/Nu2. Solution quality is low (candidate point can be too far away from true solution; large violation of constraints is possible) and is comparable with that of penalty methods. 2) subsequent outer iterations refine Lagrange multipliers and improve quality of the solution. INPUT PARAMETERS: State - structure which stores algorithm state Rho - penalty coefficient, Rho>0: * large enough that algorithm converges with desired precision. Minimum value is 10*max(S'*diag(H)*S), where S is a scale matrix (set by MinNLCSetScale) and H is a Hessian of the function being minimized. If you can not easily estimate Hessian norm, see our recommendations below. * not TOO large to prevent ill-conditioning * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * it is important to note that Rho is internally multiplied by scaling matrix, i.e. optimum value of Rho depends on scale of variables specified by MinNLCSetScale(). ItsCnt - number of outer iterations: * ItsCnt=0 means that small number of outer iterations is automatically chosen (10 iterations in current version). * ItsCnt=1 means that AUL algorithm performs just as usual barrier method. * ItsCnt>1 means that AUL algorithm performs specified number of outer iterations HOW TO CHOOSE PARAMETERS Nonlinear optimization is a tricky area and Augmented Lagrangian algorithm is sometimes hard to tune. Good values of Rho and ItsCnt are problem- specific. In order to help you we prepared following set of recommendations: * for unit-scale problems (variables and Hessian have unit magnitude), Rho=100 or Rho=1000 can be used. * start from some small value of Rho and solve problem with just one outer iteration (ItcCnt=1). In this case algorithm behaves like penalty method. Increase Rho in 2x or 10x steps until you see that one outer iteration returns point which is "rough approximation to solution". It is very important to have Rho so large that penalty term becomes constraining i.e. modified function becomes highly convex in constrained directions. From the other side, too large Rho may prevent you from converging to the solution. You can diagnose it by studying number of inner iterations performed by algorithm: too few (5-10 on 1000-dimensional problem) or too many (orders of magnitude more than dimensionality) usually means that Rho is too large. * with just one outer iteration you usually have low-quality solution. Some constraints can be violated with very large margin, while other ones (which are NOT violated in the true solution) can push final point too far in the inner area of the feasible set. For example, if you have constraint x0>=0 and true solution x0=1, then merely a presence of "x0>=0" will introduce a bias towards larger values of x0. Say, algorithm may stop at x0=1.5 instead of 1.0. * after you found good Rho, you may increase number of outer iterations. ItsCnt=10 is a good value. Subsequent outer iteration will refine values of Lagrange multipliers. Constraints which were violated will be enforced, inactive constraints will be dropped (corresponding multipliers will be decreased). Ideally, you should see 10-1000x improvement in constraint handling (constraint violation is reduced). * if you see that algorithm converges to vicinity of solution, but additional outer iterations do not refine solution, it may mean that algorithm is unstable - it wanders around true solution, but can not approach it. Sometimes algorithm may be stabilized by increasing Rho one more time, making it 5x or 10x larger. SCALING OF CONSTRAINTS [IMPORTANT] AUL optimizer scales variables according to scale specified by MinNLCSetScale() function, so it can handle problems with badly scaled variables (as long as we KNOW their scales). However, because function being optimized is a mix of original function and constraint-dependent penalty functions, it is important to rescale both variables AND constraints. Say, if you minimize f(x)=x^2 subject to 1000000*x>=0, then you have constraint whose scale is different from that of target function (another example is 0.000001*x>=0). It is also possible to have constraints whose scales are misaligned: 1000000*x0>=0, 0.000001*x1<=0. Inappropriate scaling may ruin convergence because minimizing x^2 subject to x>=0 is NOT same as minimizing it subject to 1000000*x>=0. Because we know coefficients of boundary/linear constraints, we can automatically rescale and normalize them. However, there is no way to automatically rescale nonlinear constraints Gi(x) and Hi(x) - they are black boxes. 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 rescale nonlinear constraints in such way that I-th component of dG/dX (or dH/dx) has magnitude approximately equal to 1/S[i] (where S is a scale set by MinNLCSetScale() function). WHAT IF IT DOES NOT CONVERGE? It is possible that AUL algorithm fails to converge to precise values of Lagrange multipliers. It stops somewhere around true solution, but candidate point is still too far from solution, and some constraints are violated. Such kind of failure is specific for Lagrangian algorithms - technically, they stop at some point, but this point is not constrained solution. There are exist several reasons why algorithm may fail to converge: a) too loose stopping criteria for inner iteration b) degenerate, redundant constraints c) target function has unconstrained extremum exactly at the boundary of some constraint d) numerical noise in the target function In all these cases algorithm is unstable - each outer iteration results in large and almost random step which improves handling of some constraints, but violates other ones (ideally outer iterations should form a sequence of progressively decreasing steps towards solution). First reason possible is that too loose stopping criteria for inner iteration were specified. Augmented Lagrangian algorithm solves a sequence of intermediate problems, and requries each of them to be solved with high precision. Insufficient precision results in incorrect update of Lagrange multipliers. Another reason is that you may have specified degenerate constraints: say, some constraint was repeated twice. In most cases AUL algorithm gracefully handles such situations, but sometimes it may spend too much time figuring out subtle degeneracies in constraint matrix. Third reason is tricky and hard to diagnose. Consider situation when you minimize f=x^2 subject to constraint x>=0. Unconstrained extremum is located exactly at the boundary of constrained area. In this case algorithm will tend to oscillate between negative and positive x. Each time it stops at x<0 it "reinforces" constraint x>=0, and each time it is bounced to x>0 it "relaxes" constraint (and is attracted to x<0). Such situation sometimes happens in problems with hidden symetries. Algorithm is got caught in a loop with Lagrange multipliers being continuously increased/decreased. Luckily, such loop forms after at least three iterations, so this problem can be solved by DECREASING number of outer iterations down to 1-2 and increasing penalty coefficient Rho as much as possible. Final reason is numerical noise. AUL algorithm is robust against moderate noise (more robust than, say, active set methods), but large noise may destabilize algorithm. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetalgoaul(minnlcstate state, double rho, int itscnt) { alglib.ap.assert(itscnt>=0, "MinNLCSetAlgoAUL: negative ItsCnt"); alglib.ap.assert(math.isfinite(rho), "MinNLCSetAlgoAUL: Rho is not finite"); alglib.ap.assert((double)(rho)>(double)(0), "MinNLCSetAlgoAUL: Rho<=0"); if( itscnt==0 ) { itscnt = 10; } state.aulitscnt = itscnt; state.rho = rho; state.solvertype = 0; }
/************************************************************************* 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 MinNLCOptimize(). NOTE: algorithm passes two parameters to rep() callback - current point and penalized function value at current point. Important - function value which is returned is NOT function being minimized. It is sum of the value of the function being minimized - and penalty term. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetxrep(minnlcstate state, bool needxrep) { state.xrep = needxrep; }
/************************************************************************* Internal initialization subroutine. Sets default NLC solver with default criteria. *************************************************************************/ private static void minnlcinitinternal(int n, double[] x, double diffstep, minnlcstate state) { int i = 0; double[,] c = new double[0,0]; int[] ct = new int[0]; // // Default params // state.stabilizingpoint = -100.0; state.initialinequalitymultiplier = 1.0; // // Initialize other params // state.teststep = 0; 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.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]; } minnlcsetlc(state, c, ct, 0); minnlcsetnlc(state, 0, 0); minnlcsetcond(state, 0.0, 0.0, 0.0, 0); minnlcsetxrep(state, false); minnlcsetalgoaul(state, 1.0E-3, 0); minnlcsetprecinexact(state); minlbfgs.minlbfgscreate(n, Math.Min(lbfgsfactor, n), x, state.auloptimizer); minnlcrestartfrom(state, x); }
/************************************************************************* NONLINEARLY CONSTRAINED OPTIMIZATION WITH PRECONDITIONED AUGMENTED LAGRANGIAN ALGORITHM 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 REQUIREMENTS: * user must provide function value and gradient for F(), H(), G() * starting point X0 must be feasible or not too far away from the feasible set * F(), G(), H() are twice continuously differentiable on the feasible set and its neighborhood * nonlinear constraints G() and H() must have non-zero gradient at G(x)=0 and at H(x)=0. Say, constraint like x^2>=1 is supported, but x^2>=0 is NOT supported. USAGE: Constrained optimization if far more complex than the unconstrained one. Nonlinearly constrained optimization is one of the most esoteric numerical procedures. Here we give very brief outline of the MinNLC optimizer. We strongly recommend you to study examples in the ALGLIB Reference Manual and to read ALGLIB User Guide on optimization, which is available at http://www.alglib.net/optimization/ 1. User initializes algorithm state with MinNLCCreate() 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: * AUL solver (activated with MinNLCSetAlgoAUL() function) 2. User adds boundary and/or linear and/or nonlinear constraints by means of calling one of the following functions: a) MinNLCSetBC() for boundary constraints b) MinNLCSetLC() for linear constraints c) MinNLCSetNLC() for nonlinear constraints You may combine (a), (b) and (c) in one optimization problem. 3. User sets scale of the variables with MinNLCSetScale() 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 MinNLCSetCond(). If NLC solver uses inner/outer iteration layout, this function sets stopping conditions for INNER iterations. 5. User chooses one of the preconditioning methods. Preconditioning is very important for efficient handling of boundary/linear/nonlinear constraints. Without preconditioning algorithm would require thousands of iterations even for simple problems. Two preconditioners can be used: * approximate LBFGS-based preconditioner which should be used for problems with almost orthogonal constraints (activated by calling MinNLCSetPrecInexact) * exact low-rank preconditiner (activated by MinNLCSetPrecExactLowRank) which should be used for problems with moderate number of constraints which do not have to be orthogonal. 6. Finally, user calls MinNLCOptimize() function which takes algorithm state and pointer (delegate, etc.) to callback function which calculates F/G/H. 7. User calls MinNLCResults() to get solution 8. Optionally user may call MinNLCRestartFrom() to solve another problem with same N but another starting point. MinNLCRestartFrom() 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 ofX 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 -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreate(int n, double[] x, minnlcstate state) { alglib.ap.assert(n>=1, "MinNLCCreate: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNLCCreate: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNLCCreate: X contains infinite or NaN values"); minnlcinitinternal(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 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetscale(minnlcstate state, double[] s) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinNLCSetScale: Length(S)<N"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(s[i]), "MinNLCSetScale: S contains infinite or NAN elements"); alglib.ap.assert((double)(s[i])!=(double)(0), "MinNLCSetScale: S contains zero elements"); state.s[i] = Math.Abs(s[i]); } }
/************************************************************************* This subroutine turns on verification of the user-supplied analytic gradient: * user calls this subroutine before optimization begins * MinNLCOptimize() is called * prior to actual optimization, for each component of parameters being optimized X[i] algorithm performs following steps: * two trial steps are made to X[i]-TestStep*S[i] and X[i]+TestStep*S[i], where X[i] is i-th component of the initial point and S[i] is a scale of i-th parameter * F(X) is evaluated at these trial points * we perform one more evaluation in the middle point of the interval * we build cubic model using function values and derivatives at trial points and we compare its prediction with actual value in the middle point * in case difference between prediction and actual value is higher than some predetermined threshold, algorithm stops with completion code -7; Rep.VarIdx is set to index of the parameter with incorrect derivative, and Rep.FuncIdx is set to index of the function. * after verification is over, algorithm proceeds to the actual optimization. NOTE 1: verification needs N (parameters count) gradient evaluations. It is very costly and you should use it only for low dimensional problems, when you want to be sure that you've correctly calculated analytic derivatives. You should not use it in the production code (unless you want to check derivatives provided by some third party). NOTE 2: you should carefully choose TestStep. Value which is too large (so large that function behaviour is significantly non-cubic) will lead to false alarms. You may use different step for different parameters by means of setting scale with MinNLCSetScale(). NOTE 3: this function may lead to false positives. In case it reports that I-th derivative was calculated incorrectly, you may decrease test step and try one more time - maybe your function changes too sharply and your step is too large for such rapidly chanding function. INPUT PARAMETERS: State - structure used to store algorithm state TestStep - verification step: * TestStep=0 turns verification off * TestStep>0 activates verification -- ALGLIB -- Copyright 15.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetgradientcheck(minnlcstate state, double teststep) { alglib.ap.assert(math.isfinite(teststep), "MinNLCSetGradientCheck: TestStep contains NaN or Infinite"); alglib.ap.assert((double)(teststep)>=(double)(0), "MinNLCSetGradientCheck: invalid argument TestStep(TestStep<0)"); state.teststep = teststep; }
/************************************************************************* This function sets nonlinear constraints for MinNLC optimizer. In fact, this function sets NUMBER of nonlinear constraints. Constraints itself (constraint functions) are passed to MinNLCOptimize() method. This method requires user-defined vector function F[] and its Jacobian J[], where: * first component of F[] and first row of Jacobian J[] corresponds 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 MinNLCCreate call. NLEC - number of Non-Linear Equality Constraints (NLEC), >=0 NLIC - number of Non-Linear Inquality Constraints (NLIC), >=0 NOTE 1: when you solve your problem with augmented Lagrangian solver, nonlinear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! NOTE 2: algorithm scales variables according to scale specified by MinNLCSetScale() 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 MinNLCSetScale() function). -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetnlc(minnlcstate state, int nlec, int nlic) { alglib.ap.assert(nlec>=0, "MinNLCSetNLC: NLEC<0"); alglib.ap.assert(nlic>=0, "MinNLCSetNLC: 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 inner iterations of optimizer. INPUT PARAMETERS: State - structure which stores algorithm state EpsG - >=0 The subroutine finishes its work if the condition |v|<EpsG is satisfied, where: * |.| means Euclidian norm * v - scaled gradient vector, v[i]=g[i]*s[i] * g - gradient * s - scaling coefficients set by MinNLCSetScale() EpsF - >=0 The subroutine finishes its work if on k+1-th iteration the condition |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. EpsX - >=0 The subroutine finishes its work if on k+1-th iteration the condition |v|<=EpsX is fulfilled, where: * |.| means Euclidian norm * v - scaled step vector, v[i]=dx[i]/s[i] * dx - step vector, dx=X(k+1)-X(k) * s - scaling coefficients set by MinNLCSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetcond(minnlcstate state, double epsg, double epsf, double epsx, int maxits) { alglib.ap.assert(math.isfinite(epsg), "MinNLCSetCond: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinNLCSetCond: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinNLCSetCond: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinNLCSetCond: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinNLCSetCond: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinNLCSetCond: negative EpsX"); alglib.ap.assert(maxits>=0, "MinNLCSetCond: negative MaxIts!"); if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.epsg = epsg; state.epsf = epsf; state.epsx = epsx; state.maxits = maxits; }
/************************************************************************* This function sets linear constraints for MinNLC optimizer. Linear constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinNLCRestartFrom(). You may combine linear constraints with boundary ones - and with nonlinear ones! If your problem has mixed constraints, you may explicitly specify some of them as linear. It may help optimizer to handle them more efficiently. INPUT PARAMETERS: State - structure previously allocated with MinNLCCreate 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 1: when you solve your problem with augmented Lagrangian solver, linear constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetlc(minnlcstate 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, "MinNLCSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinNLCSetLC: Cols(C)<N+1"); alglib.ap.assert(alglib.ap.rows(c)>=k, "MinNLCSetLC: Rows(C)<K"); alglib.ap.assert(alglib.ap.len(ct)>=k, "MinNLCSetLC: Length(CT)<K"); alglib.ap.assert(apserv.apservisfinitematrix(c, k, n+1), "MinNLCSetLC: 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 for NLC optimizer. Boundary constraints are inactive by default (after initial creation). They are preserved after algorithm restart with MinNLCRestartFrom(). You may combine boundary constraints with general linear ones - and with nonlinear ones! Boundary constraints are handled more efficiently than other types. Thus, if your problem has mixed constraints, you may explicitly specify some of them as boundary and save some time/space. 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: when you solve your problem with augmented Lagrangian solver, boundary constraints are satisfied only approximately! It is possible that algorithm will evaluate function outside of feasible area! -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetbc(minnlcstate state, double[] bndl, double[] bndu) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinNLCSetBC: Length(BndL)<N"); alglib.ap.assert(alglib.ap.len(bndu)>=n, "MinNLCSetBC: Length(BndU)<N"); for(i=0; i<=n-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "MinNLCSetBC: BndL contains NAN or +INF"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "MinNLCSetBC: 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]); } }
/************************************************************************* This subroutine is a finite difference variant of MinNLCCreate(). It uses finite differences in order to differentiate target function. Description below contains information which is specific to this function only. We recommend to read comments on MinNLCCreate() in order to get more information about creation of NLC optimizer. INPUT PARAMETERS: N - problem dimension, N>0: * if given, only leading N elements of X are used * if not given, automatically determined from size ofX 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, >0 OUTPUT PARAMETERS: State - structure stores algorithm state NOTES: 1. algorithm uses 4-point central formula for differentiation. 2. differentiation step along I-th axis is equal to DiffStep*S[I] where S[] is scaling vector which can be set by MinNLCSetScale() call. 3. we recommend you to use moderate values of differentiation step. Too large step will result in too large TRUNCATION errors, while too small step will result in too large NUMERICAL errors. 1.0E-4 can be good value to start from. 4. Numerical differentiation is very inefficient - one gradient calculation needs 4*N function evaluations. This function will work for any N - either small (1...10), moderate (10...100) or large (100...). However, performance penalty will be too severe for any N's except for small ones. We should also say that code which relies on numerical differentiation is less robust and precise. Imprecise gradient may slow down convergence, especially on highly nonlinear problems. Thus we recommend to use this function for fast prototyping on small- dimensional problems only, and to implement analytical gradient as soon as possible. -- ALGLIB -- Copyright 06.06.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlccreatef(int n, double[] x, double diffstep, minnlcstate state) { alglib.ap.assert(n>=1, "MinNLCCreateF: N<1"); alglib.ap.assert(alglib.ap.len(x)>=n, "MinNLCCreateF: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNLCCreateF: X contains infinite or NaN values"); alglib.ap.assert(math.isfinite(diffstep), "MinNLCCreateF: DiffStep is infinite or NaN!"); alglib.ap.assert((double)(diffstep)>(double)(0), "MinNLCCreateF: DiffStep is non-positive!"); minnlcinitinternal(n, x, diffstep, state); }
/************************************************************************* NLC results Buffered implementation of MinNLCResults() 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 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcresultsbuf(minnlcstate state, ref double[] x, minnlcreport 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.dbgphase0its = state.repdbgphase0its; 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 function sets preconditioner to "inexact LBFGS-based" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may utilize two preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, and b) exact low rank one. It also provides special unpreconditioned mode of operation which can be used for test purposes. Comments below discuss LBFGS-based preconditioner. Inexact LBFGS-based preconditioner uses L-BFGS formula combined with orthogonality assumption to perform very fast updates. For a N-dimensional problem with K general linear or nonlinear constraints (boundary ones are not counted) it has O(N*K) cost per iteration. This preconditioner has best quality (less iterations) when general linear and nonlinear constraints are orthogonal to each other (orthogonality with respect to boundary constraints is not required). Number of iterations increases when constraints are non-orthogonal, because algorithm assumes orthogonality, but still it is better than no preconditioner at all. INPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecinexact(minnlcstate state) { state.updatefreq = 0; state.prectype = 1; }
/************************************************************************* 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 MinNLCCreate call. X - new starting point. -- ALGLIB -- Copyright 28.11.2010 by Bochkanov Sergey *************************************************************************/ public static void minnlcrestartfrom(minnlcstate 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, "MinNLCRestartFrom: Length(X)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinNLCRestartFrom: 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.stage = -1; clearrequestfields(state); }
/************************************************************************* This function sets preconditioner to "exact low rank" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may utilize two preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, and b) exact low rank one. It also provides special unpreconditioned mode of operation which can be used for test purposes. Comments below discuss low rank preconditioner. Exact low-rank preconditioner uses Woodbury matrix identity to build quadratic model of the penalized function. It has no special assumptions about orthogonality, so it is quite general. However, for a N-dimensional problem with K general linear or nonlinear constraints (boundary ones are not counted) it has O(N*K^2) cost per iteration (for comparison: inexact LBFGS-based preconditioner has O(N*K) cost). INPUT PARAMETERS: State - structure stores algorithm state UpdateFreq- update frequency. Preconditioner is rebuilt after every UpdateFreq iterations. Recommended value: 10 or higher. Zero value means that good default value will be used. -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecexactlowrank(minnlcstate state, int updatefreq) { alglib.ap.assert(updatefreq>=0, "MinNLCSetPrecExactLowRank: UpdateFreq<0"); if( updatefreq==0 ) { updatefreq = 10; } state.prectype = 2; state.updatefreq = updatefreq; }
/************************************************************************* Clears request fileds (to be sure that we don't forget to clear something) *************************************************************************/ private static void clearrequestfields(minnlcstate state) { state.needfi = false; state.needfij = false; state.xupdated = false; }
/************************************************************************* This function sets preconditioner to "turned off" mode. Preconditioning is very important for convergence of Augmented Lagrangian algorithm because presence of penalty term makes problem ill-conditioned. Difference between performance of preconditioned and unpreconditioned methods can be as large as 100x! MinNLC optimizer may utilize two preconditioners, each with its own benefits and drawbacks: a) inexact LBFGS-based, and b) exact low rank one. It also provides special unpreconditioned mode of operation which can be used for test purposes. This function activates this test mode. Do not use it in production code to solve real-life problems. INPUT PARAMETERS: State - structure stores algorithm state -- ALGLIB -- Copyright 26.09.2014 by Bochkanov Sergey *************************************************************************/ public static void minnlcsetprecnone(minnlcstate state) { state.updatefreq = 0; state.prectype = 0; }
/************************************************************************* 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.2014 by Bochkanov Sergey *************************************************************************/ private static bool auliteration(minnlcstate 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 outerit = 0; int preccounter = 0; double v = 0; double vv = 0; double p = 0; double dp = 0; double d2p = 0; double v0 = 0; double v1 = 0; double v2 = 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.rstateaul.stage>=0 ) { n = state.rstateaul.ia[0]; nec = state.rstateaul.ia[1]; nic = state.rstateaul.ia[2]; ng = state.rstateaul.ia[3]; nh = state.rstateaul.ia[4]; i = state.rstateaul.ia[5]; j = state.rstateaul.ia[6]; outerit = state.rstateaul.ia[7]; preccounter = state.rstateaul.ia[8]; v = state.rstateaul.ra[0]; vv = state.rstateaul.ra[1]; p = state.rstateaul.ra[2]; dp = state.rstateaul.ra[3]; d2p = state.rstateaul.ra[4]; v0 = state.rstateaul.ra[5]; v1 = state.rstateaul.ra[6]; v2 = state.rstateaul.ra[7]; } else { n = 364; nec = 214; nic = -338; ng = -686; nh = 912; i = 585; j = 497; outerit = -271; preccounter = -581; v = 745; vv = -533; p = -77; dp = 678; d2p = -293; v0 = 316; v1 = 647; v2 = -756; } if( state.rstateaul.stage==0 ) { goto lbl_0; } if( state.rstateaul.stage==1 ) { goto lbl_1; } if( state.rstateaul.stage==2 ) { goto lbl_2; } // // Routine body // alglib.ap.assert(state.solvertype==0, "MinNLC: internal error"); n = state.n; nec = state.nec; nic = state.nic; ng = state.ng; nh = state.nh; // // Prepare scaled problem // apserv.rvectorsetlengthatleast(ref state.scaledbndl, n); apserv.rvectorsetlengthatleast(ref state.scaledbndu, n); apserv.rmatrixsetlengthatleast(ref state.scaledcleic, nec+nic, n+1); for(i=0; i<=n-1; i++) { if( state.hasbndl[i] ) { state.scaledbndl[i] = state.bndl[i]/state.s[i]; } if( state.hasbndu[i] ) { state.scaledbndu[i] = state.bndu[i]/state.s[i]; } state.xc[i] = state.xstart[i]/state.s[i]; } for(i=0; i<=nec+nic-1; i++) { // // 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; } } } // // Prepare stopping criteria // minlbfgs.minlbfgssetcond(state.auloptimizer, state.epsg, state.epsf, state.epsx, state.maxits); // // Main AUL cycle: // * prepare Lagrange multipliers NuNB/NuLC // * set GammaK (current estimate of Hessian norm) to 0.0 and XKPresent to False // apserv.rvectorsetlengthatleast(ref state.nubc, 2*n); apserv.rvectorsetlengthatleast(ref state.nulc, nec+nic); apserv.rvectorsetlengthatleast(ref state.nunlc, ng+nh); apserv.rvectorsetlengthatleast(ref state.xk, n); apserv.rvectorsetlengthatleast(ref state.gk, n); apserv.rvectorsetlengthatleast(ref state.xk1, n); apserv.rvectorsetlengthatleast(ref state.gk1, n); for(i=0; i<=n-1; i++) { state.nubc[2*i+0] = 0.0; state.nubc[2*i+1] = 0.0; if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { continue; } if( state.hasbndl[i] ) { state.nubc[2*i+0] = state.initialinequalitymultiplier; } if( state.hasbndu[i] ) { state.nubc[2*i+1] = state.initialinequalitymultiplier; } } for(i=0; i<=nec-1; i++) { state.nulc[i] = 0.0; } for(i=0; i<=nic-1; i++) { state.nulc[nec+i] = state.initialinequalitymultiplier; } for(i=0; i<=ng-1; i++) { state.nunlc[i] = 0.0; } for(i=0; i<=nh-1; i++) { state.nunlc[ng+i] = state.initialinequalitymultiplier; } state.gammak = 0.0; state.xkpresent = false; alglib.ap.assert(state.aulitscnt>0, "MinNLC: integrity check failed"); clearpreconditioner(state.auloptimizer); outerit = 0; lbl_3: if( outerit>state.aulitscnt-1 ) { goto lbl_5; } // // Optimize with current Lagrange multipliers // // NOTE: this code expects and checks that line search ends in the // point which is used as beginning for the next search. Such // guarantee is given by MCSRCH function. L-BFGS optimizer // does not formally guarantee it, but it follows same rule. // Below we a) rely on such property of the optimizer, and b) // assert that it is true, in order to fail loudly if it is // not true. // // NOTE: security check for NAN/INF in F/G is responsibility of // LBFGS optimizer. AUL optimizer checks for NAN/INF only // when we update Lagrange multipliers. // preccounter = 0; minlbfgs.minlbfgssetxrep(state.auloptimizer, true); minlbfgs.minlbfgsrestartfrom(state.auloptimizer, state.xc); lbl_6: if( !minlbfgs.minlbfgsiteration(state.auloptimizer) ) { goto lbl_7; } if( !state.auloptimizer.needfg ) { goto lbl_8; } // // Un-scale X, evaluate F/G/H, re-scale Jacobian // for(i=0; i<=n-1; i++) { state.x[i] = state.auloptimizer.x[i]*state.s[i]; } state.needfij = true; state.rstateaul.stage = 0; goto lbl_rcomm; lbl_0: state.needfij = false; for(i=0; i<=ng+nh; i++) { for(j=0; j<=n-1; j++) { state.j[i,j] = state.j[i,j]*state.s[j]; } } // // Store data for estimation of Hessian norm: // * current point (re-scaled) // * gradient of the target function (re-scaled, unmodified) // for(i_=0; i_<=n-1;i_++) { state.xk1[i_] = state.auloptimizer.x[i_]; } for(i_=0; i_<=n-1;i_++) { state.gk1[i_] = state.j[0,i_]; } // // Function being optimized // state.auloptimizer.f = state.fi[0]; for(i=0; i<=n-1; i++) { state.auloptimizer.g[i] = state.j[0,i]; } // // Penalty for violation of boundary/linear/nonlinear constraints // penaltybc(state.auloptimizer.x, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, state.nubc, n, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g); penaltylc(state.auloptimizer.x, state.scaledcleic, state.nulc, n, nec, nic, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g); penaltynlc(state.fi, state.j, state.nunlc, n, ng, nh, state.rho, state.stabilizingpoint, ref state.auloptimizer.f, state.auloptimizer.g); // // To optimizer // goto lbl_6; lbl_8: if( !state.auloptimizer.xupdated ) { goto lbl_10; } // // Report current point (if needed) // if( !state.xrep ) { goto lbl_12; } for(i=0; i<=n-1; i++) { state.x[i] = state.auloptimizer.x[i]*state.s[i]; } state.f = state.auloptimizer.f; state.xupdated = true; state.rstateaul.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_12: // // Update GammaK // if( state.xkpresent ) { // // XK/GK store beginning of current line search, and XK1/GK1 // store data for the end of the line search: // * first, we Assert() that XK1 (last point where function // was evaluated) is same as AULOptimizer.X (what is // reported by RComm interface // * calculate step length V2. // // If V2>HessEstTol, then: // * calculate V0 - directional derivative at XK, // and V1 - directional derivative at XK1 // * set GammaK to Max(GammaK, |V1-V0|/V2) // for(i=0; i<=n-1; i++) { alglib.ap.assert((double)(Math.Abs(state.auloptimizer.x[i]-state.xk1[i]))<=(double)(100*math.machineepsilon), "MinNLC: integrity check failed, unexpected behavior of LBFGS optimizer"); } v2 = 0.0; for(i=0; i<=n-1; i++) { v2 = v2+math.sqr(state.xk[i]-state.xk1[i]); } v2 = Math.Sqrt(v2); if( (double)(v2)>(double)(hessesttol) ) { v0 = 0.0; v1 = 0.0; for(i=0; i<=n-1; i++) { v = (state.xk[i]-state.xk1[i])/v2; v0 = v0+state.gk[i]*v; v1 = v1+state.gk1[i]*v; } state.gammak = Math.Max(state.gammak, Math.Abs(v1-v0)/v2); } } else { // // Beginning of the first line search, XK is not yet initialized. // for(i_=0; i_<=n-1;i_++) { state.xk[i_] = state.xk1[i_]; } for(i_=0; i_<=n-1;i_++) { state.gk[i_] = state.gk1[i_]; } state.xkpresent = true; } // // Update preconsitioner using current GammaK // updatepreconditioner(state.prectype, state.updatefreq, ref preccounter, state.auloptimizer, state.auloptimizer.x, state.rho, state.gammak, state.scaledbndl, state.hasbndl, state.scaledbndu, state.hasbndu, state.nubc, state.scaledcleic, state.nulc, state.fi, state.j, state.nunlc, ref state.bufd, ref state.bufc, ref state.bufw, n, nec, nic, ng, nh); goto lbl_6; lbl_10: alglib.ap.assert(false, "MinNLC: integrity check failed"); goto lbl_6; lbl_7: minlbfgs.minlbfgsresultsbuf(state.auloptimizer, ref state.xc, state.aulreport); state.repinneriterationscount = state.repinneriterationscount+state.aulreport.iterationscount; state.repnfev = state.repnfev+state.aulreport.nfev; state.repterminationtype = state.aulreport.terminationtype; apserv.inc(ref state.repouteriterationscount); if( state.repterminationtype<=0 ) { goto lbl_5; } // // 1. Evaluate F/J // 2. Check for NAN/INF in F/J: we just calculate sum of their // components, it should be enough to reduce vector/matrix to // just one value which either "normal" (all summands were "normal") // or NAN/INF (at least one summand was NAN/INF). // 3. Update Lagrange multipliers // for(i=0; i<=n-1; i++) { state.x[i] = state.xc[i]*state.s[i]; } state.needfij = true; state.rstateaul.stage = 2; goto lbl_rcomm; lbl_2: state.needfij = false; v = 0.0; for(i=0; i<=ng+nh; i++) { v = 0.1*v+state.fi[i]; for(j=0; j<=n-1; j++) { v = 0.1*v+state.j[i,j]; } } if( !math.isfinite(v) ) { // // Abnormal termination - infinities in function/gradient // state.repterminationtype = -8; result = false; return result; } for(i=0; i<=ng+nh; i++) { for(j=0; j<=n-1; j++) { state.j[i,j] = state.j[i,j]*state.s[j]; } } for(i=0; i<=n-1; i++) { // // Process coefficients corresponding to equality-type // constraints. // if( (state.hasbndl[i] && state.hasbndu[i]) && (double)(state.bndl[i])==(double)(state.bndu[i]) ) { minnlcequalitypenaltyfunction((state.xc[i]-state.scaledbndl[i])*state.rho, ref p, ref dp, ref d2p); state.nubc[2*i+0] = state.nubc[2*i+0]-dp; continue; } // // Process coefficients corresponding to inequality-type // constraints. These coefficients have limited growth/decay // per iteration which helps to stabilize algorithm. // alglib.ap.assert((double)(aulmaxgrowth)>(double)(1.0), "MinNLC: integrity error"); if( state.hasbndl[i] ) { minnlcinequalityshiftfunction((state.xc[i]-state.scaledbndl[i])*state.rho+1, ref p, ref dp, ref d2p); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nubc[2*i+0] = state.nubc[2*i+0]*v; } if( state.hasbndu[i] ) { minnlcinequalityshiftfunction((state.scaledbndu[i]-state.xc[i])*state.rho+1, ref p, ref dp, ref d2p); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nubc[2*i+1] = state.nubc[2*i+1]*v; } } for(i=0; i<=nec+nic-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.scaledcleic[i,i_]*state.xc[i_]; } v = v-state.scaledcleic[i,n]; if( i<nec ) { minnlcequalitypenaltyfunction(v*state.rho, ref p, ref dp, ref d2p); state.nulc[i] = state.nulc[i]-dp; } else { minnlcinequalityshiftfunction(-(v*state.rho)+1, ref p, ref dp, ref d2p); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nulc[i] = state.nulc[i]*v; } } for(i=1; i<=ng+nh; i++) { // // NOTE: loop index must start from 1, not zero! // v = state.fi[i]; if( i<=ng ) { minnlcequalitypenaltyfunction(v*state.rho, ref p, ref dp, ref d2p); state.nunlc[i-1] = state.nunlc[i-1]-dp; } else { minnlcinequalityshiftfunction(-(v*state.rho)+1, ref p, ref dp, ref d2p); v = Math.Abs(dp); v = Math.Min(v, aulmaxgrowth); v = Math.Max(v, 1/aulmaxgrowth); state.nunlc[i-1] = state.nunlc[i-1]*v; } } outerit = outerit+1; goto lbl_3; lbl_5: for(i=0; i<=n-1; i++) { state.xc[i] = state.xc[i]*state.s[i]; } result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstateaul.ia[0] = n; state.rstateaul.ia[1] = nec; state.rstateaul.ia[2] = nic; state.rstateaul.ia[3] = ng; state.rstateaul.ia[4] = nh; state.rstateaul.ia[5] = i; state.rstateaul.ia[6] = j; state.rstateaul.ia[7] = outerit; state.rstateaul.ia[8] = preccounter; state.rstateaul.ra[0] = v; state.rstateaul.ra[1] = vv; state.rstateaul.ra[2] = p; state.rstateaul.ra[3] = dp; state.rstateaul.ra[4] = d2p; state.rstateaul.ra[5] = v0; state.rstateaul.ra[6] = v1; state.rstateaul.ra[7] = v2; return result; }
public override alglib.apobject make_copy() { minnlcstate _result = new minnlcstate(); _result.stabilizingpoint = stabilizingpoint; _result.initialinequalitymultiplier = initialinequalitymultiplier; _result.solvertype = solvertype; _result.prectype = prectype; _result.updatefreq = updatefreq; _result.rho = rho; _result.n = n; _result.epsg = epsg; _result.epsf = epsf; _result.epsx = epsx; _result.maxits = maxits; _result.aulitscnt = aulitscnt; _result.xrep = xrep; _result.diffstep = diffstep; _result.teststep = teststep; _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.rstateaul = (rcommstate)rstateaul.make_copy(); _result.scaledbndl = (double[])scaledbndl.Clone(); _result.scaledbndu = (double[])scaledbndu.Clone(); _result.scaledcleic = (double[,])scaledcleic.Clone(); _result.xc = (double[])xc.Clone(); _result.xstart = (double[])xstart.Clone(); _result.xbase = (double[])xbase.Clone(); _result.fbase = (double[])fbase.Clone(); _result.dfbase = (double[])dfbase.Clone(); _result.fm2 = (double[])fm2.Clone(); _result.fm1 = (double[])fm1.Clone(); _result.fp1 = (double[])fp1.Clone(); _result.fp2 = (double[])fp2.Clone(); _result.dfm1 = (double[])dfm1.Clone(); _result.dfp1 = (double[])dfp1.Clone(); _result.bufd = (double[])bufd.Clone(); _result.bufc = (double[])bufc.Clone(); _result.bufw = (double[,])bufw.Clone(); _result.xk = (double[])xk.Clone(); _result.xk1 = (double[])xk1.Clone(); _result.gk = (double[])gk.Clone(); _result.gk1 = (double[])gk1.Clone(); _result.gammak = gammak; _result.xkpresent = xkpresent; _result.auloptimizer = (minlbfgs.minlbfgsstate)auloptimizer.make_copy(); _result.aulreport = (minlbfgs.minlbfgsreport)aulreport.make_copy(); _result.nubc = (double[])nubc.Clone(); _result.nulc = (double[])nulc.Clone(); _result.nunlc = (double[])nunlc.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repnfev = repnfev; _result.repvaridx = repvaridx; _result.repfuncidx = repfuncidx; _result.repterminationtype = repterminationtype; _result.repdbgphase0its = repdbgphase0its; return _result; }