/************************************************************************* Having feasible current point XC and possibly infeasible candidate point XN, this function performs longest step from XC to XN which retains feasibility. In case XN is found to be infeasible, at least one constraint is activated. For example, if we have: XC=0.5 XN=1.2 x>=0, x<=1 then this function will move us to X=1.0 and activate constraint "x<=1". INPUT PARAMETERS: State - MinQP state. XC - current point, must be feasible with respect to all constraints XN - candidate point, can be infeasible with respect to some constraints. Must be located in the subspace of current active set, i.e. it is feasible with respect to already active constraints. Buf - temporary buffer, automatically resized if needed OUTPUT PARAMETERS: State - this function changes following fields of State: * State.ActiveSet * State.ActiveC - active linear constraints XC - new position RESULT: >0, in case at least one inactive non-candidate constraint was activated =0, in case only "candidate" constraints were activated <0, in case no constraints were activated by the step -- ALGLIB -- Copyright 29.02.2012 by Bochkanov Sergey *************************************************************************/ private static int minqpboundedstepandactivation(minqpstate state, double[] xn, ref double[] buf) { int result = 0; int n = 0; double stpmax = 0; int cidx = 0; double cval = 0; bool needact = new bool(); double v = 0; int i_ = 0; n = state.n; apserv.rvectorsetlengthatleast(ref buf, n); for(i_=0; i_<=n-1;i_++) { buf[i_] = xn[i_]; } for(i_=0; i_<=n-1;i_++) { buf[i_] = buf[i_] - state.sas.xc[i_]; } sactivesets.sasexploredirection(state.sas, buf, ref stpmax, ref cidx, ref cval); needact = (double)(stpmax)<=(double)(1); v = Math.Min(stpmax, 1.0); for(i_=0; i_<=n-1;i_++) { buf[i_] = v*buf[i_]; } for(i_=0; i_<=n-1;i_++) { buf[i_] = buf[i_] + state.sas.xc[i_]; } result = sactivesets.sasmoveto(state.sas, buf, needact, cidx, cval); return result; }
/************************************************************************* Fast version of MinQPSetQuadraticTerm(), which doesn't check its arguments. It accepts additional parameter - shift S, which allows to "shift" matrix A by adding s*I to A. S must be positive (although it is not checked). For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadratictermfast(minqpstate state, double[,] a, bool isupper, double s) { int i = 0; int j = 0; int n = 0; double v = 0; int j0 = 0; int j1 = 0; n = state.n; state.akind = 0; cqmodels.cqmseta(state.a, a, isupper, 1.0); if( (double)(s)>(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.tmp0, n); for(i=0; i<=n-1; i++) { state.tmp0[i] = a[i,i]+s; } cqmodels.cqmrewritedensediagonal(state.a, state.tmp0); } // // Estimate norm of A // (it will be used later in the quadratic penalty function) // state.absamax = 0; state.absasum = 0; state.absasum2 = 0; for(i=0; i<=n-1; i++) { if( isupper ) { j0 = i; j1 = n-1; } else { j0 = 0; j1 = i; } for(j=j0; j<=j1; j++) { v = Math.Abs(a[i,j]); state.absamax = Math.Max(state.absamax, v); state.absasum = state.absasum+v; state.absasum2 = state.absasum2+v*v; } } }
/************************************************************************* Fast version of MinQPSetStartingPoint(), which doesn't check its arguments. For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetstartingpointfast(minqpstate state, double[] x) { int n = 0; int i_ = 0; n = state.n; for(i_=0; i_<=n-1;i_++) { state.startx[i_] = x[i_]; } state.havex = true; }
/************************************************************************* This function solves quadratic programming problem. Prior to calling this function you should choose solver by means of one of the following functions: * MinQPSetAlgoQuickQP() - for QuickQP solver * MinQPSetAlgoBLEIC() - for BLEIC-QP solver These functions also allow you to control stopping criteria of the solver. If you did not set solver, MinQP subpackage will automatically select solver for your problem and will run it with default stopping criteria. However, it is better to set explicitly solver and its stopping criteria. INPUT PARAMETERS: State - algorithm state You should use MinQPResults() function to access results after calls to this function. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey. Special thanks to Elvira Illarionova for important suggestions on the linearly constrained QP algorithm. *************************************************************************/ public static void minqpoptimize(minqpstate state) { int n = 0; int i = 0; int nbc = 0; int currentsolver = 0; n = state.n; state.repterminationtype = -5; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repncholesky = 0; state.repnmv = 0; // // check correctness of constraints // for(i=0; i<=n-1; i++) { if( state.havebndl[i] && state.havebndu[i] ) { if( (double)(state.bndl[i])>(double)(state.bndu[i]) ) { state.repterminationtype = -3; return; } } } // // count number of bound and linear constraints // nbc = 0; for(i=0; i<=n-1; i++) { if( state.havebndl[i] ) { nbc = nbc+1; } if( state.havebndu[i] ) { nbc = nbc+1; } } // // Initial point: // * if we have starting point in StartX, we just have to bound it // * if we do not have StartX, deduce initial point from boundary constraints // if( state.havex ) { for(i=0; i<=n-1; i++) { state.xs[i] = state.startx[i]; if( state.havebndl[i] && (double)(state.xs[i])<(double)(state.bndl[i]) ) { state.xs[i] = state.bndl[i]; } if( state.havebndu[i] && (double)(state.xs[i])>(double)(state.bndu[i]) ) { state.xs[i] = state.bndu[i]; } } } else { for(i=0; i<=n-1; i++) { if( state.havebndl[i] && state.havebndu[i] ) { state.xs[i] = 0.5*(state.bndl[i]+state.bndu[i]); continue; } if( state.havebndl[i] ) { state.xs[i] = state.bndl[i]; continue; } if( state.havebndu[i] ) { state.xs[i] = state.bndu[i]; continue; } state.xs[i] = 0; } } // // Choose solver - user-specified or default one. // currentsolver = state.algokind; if( currentsolver==0 ) { // // Choose solver automatically // if( state.nec+state.nic==0 ) { // // QQP solver is used for problems without linear constraints // currentsolver = 3; qqpsolver.qqploaddefaults(n, state.qqpsettingscurrent); } else { // // QP-BLEIC solver is used for problems without linear constraints // currentsolver = 2; qpbleicsolver.qpbleicloaddefaults(n, state.qpbleicsettingscurrent); } } else { alglib.ap.assert((currentsolver==1 || currentsolver==2) || currentsolver==3, "MinQPOptimize: internal error"); if( currentsolver==1 ) { } if( currentsolver==2 ) { // // QP-BLEIC solver is chosen by user // qpbleicsolver.qpbleiccopysettings(state.qpbleicsettingsuser, state.qpbleicsettingscurrent); } if( currentsolver==3 ) { // // QQP solver is chosen by user // qqpsolver.qqpcopysettings(state.qqpsettingsuser, state.qqpsettingscurrent); } } // // QP-BLEIC solver // if( currentsolver==2 ) { qpbleicsolver.qpbleicoptimize(state.a, state.sparsea, state.akind, state.sparseaupper, state.absasum, state.absasum2, state.b, state.bndl, state.bndu, state.s, state.xorigin, n, state.cleic, state.nec, state.nic, state.qpbleicsettingscurrent, state.qpbleicbuf, ref state.qpbleicfirstcall, ref state.xs, ref state.repterminationtype); state.repinneriterationscount = state.qpbleicbuf.repinneriterationscount; state.repouteriterationscount = state.qpbleicbuf.repouteriterationscount; return; } // // QuickQP solver // if( currentsolver==3 ) { if( state.nec+state.nic>0 ) { state.repterminationtype = -5; return; } qqpsolver.qqpoptimize(state.a, state.sparsea, state.akind, state.sparseaupper, state.b, state.bndl, state.bndu, state.s, state.xorigin, n, state.cleic, state.nec, state.nic, state.qqpsettingscurrent, state.qqpbuf, state.xs, ref state.repterminationtype); state.repinneriterationscount = state.qqpbuf.repinneriterationscount; state.repouteriterationscount = state.qqpbuf.repouteriterationscount; state.repncholesky = state.qqpbuf.repncholesky; return; } // // Cholesky solver. // if( currentsolver==1 ) { // // Check matrix type. // Cholesky solver supports only dense matrices. // if( state.akind!=0 ) { state.repterminationtype = -5; return; } qpcholeskysolver.qpcholeskyoptimize(state.a, state.absamax*n, state.b, state.bndl, state.bndu, state.s, state.xorigin, n, state.cleic, state.nec, state.nic, state.qpcholeskybuf, ref state.xs, ref state.repterminationtype); state.repinneriterationscount = state.qqpbuf.repinneriterationscount; state.repouteriterationscount = state.qqpbuf.repouteriterationscount; state.repncholesky = state.qqpbuf.repncholesky; return; } }
/************************************************************************* QP results Buffered implementation of MinQPResults() 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 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresultsbuf(minqpstate state, ref double[] x, minqpreport rep) { int i_ = 0; if( alglib.ap.len(x)<state.n ) { x = new double[state.n]; } for(i_=0; i_<=state.n-1;i_++) { x[i_] = state.xs[i_]; } rep.inneriterationscount = state.repinneriterationscount; rep.outeriterationscount = state.repouteriterationscount; rep.nmv = state.repnmv; rep.ncholesky = state.repncholesky; rep.terminationtype = state.repterminationtype; }
/************************************************************************* This function tells solver to use BLEIC-based algorithm and sets stopping criteria for the algorithm. ALGORITHM FEATURES: * supports dense and sparse QP problems * supports boundary and general linear equality/inequality constraints * can solve all types of problems (convex, semidefinite, nonconvex) as long as they are bounded from below under constraints. Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1". Of course, global minimum is found only for positive definite and semidefinite problems. As for indefinite ones - only local minimum is found. ALGORITHM OUTLINE: * BLEIC-QP solver is just a driver function for MinBLEIC solver; it solves quadratic programming problem as general linearly constrained optimization problem, which is solved by means of BLEIC solver (part of ALGLIB, active set method). ALGORITHM LIMITATIONS: * unlike QuickQP solver, this algorithm does not perform Newton steps and does not use Level 3 BLAS. Being general-purpose active set method, it can activate constraints only one-by-one. Thus, its performance is lower than that of QuickQP. * its precision is also a bit inferior to that of QuickQP. BLEIC-QP performs only LBFGS steps (no Newton steps), which are good at detecting neighborhood of the solution, buy need many iterations to find solution with more than 6 digits of precision. 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 constrained gradient vector, v[i]=g[i]*s[i] * g - gradient * s - scaling coefficients set by MinQPSetScale() EpsF - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| 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 MinQPSetScale() MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. NOTE: this algorithm uses LBFGS iterations, which are relatively cheap, but improve function value only a bit. So you will need many iterations to converge - from 0.1*N to 10*N, depending on problem's condition number. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgobleic(minqpstate state, double epsg, double epsf, double epsx, int maxits) { alglib.ap.assert(math.isfinite(epsg), "MinQPSetAlgoBLEIC: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinQPSetAlgoBLEIC: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinQPSetAlgoBLEIC: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinQPSetAlgoBLEIC: negative EpsX"); alglib.ap.assert(maxits>=0, "MinQPSetAlgoBLEIC: negative MaxIts!"); state.algokind = 2; if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxits==0 ) { epsx = 1.0E-6; } state.qpbleicsettingsuser.epsg = epsg; state.qpbleicsettingsuser.epsf = epsf; state.qpbleicsettingsuser.epsx = epsx; state.qpbleicsettingsuser.maxits = maxits; }
/************************************************************************* This function sets boundary constraints for QP solver Boundary constraints are inactive by default (after initial creation). After being set, they are preserved until explicitly turned off with another SetBC() call. 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 (latter is recommended because it will allow solver to use better algorithm). BndU - upper bounds, array[N]. If some (all) variables are unbounded, you may specify very large number or +INF (latter is recommended because it will allow solver to use better algorithm). NOTE: 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]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetbc(minqpstate state, double[] bndl, double[] bndu) { int i = 0; int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(bndl)>=n, "MinQPSetBC: Length(BndL)<N"); alglib.ap.assert(alglib.ap.len(bndu)>=n, "MinQPSetBC: Length(BndU)<N"); for(i=0; i<=n-1; i++) { alglib.ap.assert(math.isfinite(bndl[i]) || Double.IsNegativeInfinity(bndl[i]), "MinQPSetBC: BndL contains NAN or +INF"); alglib.ap.assert(math.isfinite(bndu[i]) || Double.IsPositiveInfinity(bndu[i]), "MinQPSetBC: BndU contains NAN or -INF"); state.bndl[i] = bndl[i]; state.havebndl[i] = math.isfinite(bndl[i]); state.bndu[i] = bndu[i]; state.havebndu[i] = math.isfinite(bndu[i]); } }
/************************************************************************* Fast version of MinQPSetQuadraticTerm(), which doesn't check its arguments. It accepts additional parameter - shift S, which allows to "shift" matrix A by adding s*I to A. S must be positive (although it is not checked). For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadratictermfast(minqpstate state, double[,] a, bool isupper, double s) { int k = 0; int n = 0; int i_ = 0; // // We store off-diagonal part of A in the lower triangle of DenseA. // Diagonal elements of A are stored in the DiagA. // Diagonal of DenseA and uppper triangle are used as temporaries. // // Why such complex storage? Because it: // 1. allows us to easily recover from exceptions (lower triangle // is unmodified during execution as well as DiagA, and on entry // we will always find unmodified matrix) // 2. allows us to make Cholesky decomposition in the upper triangle // of DenseA or to do other SPD-related operations. // n = state.n; state.akind = 0; apserv.rmatrixsetlengthatleast(ref state.densea, n, n); apserv.rvectorsetlengthatleast(ref state.diaga, n); if( isupper ) { for(k=0; k<=n-2; k++) { state.diaga[k] = a[k,k]+s; for(i_=k+1; i_<=n-1;i_++) { state.densea[i_,k] = a[k,i_]; } } state.diaga[n-1] = a[n-1,n-1]+s; } else { state.diaga[0] = a[0,0]+s; for(k=1; k<=n-1; k++) { for(i_=0; i_<=k-1;i_++) { state.densea[k,i_] = a[k,i_]; } state.diaga[k] = a[k,k]+s; } } }
/************************************************************************* Interna lfunction which allows to rewrite diagonal of quadratic term. For internal use only. This function can be used only when you have dense A and already made MinQPSetQuadraticTerm(Fast) call. -- ALGLIB -- Copyright 16.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqprewritediagonal(minqpstate state, double[] s) { int k = 0; int n = 0; ap.assert(state.akind==0, "MinQPRewriteDiagonal: internal error (AKind<>0)"); n = state.n; for(k=0; k<=n-1; k++) { state.diaga[k] = s[k]; } }
/************************************************************************* CONSTRAINED QUADRATIC PROGRAMMING The subroutine creates QP optimizer. After initial creation, it contains default optimization problem with zero quadratic and linear terms and no constraints. You should set quadratic/linear terms with calls to functions provided by MinQP subpackage. INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer with zero quadratic/linear terms and no constraints -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpcreate(int n, minqpstate state) { int i = 0; ap.assert(n>=1, "MinQPCreate: N<1"); // // initialize QP solver // state.n = n; state.akind = -1; state.repterminationtype = 0; state.b = new double[n]; state.bndl = new double[n]; state.bndu = new double[n]; state.workbndl = new double[n]; state.workbndu = new double[n]; state.havebndl = new bool[n]; state.havebndu = new bool[n]; state.startx = new double[n]; state.xorigin = new double[n]; state.xc = new double[n]; state.gc = new double[n]; for(i=0; i<=n-1; i++) { state.b[i] = 0.0; state.workbndl[i] = Double.NegativeInfinity; state.workbndu[i] = Double.PositiveInfinity; state.havebndl[i] = false; state.havebndu[i] = false; state.startx[i] = 0.0; state.xorigin[i] = 0.0; } state.havex = false; minqpsetalgocholesky(state); }
/************************************************************************* This function solves quadratic programming problem. You should call it after setting solver options with MinQPSet...() calls. INPUT PARAMETERS: State - algorithm state You should use MinQPResults() function to access results after calls to this function. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpoptimize(minqpstate state) { int n = 0; int i = 0; int j = 0; int k = 0; int nbc = 0; int nlc = 0; int nactive = 0; int nfree = 0; double f = 0; double fprev = 0; double v = 0; bool b = new bool(); int i_ = 0; n = state.n; state.repterminationtype = -5; state.repinneriterationscount = 0; state.repouteriterationscount = 0; state.repncholesky = 0; state.repnmv = 0; // // check correctness of constraints // for(i=0; i<=n-1; i++) { if( state.havebndl[i] & state.havebndu[i] ) { if( (double)(state.bndl[i])>(double)(state.bndu[i]) ) { state.repterminationtype = -3; return; } } } // // count number of bound and linear constraints // nbc = 0; nlc = 0; for(i=0; i<=n-1; i++) { if( state.havebndl[i] ) { nbc = nbc+1; } if( state.havebndu[i] ) { nbc = nbc+1; } } // // Our formulation of quadratic problem includes origin point, // i.e. we have F(x-x_origin) which is minimized subject to // constraints on x, instead of having simply F(x). // // Here we make transition from non-zero origin to zero one. // In order to make such transition we have to: // 1. subtract x_origin from x_start // 2. modify constraints // 3. solve problem // 4. add x_origin to solution // // There is alternate solution - to modify quadratic function // by expansion of multipliers containing (x-x_origin), but // we prefer to modify constraints, because it is a) more precise // and b) easier to to. // // Parts (1)-(2) are done here. After this block is over, // we have: // * XC, which stores shifted XStart (if we don't have XStart, // value of XC will be ignored later) // * WorkBndL, WorkBndU, which store modified boundary constraints. // for(i=0; i<=n-1; i++) { state.xc[i] = state.startx[i]-state.xorigin[i]; if( state.havebndl[i] ) { state.workbndl[i] = state.bndl[i]-state.xorigin[i]; } if( state.havebndu[i] ) { state.workbndu[i] = state.bndu[i]-state.xorigin[i]; } } // // modify starting point XC according to boundary constraints // if( state.havex ) { // // We have starting point in XC, so we just have to bound it // for(i=0; i<=n-1; i++) { if( state.havebndl[i] ) { if( (double)(state.xc[i])<(double)(state.workbndl[i]) ) { state.xc[i] = state.workbndl[i]; } } if( state.havebndu[i] ) { if( (double)(state.xc[i])>(double)(state.workbndu[i]) ) { state.xc[i] = state.workbndu[i]; } } } } else { // // We don't have starting point, so we deduce it from // constraints (if they are present). // // NOTE: XC contains some meaningless values from previous block // which are ignored by code below. // for(i=0; i<=n-1; i++) { if( state.havebndl[i] & state.havebndu[i] ) { state.xc[i] = 0.5*(state.workbndl[i]+state.workbndu[i]); if( (double)(state.xc[i])<(double)(state.workbndl[i]) ) { state.xc[i] = state.workbndl[i]; } if( (double)(state.xc[i])>(double)(state.workbndu[i]) ) { state.xc[i] = state.workbndu[i]; } continue; } if( state.havebndl[i] ) { state.xc[i] = state.workbndl[i]; continue; } if( state.havebndu[i] ) { state.xc[i] = state.workbndu[i]; continue; } state.xc[i] = 0; } } // // Select algo // if( state.algokind==1 & state.akind==0 ) { // // Cholesky-based algorithm for dense bound constrained problems. // // This algorithm exists in two variants: // * unconstrained one, which can solve problem using only one NxN // double matrix // * bound constrained one, which needs two NxN matrices // // We will try to solve problem using unconstrained algorithm, // and will use bound constrained version only when constraints // are actually present // if( nbc==0 & nlc==0 ) { // // "Simple" unconstrained version // apserv.rvectorsetlengthatleast(ref state.tmp0, n); apserv.rvectorsetlengthatleast(ref state.bufb, n); state.densea[0,0] = state.diaga[0]; for(k=1; k<=n-1; k++) { for(i_=0; i_<=k-1;i_++) { state.densea[i_,k] = state.densea[k,i_]; } state.densea[k,k] = state.diaga[k]; } for(i_=0; i_<=n-1;i_++) { state.bufb[i_] = state.b[i_]; } state.repncholesky = 1; if( !trfac.spdmatrixcholeskyrec(ref state.densea, 0, n, true, ref state.tmp0) ) { state.repterminationtype = -5; return; } fbls.fblscholeskysolve(state.densea, 1.0, n, true, ref state.bufb, ref state.tmp0); for(i_=0; i_<=n-1;i_++) { state.xc[i_] = -state.bufb[i_]; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xc[i_] + state.xorigin[i_]; } state.repouteriterationscount = 1; state.repterminationtype = 4; return; } // // General bound constrained algo // apserv.rmatrixsetlengthatleast(ref state.bufa, n, n); apserv.rvectorsetlengthatleast(ref state.bufb, n); apserv.rvectorsetlengthatleast(ref state.bufx, n); apserv.ivectorsetlengthatleast(ref state.activeconstraints, n); apserv.ivectorsetlengthatleast(ref state.prevactiveconstraints, n); apserv.rvectorsetlengthatleast(ref state.tmp0, n); // // Prepare constraints vectors: // * ActiveConstraints - constraints active at current step // * PrevActiveConstraints - constraints which were active at previous step // // Elements of constraints vectors can be: // * 0 - inactive // * 1 - active // * -1 - undefined (used to initialize PrevActiveConstraints before first iteration) // for(i=0; i<=n-1; i++) { state.prevactiveconstraints[i] = -1; } // // Main cycle // fprev = math.maxrealnumber; while( true ) { // // * calculate gradient at XC // * determine active constraints // * break if there is no free variables or // there were no changes in the list of active constraints // minqpgrad(state); nactive = 0; for(i=0; i<=n-1; i++) { state.activeconstraints[i] = 0; if( state.havebndl[i] ) { if( (double)(state.xc[i])<=(double)(state.workbndl[i]) & (double)(state.gc[i])>=(double)(0) ) { state.activeconstraints[i] = 1; } } if( state.havebndu[i] ) { if( (double)(state.xc[i])>=(double)(state.workbndu[i]) & (double)(state.gc[i])<=(double)(0) ) { state.activeconstraints[i] = 1; } } if( state.havebndl[i] & state.havebndu[i] ) { if( (double)(state.workbndl[i])==(double)(state.workbndu[i]) ) { state.activeconstraints[i] = 1; } } if( state.activeconstraints[i]>0 ) { nactive = nactive+1; } } nfree = n-nactive; if( nfree==0 ) { break; } b = false; for(i=0; i<=n-1; i++) { if( state.activeconstraints[i]!=state.prevactiveconstraints[i] ) { b = true; } } if( !b ) { break; } // // * copy A, B and X to buffer // * rearrange BufA, BufB and BufX, in such way that active variables come first, // inactive are moved to the tail. We use sorting subroutine // to solve this problem. // state.bufa[0,0] = state.diaga[0]; for(k=1; k<=n-1; k++) { for(i_=0; i_<=k-1;i_++) { state.bufa[k,i_] = state.densea[k,i_]; } for(i_=0; i_<=k-1;i_++) { state.bufa[i_,k] = state.densea[k,i_]; } state.bufa[k,k] = state.diaga[k]; } for(i_=0; i_<=n-1;i_++) { state.bufb[i_] = state.b[i_]; } for(i_=0; i_<=n-1;i_++) { state.bufx[i_] = state.xc[i_]; } for(i=0; i<=n-1; i++) { state.tmp0[i] = state.activeconstraints[i]; } tsort.tagsortbuf(ref state.tmp0, n, ref state.itmp0, ref state.p2, state.buf); for(k=0; k<=n-1; k++) { if( state.p2[k]!=k ) { v = state.bufb[k]; state.bufb[k] = state.bufb[state.p2[k]]; state.bufb[state.p2[k]] = v; v = state.bufx[k]; state.bufx[k] = state.bufx[state.p2[k]]; state.bufx[state.p2[k]] = v; } } for(i=0; i<=n-1; i++) { for(i_=0; i_<=n-1;i_++) { state.tmp0[i_] = state.bufa[i,i_]; } for(k=0; k<=n-1; k++) { if( state.p2[k]!=k ) { v = state.tmp0[k]; state.tmp0[k] = state.tmp0[state.p2[k]]; state.tmp0[state.p2[k]] = v; } } for(i_=0; i_<=n-1;i_++) { state.bufa[i,i_] = state.tmp0[i_]; } } for(i=0; i<=n-1; i++) { if( state.p2[i]!=i ) { for(i_=0; i_<=n-1;i_++) { state.tmp0[i_] = state.bufa[i,i_]; } for(i_=0; i_<=n-1;i_++) { state.bufa[i,i_] = state.bufa[state.p2[i],i_]; } for(i_=0; i_<=n-1;i_++) { state.bufa[state.p2[i],i_] = state.tmp0[i_]; } } } // // Now we have A and B in BufA and BufB, variables are rearranged // into two groups: Xf - free variables, Xc - active (fixed) variables, // and our quadratic problem can be written as // // ( Af Ac ) ( Xf ) ( Xf ) // F(X) = 0.5* ( Xf' Xc' ) * ( ) * ( ) + ( Bf' Bc' ) * ( ) // ( Ac' Acc ) ( Xc ) ( Xc ) // // we want to convert to the optimization with respect to Xf, // treating Xc as constant term. After expansion of expression above // we get // // F(Xf) = 0.5*Xf'*Af*Xf + (Bf+Ac*Xc)'*Xf + 0.5*Xc'*Acc*Xc // // We will update BufB using this expression and calculate // constant term. // ablas.rmatrixmv(nfree, nactive, state.bufa, 0, nfree, 0, state.bufx, nfree, ref state.tmp0, 0); for(i_=0; i_<=nfree-1;i_++) { state.bufb[i_] = state.bufb[i_] + state.tmp0[i_]; } state.constterm = 0.0; for(i=nfree; i<=n-1; i++) { state.constterm = state.constterm+0.5*state.bufx[i]*state.bufa[i,i]*state.bufx[i]; for(j=i+1; j<=n-1; j++) { state.constterm = state.constterm+state.bufx[i]*state.bufa[i,j]*state.bufx[j]; } } // // Now we are ready to minimize F(Xf)... // state.repncholesky = state.repncholesky+1; if( !trfac.spdmatrixcholeskyrec(ref state.bufa, 0, nfree, true, ref state.tmp0) ) { state.repterminationtype = -5; return; } fbls.fblscholeskysolve(state.bufa, 1.0, nfree, true, ref state.bufb, ref state.tmp0); for(i_=0; i_<=nfree-1;i_++) { state.bufx[i_] = -state.bufb[i_]; } // // ...and to copy results back to XC. // // It is done in several steps: // * original order of variables is restored // * result is copied back to XC // * XC is bounded with respect to bound constraints // for(k=n-1; k>=0; k--) { if( state.p2[k]!=k ) { v = state.bufx[k]; state.bufx[k] = state.bufx[state.p2[k]]; state.bufx[state.p2[k]] = v; } } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.bufx[i_]; } for(i=0; i<=n-1; i++) { if( state.havebndl[i] ) { if( (double)(state.xc[i])<(double)(state.workbndl[i]) ) { state.xc[i] = state.workbndl[i]; } } if( state.havebndu[i] ) { if( (double)(state.xc[i])>(double)(state.workbndu[i]) ) { state.xc[i] = state.workbndu[i]; } } } // // Calculate F, compare it with FPrev. // // Break if F>=FPrev // (sometimes possible at extremum due to numerical noise). // f = 0.0; for(i_=0; i_<=n-1;i_++) { f += state.b[i_]*state.xc[i_]; } f = f+minqpxtax(state, state.xc); if( (double)(f)>=(double)(fprev) ) { break; } fprev = f; // // Update PrevActiveConstraints // for(i=0; i<=n-1; i++) { state.prevactiveconstraints[i] = state.activeconstraints[i]; } // // Update report-related fields // state.repouteriterationscount = state.repouteriterationscount+1; } state.repterminationtype = 4; for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xc[i_] + state.xorigin[i_]; } return; } }
/************************************************************************* This function calculates x'*A*x for given X. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ private static double minqpxtax(minqpstate state, double[] x) { double result = 0; int n = 0; int i = 0; int j = 0; n = state.n; ap.assert(state.akind==-1 | state.akind==0, "MinQPXTAX: internal error"); result = 0; // // zero A // if( state.akind==-1 ) { result = 0.0; return result; } // // dense A // if( state.akind==0 ) { result = 0; for(i=0; i<=n-1; i++) { for(j=0; j<=i-1; j++) { result = result+state.densea[i,j]*x[i]*x[j]; } result = result+0.5*state.diaga[i]*math.sqr(x[i]); } return result; } return result; }
/************************************************************************* This function calculates gradient of quadratic function at XC and stores it in the GC. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ private static void minqpgrad(minqpstate state) { int n = 0; int i = 0; double v = 0; int i_ = 0; n = state.n; ap.assert(state.akind==-1 | state.akind==0, "MinQPGrad: internal error"); // // zero A // if( state.akind==-1 ) { for(i_=0; i_<=n-1;i_++) { state.gc[i_] = state.b[i_]; } return; } // // dense A // if( state.akind==0 ) { for(i_=0; i_<=n-1;i_++) { state.gc[i_] = state.b[i_]; } state.gc[0] = state.gc[0]+state.diaga[0]*state.xc[0]; for(i=1; i<=n-1; i++) { v = 0.0; for(i_=0; i_<=i-1;i_++) { v += state.densea[i,i_]*state.xc[i_]; } state.gc[i] = state.gc[i]+v+state.diaga[i]*state.xc[i]; v = state.xc[i]; for(i_=0; i_<=i-1;i_++) { state.gc[i_] = state.gc[i_] + v*state.densea[i,i_]; } } return; } }
/************************************************************************* Optimum of A subject to: a) active boundary constraints (given by ActiveSet[] and corresponding elements of XC) b) active linear constraints (given by C, R, LagrangeC) INPUT PARAMETERS: A - main quadratic term of the model; although structure may store linear and rank-K terms, these terms are ignored and rewritten by this function. ANorm - estimate of ||A|| (2-norm is used) B - array[N], linear term of the model XN - possibly preallocated buffer Tmp - temporary buffer (automatically resized) Tmp1 - temporary buffer (automatically resized) OUTPUT PARAMETERS: A - modified quadratic model (this function changes rank-K term and linear term of the model) LagrangeC- current estimate of the Lagrange coefficients XN - solution RESULT: True on success, False on failure (non-SPD model) -- ALGLIB -- Copyright 20.06.2012 by Bochkanov Sergey *************************************************************************/ private static bool minqpconstrainedoptimum(minqpstate state, cqmodels.convexquadraticmodel a, double anorm, double[] b, ref double[] xn, ref double[] tmp, ref bool[] tmpb, ref double[] lagrangec) { bool result = new bool(); int itidx = 0; int i = 0; double v = 0; double feaserrold = 0; double feaserrnew = 0; double theta = 0; int n = 0; int i_ = 0; n = state.n; // // Rebuild basis accroding to current active set. // We call SASRebuildBasis() to make sure that fields of SAS // store up to date values. // sactivesets.sasrebuildbasis(state.sas); // // Allocate temporaries. // apserv.rvectorsetlengthatleast(ref tmp, Math.Max(n, state.sas.basissize)); apserv.bvectorsetlengthatleast(ref tmpb, n); apserv.rvectorsetlengthatleast(ref lagrangec, state.sas.basissize); // // Prepare model // for(i=0; i<=state.sas.basissize-1; i++) { tmp[i] = state.sas.pbasis[i,n]; } theta = 100.0*anorm; for(i=0; i<=n-1; i++) { if( state.sas.activeset[i]>0 ) { tmpb[i] = true; } else { tmpb[i] = false; } } cqmodels.cqmsetactiveset(a, state.sas.xc, tmpb); cqmodels.cqmsetq(a, state.sas.pbasis, tmp, state.sas.basissize, theta); // // Iterate until optimal values of Lagrange multipliers are found // for(i=0; i<=state.sas.basissize-1; i++) { lagrangec[i] = 0; } feaserrnew = math.maxrealnumber; result = true; for(itidx=1; itidx<=maxlagrangeits; itidx++) { // // Generate right part B using linear term and current // estimate of the Lagrange multipliers. // for(i_=0; i_<=n-1;i_++) { tmp[i_] = b[i_]; } for(i=0; i<=state.sas.basissize-1; i++) { v = lagrangec[i]; for(i_=0; i_<=n-1;i_++) { tmp[i_] = tmp[i_] - v*state.sas.pbasis[i,i_]; } } cqmodels.cqmsetb(a, tmp); // // Solve // result = cqmodels.cqmconstrainedoptimum(a, ref xn); if( !result ) { return result; } // // Compare feasibility errors. // Terminate if error decreased too slowly. // feaserrold = feaserrnew; feaserrnew = 0; for(i=0; i<=state.sas.basissize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.sas.pbasis[i,i_]*xn[i_]; } feaserrnew = feaserrnew+math.sqr(v-state.sas.pbasis[i,n]); } feaserrnew = Math.Sqrt(feaserrnew); if( (double)(feaserrnew)>=(double)(0.2*feaserrold) ) { break; } // // Update Lagrange multipliers // for(i=0; i<=state.sas.basissize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += state.sas.pbasis[i,i_]*xn[i_]; } lagrangec[i] = lagrangec[i]-theta*(v-state.sas.pbasis[i,n]); } } return result; }
/************************************************************************* This function sets scaling coefficients. 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 BLEIC-based QP solver uses scale for two purposes: * to evaluate stopping conditions * for preconditioning of the underlying BLEIC solver 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 14.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetscale(minqpstate state, double[] s) { int i = 0; alglib.ap.assert(alglib.ap.len(s)>=state.n, "MinQPSetScale: Length(S)<N"); for(i=0; i<=state.n-1; i++) { alglib.ap.assert(math.isfinite(s[i]), "MinQPSetScale: S contains infinite or NAN elements"); alglib.ap.assert((double)(s[i])!=(double)(0), "MinQPSetScale: S contains zero elements"); state.s[i] = Math.Abs(s[i]); } }
public override alglib.apobject make_copy() { minqpstate _result = new minqpstate(); _result.n = n; _result.qqpsettingsuser = (qqpsolver.qqpsettings)qqpsettingsuser.make_copy(); _result.qqpsettingscurrent = (qqpsolver.qqpsettings)qqpsettingscurrent.make_copy(); _result.qpbleicsettingsuser = (qpbleicsolver.qpbleicsettings)qpbleicsettingsuser.make_copy(); _result.qpbleicsettingscurrent = (qpbleicsolver.qpbleicsettings)qpbleicsettingscurrent.make_copy(); _result.algokind = algokind; _result.akind = akind; _result.a = (cqmodels.convexquadraticmodel)a.make_copy(); _result.sparsea = (sparse.sparsematrix)sparsea.make_copy(); _result.sparseaupper = sparseaupper; _result.absamax = absamax; _result.absasum = absasum; _result.absasum2 = absasum2; _result.b = (double[])b.Clone(); _result.bndl = (double[])bndl.Clone(); _result.bndu = (double[])bndu.Clone(); _result.s = (double[])s.Clone(); _result.havebndl = (bool[])havebndl.Clone(); _result.havebndu = (bool[])havebndu.Clone(); _result.xorigin = (double[])xorigin.Clone(); _result.startx = (double[])startx.Clone(); _result.havex = havex; _result.cleic = (double[,])cleic.Clone(); _result.nec = nec; _result.nic = nic; _result.xs = (double[])xs.Clone(); _result.repinneriterationscount = repinneriterationscount; _result.repouteriterationscount = repouteriterationscount; _result.repncholesky = repncholesky; _result.repnmv = repnmv; _result.repterminationtype = repterminationtype; _result.tmp0 = (double[])tmp0.Clone(); _result.qpbleicfirstcall = qpbleicfirstcall; _result.qpbleicbuf = (qpbleicsolver.qpbleicbuffers)qpbleicbuf.make_copy(); _result.qqpbuf = (qqpsolver.qqpbuffers)qqpbuf.make_copy(); _result.qpcholeskybuf = (qpcholeskysolver.qpcholeskybuffers)qpcholeskybuf.make_copy(); _result.estimator = (normestimator.normestimatorstate)estimator.make_copy(); return _result; }
/************************************************************************* This function tells solver to use Cholesky-based algorithm. This algorithm was deprecated in ALGLIB 3.9.0 because its performance is inferior to that of BLEIC-QP or QuickQP on high-dimensional problems. Furthermore, it supports only dense convex QP problems. This solver is no longer active by default. We recommend you to switch to BLEIC-QP or QuickQP solver. INPUT PARAMETERS: State - structure which stores algorithm state -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgocholesky(minqpstate state) { state.algokind = 1; }
/************************************************************************* CONSTRAINED QUADRATIC PROGRAMMING The subroutine creates QP optimizer. After initial creation, it contains default optimization problem with zero quadratic and linear terms and no constraints. You should set quadratic/linear terms with calls to functions provided by MinQP subpackage. You should also choose appropriate QP solver and set it and its stopping criteria by means of MinQPSetAlgo??????() function. Then, you should start solution process by means of MinQPOptimize() call. Solution itself can be obtained with MinQPResults() function. INPUT PARAMETERS: N - problem size OUTPUT PARAMETERS: State - optimizer with zero quadratic/linear terms and no constraints -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpcreate(int n, minqpstate state) { int i = 0; alglib.ap.assert(n>=1, "MinQPCreate: N<1"); // // initialize QP solver // state.n = n; state.nec = 0; state.nic = 0; state.repterminationtype = 0; state.absamax = 1; state.absasum = 1; state.absasum2 = 1; state.akind = 0; state.sparseaupper = false; cqmodels.cqminit(n, state.a); state.b = new double[n]; state.bndl = new double[n]; state.bndu = new double[n]; state.havebndl = new bool[n]; state.havebndu = new bool[n]; state.s = new double[n]; state.startx = new double[n]; state.xorigin = new double[n]; state.xs = new double[n]; for(i=0; i<=n-1; i++) { state.bndl[i] = Double.NegativeInfinity; state.bndu[i] = Double.PositiveInfinity; state.havebndl[i] = false; state.havebndu[i] = false; state.b[i] = 0.0; state.startx[i] = 0.0; state.xorigin[i] = 0.0; state.s[i] = 1.0; } state.havex = false; minqpsetalgocholesky(state); normestimator.normestimatorcreate(n, n, 5, 5, state.estimator); qqpsolver.qqploaddefaults(n, state.qqpsettingsuser); qpbleicsolver.qpbleicloaddefaults(n, state.qpbleicsettingsuser); state.qpbleicfirstcall = true; }
/************************************************************************* This function tells solver to use QuickQP algorithm: special extra-fast algorithm for problems with boundary-only constrants. It may solve non-convex problems as long as they are bounded from below under constraints. ALGORITHM FEATURES: * many times (from 5x to 50x!) faster than BLEIC-based QP solver; utilizes accelerated methods for activation of constraints. * supports dense and sparse QP problems * supports ONLY boundary constraints; general linear constraints are NOT supported by this solver * can solve all types of problems (convex, semidefinite, nonconvex) as long as they are bounded from below under constraints. Say, it is possible to solve "min{-x^2} subject to -1<=x<=+1". In convex/semidefinite case global minimum is returned, in nonconvex case - algorithm returns one of the local minimums. ALGORITHM OUTLINE: * algorithm performs two kinds of iterations: constrained CG iterations and constrained Newton iterations * initially it performs small number of constrained CG iterations, which can efficiently activate/deactivate multiple constraints * after CG phase algorithm tries to calculate Cholesky decomposition and to perform several constrained Newton steps. If Cholesky decomposition failed (matrix is indefinite even under constraints), we perform more CG iterations until we converge to such set of constraints that system matrix becomes positive definite. Constrained Newton steps greatly increase convergence speed and precision. * algorithm interleaves CG and Newton iterations which allows to handle indefinite matrices (CG phase) and quickly converge after final set of constraints is found (Newton phase). Combination of CG and Newton phases is called "outer iteration". * it is possible to turn off Newton phase (beneficial for semidefinite problems - Cholesky decomposition will fail too often) ALGORITHM LIMITATIONS: * algorithm does not support general linear constraints; only boundary ones are supported * Cholesky decomposition for sparse problems is performed with Skyline Cholesky solver, which is intended for low-profile matrices. No profile- reducing reordering of variables is performed in this version of ALGLIB. * problems with near-zero negative eigenvalues (or exacty zero ones) may experience about 2-3x performance penalty. The reason is that Cholesky decomposition can not be performed until we identify directions of zero and negative curvature and activate corresponding boundary constraints - but we need a lot of trial and errors because these directions are hard to notice in the matrix spectrum. In this case you may turn off Newton phase of algorithm. Large negative eigenvalues are not an issue, so highly non-convex problems can be solved very efficiently. 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 constrained gradient vector, v[i]=g[i]*s[i] * g - gradient * s - scaling coefficients set by MinQPSetScale() EpsF - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: |F(k+1)-F(k)|<=EpsF*max{|F(k)|,|F(k+1)|,1} EpsX - >=0 The subroutine finishes its work if exploratory steepest descent step on k+1-th iteration satisfies following condition: * |.| 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 MinQPSetScale() MaxOuterIts-maximum number of OUTER iterations. One outer iteration includes some amount of CG iterations (from 5 to ~N) and one or several (usually small amount) Newton steps. Thus, one outer iteration has high cost, but can greatly reduce funcation value. UseNewton- use Newton phase or not: * Newton phase improves performance of positive definite dense problems (about 2 times improvement can be observed) * can result in some performance penalty on semidefinite or slightly negative definite problems - each Newton phase will bring no improvement (Cholesky failure), but still will require computational time. * if you doubt, you can turn off this phase - optimizer will retain its most of its high speed. IT IS VERY IMPORTANT TO CALL MinQPSetScale() WHEN YOU USE THIS ALGORITHM BECAUSE ITS STOPPING CRITERIA ARE SCALE-DEPENDENT! Passing EpsG=0, EpsF=0 and EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic stopping criterion selection (presently it is small step length, but it may change in the future versions of ALGLIB). -- ALGLIB -- Copyright 22.05.2014 by Bochkanov Sergey *************************************************************************/ public static void minqpsetalgoquickqp(minqpstate state, double epsg, double epsf, double epsx, int maxouterits, bool usenewton) { alglib.ap.assert(math.isfinite(epsg), "MinQPSetAlgoQuickQP: EpsG is not finite number"); alglib.ap.assert((double)(epsg)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsG"); alglib.ap.assert(math.isfinite(epsf), "MinQPSetAlgoQuickQP: EpsF is not finite number"); alglib.ap.assert((double)(epsf)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsF"); alglib.ap.assert(math.isfinite(epsx), "MinQPSetAlgoQuickQP: EpsX is not finite number"); alglib.ap.assert((double)(epsx)>=(double)(0), "MinQPSetAlgoQuickQP: negative EpsX"); alglib.ap.assert(maxouterits>=0, "MinQPSetAlgoQuickQP: negative MaxOuterIts!"); state.algokind = 3; if( (((double)(epsg)==(double)(0) && (double)(epsf)==(double)(0)) && (double)(epsx)==(double)(0)) && maxouterits==0 ) { epsx = 1.0E-6; } state.qqpsettingsuser.maxouterits = maxouterits; state.qqpsettingsuser.epsg = epsg; state.qqpsettingsuser.epsf = epsf; state.qqpsettingsuser.epsx = epsx; state.qqpsettingsuser.cnphase = usenewton; }
/************************************************************************* This function sets linear term for QP solver. By default, linear term is zero. INPUT PARAMETERS: State - structure which stores algorithm state B - linear term, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlinearterm(minqpstate state, double[] b) { int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(b)>=n, "MinQPSetLinearTerm: Length(B)<N"); alglib.ap.assert(apserv.isfinitevector(b, n), "MinQPSetLinearTerm: B contains infinite or NaN elements"); minqpsetlineartermfast(state, b); }
/************************************************************************* This function sets linear constraints for QP optimizer. Linear constraints are inactive by default (after initial creation). INPUT PARAMETERS: State - structure previously allocated with MinQPCreate 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: linear (non-bound) constraints are satisfied only approximately - there always exists some minor violation (about 10^-10...10^-13) due to numerical errors. -- ALGLIB -- Copyright 19.06.2012 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlc(minqpstate state, double[,] c, int[] ct, int k) { int n = 0; int i = 0; int j = 0; double v = 0; int i_ = 0; n = state.n; // // First, check for errors in the inputs // alglib.ap.assert(k>=0, "MinQPSetLC: K<0"); alglib.ap.assert(alglib.ap.cols(c)>=n+1 || k==0, "MinQPSetLC: Cols(C)<N+1"); alglib.ap.assert(alglib.ap.rows(c)>=k, "MinQPSetLC: Rows(C)<K"); alglib.ap.assert(alglib.ap.len(ct)>=k, "MinQPSetLC: Length(CT)<K"); alglib.ap.assert(apserv.apservisfinitematrix(c, k, n+1), "MinQPSetLC: 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; } } // // Normalize rows of State.CLEIC: each row must have unit norm. // Norm is calculated using first N elements (i.e. right part is // not counted when we calculate norm). // for(i=0; i<=k-1; i++) { v = 0; for(j=0; j<=n-1; j++) { v = v+math.sqr(state.cleic[i,j]); } if( (double)(v)==(double)(0) ) { continue; } v = 1/Math.Sqrt(v); for(i_=0; i_<=n;i_++) { state.cleic[i,i_] = v*state.cleic[i,i_]; } } }
/************************************************************************* This function sets dense quadratic term for QP solver. By default, quadratic term is zero. SUPPORT BY ALGLIB QP ALGORITHMS: Dense quadratic term can be handled by any of the QP algorithms supported by ALGLIB QP Solver. IMPORTANT: This solver minimizes following function: f(x) = 0.5*x'*A*x + b'*x. Note that quadratic term has 0.5 before it. So if you want to minimize f(x) = x^2 + x you should rewrite your problem as follows: f(x) = 0.5*(2*x^2) + x and your matrix A will be equal to [[2.0]], not to [[1.0]] INPUT PARAMETERS: State - structure which stores algorithm state A - matrix, array[N,N] IsUpper - (optional) storage type: * if True, symmetric matrix A is given by its upper triangle, and the lower triangle isn’t used * if False, symmetric matrix A is given by its lower triangle, and the upper triangle isn’t used * if not given, both lower and upper triangles must be filled. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadraticterm(minqpstate state, double[,] a, bool isupper) { int n = 0; n = state.n; alglib.ap.assert(alglib.ap.rows(a)>=n, "MinQPSetQuadraticTerm: Rows(A)<N"); alglib.ap.assert(alglib.ap.cols(a)>=n, "MinQPSetQuadraticTerm: Cols(A)<N"); alglib.ap.assert(apserv.isfinitertrmatrix(a, n, isupper), "MinQPSetQuadraticTerm: A contains infinite or NaN elements"); minqpsetquadratictermfast(state, a, isupper, 0.0); }
/************************************************************************* QP solver results INPUT PARAMETERS: State - algorithm state OUTPUT PARAMETERS: X - array[0..N-1], solution. This array is allocated and initialized only when Rep.TerminationType parameter is positive (success). Rep - optimization report. You should check Rep.TerminationType, which contains completion code, and you may check another fields which contain another information about algorithm functioning. Failure codes returned by algorithm are: * -5 inappropriate solver was used: * Cholesky solver for (semi)indefinite problems * Cholesky solver for problems with sparse matrix * QuickQP solver for problem with general linear constraints * -4 BLEIC-QP/QuickQP solver found unconstrained direction of negative curvature (function is unbounded from below even under constraints), no meaningful minimum can be found. * -3 inconsistent constraints (or maybe feasible point is too hard to find). If you are sure that constraints are feasible, try to restart optimizer with better initial approximation. Completion codes specific for Cholesky algorithm: * 4 successful completion Completion codes specific for BLEIC/QuickQP algorithms: * 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 -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpresults(minqpstate state, ref double[] x, minqpreport rep) { x = new double[0]; minqpresultsbuf(state, ref x, rep); }
/************************************************************************* This function sets sparse quadratic term for QP solver. By default, quadratic term is zero. IMPORTANT: This solver minimizes following function: f(x) = 0.5*x'*A*x + b'*x. Note that quadratic term has 0.5 before it. So if you want to minimize f(x) = x^2 + x you should rewrite your problem as follows: f(x) = 0.5*(2*x^2) + x and your matrix A will be equal to [[2.0]], not to [[1.0]] INPUT PARAMETERS: State - structure which stores algorithm state A - matrix, array[N,N] IsUpper - (optional) storage type: * if True, symmetric matrix A is given by its upper triangle, and the lower triangle isn’t used * if False, symmetric matrix A is given by its lower triangle, and the upper triangle isn’t used * if not given, both lower and upper triangles must be filled. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadratictermsparse(minqpstate state, sparse.sparsematrix a, bool isupper) { int n = 0; int t0 = 0; int t1 = 0; int i = 0; int j = 0; double v = 0; n = state.n; alglib.ap.assert(sparse.sparsegetnrows(a)==n, "MinQPSetQuadraticTermSparse: Rows(A)<>N"); alglib.ap.assert(sparse.sparsegetncols(a)==n, "MinQPSetQuadraticTermSparse: Cols(A)<>N"); sparse.sparsecopytocrsbuf(a, state.sparsea); state.sparseaupper = isupper; state.akind = 1; // // Estimate norm of A // (it will be used later in the quadratic penalty function) // state.absamax = 0; state.absasum = 0; state.absasum2 = 0; t0 = 0; t1 = 0; while( sparse.sparseenumerate(a, ref t0, ref t1, ref i, ref j, ref v) ) { if( i==j ) { // // Diagonal terms are counted only once // state.absamax = Math.Max(state.absamax, v); state.absasum = state.absasum+v; state.absasum2 = state.absasum2+v*v; } if( (j>i && isupper) || (j<i && !isupper) ) { // // Offdiagonal terms are counted twice // state.absamax = Math.Max(state.absamax, v); state.absasum = state.absasum+2*v; state.absasum2 = state.absasum2+2*v*v; } } }
/************************************************************************* Fast version of MinQPSetLinearTerm(), which doesn't check its arguments. For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetlineartermfast(minqpstate state, double[] b) { int i_ = 0; for(i_=0; i_<=state.n-1;i_++) { state.b[i_] = b[i_]; } }
/************************************************************************* This function sets starting point for QP solver. It is useful to have good initial approximation to the solution, because it will increase speed of convergence and identification of active constraints. INPUT PARAMETERS: State - structure which stores algorithm state X - starting point, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetstartingpoint(minqpstate state, double[] x) { int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(x)>=n, "MinQPSetStartingPoint: Length(B)<N"); alglib.ap.assert(apserv.isfinitevector(x, n), "MinQPSetStartingPoint: X contains infinite or NaN elements"); minqpsetstartingpointfast(state, x); }
/************************************************************************* Internal function which allows to rewrite diagonal of quadratic term. For internal use only. This function can be used only when you have dense A and already made MinQPSetQuadraticTerm(Fast) call. -- ALGLIB -- Copyright 16.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqprewritediagonal(minqpstate state, double[] s) { cqmodels.cqmrewritedensediagonal(state.a, s); }
/************************************************************************* This function sets origin for QP solver. By default, following QP program is solved: min(0.5*x'*A*x+b'*x) This function allows to solve different problem: min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin)) INPUT PARAMETERS: State - structure which stores algorithm state XOrigin - origin, array[N]. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetorigin(minqpstate state, double[] xorigin) { int n = 0; n = state.n; alglib.ap.assert(alglib.ap.len(xorigin)>=n, "MinQPSetOrigin: Length(B)<N"); alglib.ap.assert(apserv.isfinitevector(xorigin, n), "MinQPSetOrigin: B contains infinite or NaN elements"); minqpsetoriginfast(state, xorigin); }
/************************************************************************* Fast version of MinQPSetOrigin(), which doesn't check its arguments. For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetoriginfast(minqpstate state, double[] xorigin) { int n = 0; int i_ = 0; n = state.n; for(i_=0; i_<=n-1;i_++) { state.xorigin[i_] = xorigin[i_]; } }
/************************************************************************* Fast version of MinQPSetQuadraticTerm(), which doesn't check its arguments. It accepts additional parameter - shift S, which allows to "shift" matrix A by adding s*I to A. S must be positive (although it is not checked). For internal use only. -- ALGLIB -- Copyright 11.01.2011 by Bochkanov Sergey *************************************************************************/ public static void minqpsetquadratictermfast(minqpstate state, double[,] a, bool isupper, double s) { int i = 0; int j = 0; int n = 0; n = state.n; cqmodels.cqmseta(state.a, a, isupper, 1.0); if( (double)(s)>(double)(0) ) { apserv.rvectorsetlengthatleast(ref state.tmp0, n); for(i=0; i<=n-1; i++) { state.tmp0[i] = a[i,i]+s; } cqmodels.cqmrewritedensediagonal(state.a, state.tmp0); } // // Estimate norm of A // (it will be used later in the quadratic penalty function) // state.anorm = 0; for(i=0; i<=n-1; i++) { if( isupper ) { for(j=i; j<=n-1; j++) { state.anorm = Math.Max(state.anorm, Math.Abs(a[i,j])); } } else { for(j=0; j<=i; j++) { state.anorm = Math.Max(state.anorm, Math.Abs(a[i,j])); } } } state.anorm = state.anorm*n; }