/************************************************************************* 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 boundedstepandactivation(sactivesets.sactiveset sas, double[] xn, int n, ref double[] buf) { int result = 0; double stpmax = 0; int cidx = 0; double cval = 0; bool needact = new bool(); double v = 0; int i_ = 0; 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_] - sas.xc[i_]; } sactivesets.sasexploredirection(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_] + sas.xc[i_]; } result = sactivesets.sasmoveto(sas, buf, needact, cidx, cval); return result; }
/************************************************************************* 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 constrainedoptimum(sactivesets.sactiveset sas, cqmodels.convexquadraticmodel a, double anorm, double[] b, ref double[] xn, int n, 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 i_ = 0; // // Rebuild basis accroding to current active set. // We call SASRebuildBasis() to make sure that fields of SAS // store up to date values. // sactivesets.sasrebuildbasis(sas); // // Allocate temporaries. // apserv.rvectorsetlengthatleast(ref tmp, Math.Max(n, sas.basissize)); apserv.bvectorsetlengthatleast(ref tmpb, n); apserv.rvectorsetlengthatleast(ref lagrangec, sas.basissize); // // Prepare model // for(i=0; i<=sas.basissize-1; i++) { tmp[i] = sas.pbasis[i,n]; } theta = 100.0*anorm; for(i=0; i<=n-1; i++) { if( sas.activeset[i]>0 ) { tmpb[i] = true; } else { tmpb[i] = false; } } cqmodels.cqmsetactiveset(a, sas.xc, tmpb); cqmodels.cqmsetq(a, sas.pbasis, tmp, sas.basissize, theta); // // Iterate until optimal values of Lagrange multipliers are found // for(i=0; i<=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<=sas.basissize-1; i++) { v = lagrangec[i]; for(i_=0; i_<=n-1;i_++) { tmp[i_] = tmp[i_] - v*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<=sas.basissize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += sas.pbasis[i,i_]*xn[i_]; } feaserrnew = feaserrnew+math.sqr(v-sas.pbasis[i,n]); } feaserrnew = Math.Sqrt(feaserrnew); if( (double)(feaserrnew)>=(double)(0.2*feaserrold) ) { break; } // // Update Lagrange multipliers // for(i=0; i<=sas.basissize-1; i++) { v = 0.0; for(i_=0; i_<=n-1;i_++) { v += sas.pbasis[i,i_]*xn[i_]; } lagrangec[i] = lagrangec[i]-theta*(v-sas.pbasis[i,n]); } } return result; }
/************************************************************************* This function accepts quadratic model of the form f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b) and list of possible steps along direction D. It chooses best step (one which achieves minimum value of the target function) and moves current point (given by SAS object) to the new location. Step is bounded subject to boundary constraints. Candidate steps are divided into two groups: * "default" step, which is always performed when no candidate steps LONGER THAN THE DEFAULT ONE is given. This candidate MUST reduce target function value; it is responsibility of caller to provide default candidate which reduces target function. * "additional candidates", which may be shorter or longer than the default step. Candidates which are shorter that the default step are ignored; candidates which are longer than the "default" step are tested. The idea is that we ALWAYS try "default" step, and it is responsibility of the caller to provide us with something which is worth trying. This step may activate some constraint - that's why we stopped at "default" step size. However, we may also try longer steps which may activate additional constraints and further reduce function value. INPUT PARAMETERS: SState - structure which stores model SAS - active set structure which stores current point in SAS.XC D - direction for step Stp - step length for "default" candidate NeedAct - whether default candidate activates some constraint; if NeedAct is True, constraint given by CIdc/CVal is GUARANTEED to be activated in the final point. CIdx - if NeedAct is True, stores index of the constraint to activate CVal - if NeedAct is True, stores constrained value; SAS.XC[CIdx] is forced to be equal to CVal. AddSteps- array[AddStepsCnt] of additional steps: * AddSteps[]<=Stp are ignored * AddSteps[]>Stp are tried Activated- possibly preallocated buffer; previously allocated memory will be reused. Tmp0 - possibly preallocated buffer; previously allocated memory will be reused. OUTPUT PARAMETERS: SAS - SAS.XC is set to new point; if there was a constraint specified by NeedAct/CIdx/CVal, it will be activated (other constraints may be activated too, but this one is guaranteed to be active in the final point). Activated- elements of this array are set to True, if I-th constraint as inactive at previous point, but become active in the new one. Situations when we deactivate xi>=0 and activate xi<=1 are considered as activation of previously inactive constraint -- ALGLIB -- Copyright 14.05.2014 by Bochkanov Sergey *************************************************************************/ private static void findbeststepandmove(qqpbuffers sstate, sactivesets.sactiveset sas, double[] d, double stp, bool needact, int cidx, double cval, double[] addsteps, int addstepscnt, ref bool[] activated, ref double[] tmp0) { int n = 0; int i = 0; int k = 0; double v = 0; double stpbest = 0; double fbest = 0; double fcand = 0; n = sstate.n; apserv.rvectorsetlengthatleast(ref tmp0, n); apserv.bvectorsetlengthatleast(ref activated, n); // // Calculate initial step, store to Tmp0 // // NOTE: Tmp0 is guaranteed to be feasible w.r.t. boundary constraints // for(i=0; i<=n-1; i++) { v = sas.xc[i]+stp*d[i]; if( sstate.havebndl[i] && (double)(v)<(double)(sstate.bndl[i]) ) { v = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(v)>(double)(sstate.bndu[i]) ) { v = sstate.bndu[i]; } tmp0[i] = v; } if( needact ) { tmp0[cidx] = cval; } // // Try additional steps, if AddStepsCnt>0 // if( addstepscnt>0 ) { // // Find best step // stpbest = stp; fbest = projectedtargetfunction(sstate, sas.xc, d, stpbest, ref tmp0); for(k=0; k<=addstepscnt-1; k++) { if( (double)(addsteps[k])>(double)(stp) ) { fcand = projectedtargetfunction(sstate, sas.xc, d, addsteps[k], ref tmp0); if( (double)(fcand)<(double)(fbest) ) { fbest = fcand; stpbest = addsteps[k]; } } } // // Prepare best step // // NOTE: because only AddSteps[]>Stp were checked, // this step will activate constraint CIdx. // for(i=0; i<=n-1; i++) { v = sas.xc[i]+stpbest*d[i]; if( sstate.havebndl[i] && (double)(v)<(double)(sstate.bndl[i]) ) { v = sstate.bndl[i]; } if( sstate.havebndu[i] && (double)(v)>(double)(sstate.bndu[i]) ) { v = sstate.bndu[i]; } tmp0[i] = v; } if( needact ) { tmp0[cidx] = cval; } } // // Fill Activated array by information about activated constraints. // Perform step // for(i=0; i<=n-1; i++) { activated[i] = false; v = tmp0[i]; if( (double)(v)==(double)(sas.xc[i]) ) { continue; } if( sstate.havebndl[i] && (double)(v)==(double)(sstate.bndl[i]) ) { activated[i] = true; } if( sstate.havebndu[i] && (double)(v)==(double)(sstate.bndu[i]) ) { activated[i] = true; } } sactivesets.sasmoveto(sas, tmp0, needact, cidx, cval); }