public override void init() { s = new double[0]; bndl = new double[0]; bndu = new double[0]; hasbndl = new bool[0]; hasbndu = new bool[0]; cleic = new double[0,0]; x = new double[0]; fi = new double[0]; j = new double[0,0]; rstate = new rcommstate(); rstateags = new rcommstate(); agsrs = new hqrnd.hqrndstate(); xstart = new double[0]; xc = new double[0]; xn = new double[0]; grs = new double[0]; d = new double[0]; colmax = new double[0]; diagh = new double[0]; signmin = new double[0]; signmax = new double[0]; scaledbndl = new double[0]; scaledbndu = new double[0]; scaledcleic = new double[0,0]; rholinear = new double[0]; samplex = new double[0,0]; samplegm = new double[0,0]; samplegmbc = new double[0,0]; samplef = new double[0]; samplef0 = new double[0]; nsqp = new minnsqp(); tmp0 = new double[0]; tmp1 = new double[0]; tmp2 = new double[0,0]; tmp3 = new int[0]; xbase = new double[0]; fp = new double[0]; fm = new double[0]; }
/************************************************************************* This function solves QP problem of the form [ ] min [ 0.5*c'*(G*inv(H)*G')*c ] s.t. c[i]>=0, SUM(c[i])=1.0 [ ] where G is stored in SampleG[] array, diagonal H is stored in DiagH[]. DbgNCholesky is incremented every time we perform Cholesky decomposition. -- ALGLIB -- Copyright 02.06.2015 by Bochkanov Sergey *************************************************************************/ private static void solveqp(double[,] sampleg, double[] diagh, int nsample, int nvars, ref double[] coeffs, ref int dbgncholesky, minnsqp state) { int i = 0; int j = 0; int k = 0; double v = 0; double vv = 0; int n = 0; int nr = 0; int idx0 = 0; int idx1 = 0; int ncandbnd = 0; int innerits = 0; int outerits = 0; double dnrm = 0; double stp = 0; double stpmax = 0; int actidx = 0; double dtol = 0; bool kickneeded = new bool(); bool terminationneeded = new bool(); double kicklength = 0; double lambdav = 0; double maxdiag = 0; bool wasactivation = new bool(); bool werechanges = new bool(); int termcnt = 0; int i_ = 0; n = nsample; nr = nvars; // // Allocate arrays, prepare data // apserv.rvectorsetlengthatleast(ref coeffs, n); apserv.rvectorsetlengthatleast(ref state.xc, n); apserv.rvectorsetlengthatleast(ref state.xn, n); apserv.rvectorsetlengthatleast(ref state.x0, n); apserv.rvectorsetlengthatleast(ref state.gc, n); apserv.rvectorsetlengthatleast(ref state.d, n); apserv.rmatrixsetlengthatleast(ref state.uh, n, n); apserv.rmatrixsetlengthatleast(ref state.ch, n, n); apserv.rmatrixsetlengthatleast(ref state.rk, nsample, nvars); apserv.rvectorsetlengthatleast(ref state.invutc, n); apserv.rvectorsetlengthatleast(ref state.tmp0, n); apserv.bvectorsetlengthatleast(ref state.tmpb, n); for(i=0; i<=n-1; i++) { state.xc[i] = 1.0/n; coeffs[i] = 1.0/n; } for(i=0; i<=nsample-1; i++) { for(j=0; j<=nvars-1; j++) { state.rk[i,j] = sampleg[i,j]/Math.Sqrt(diagh[j]); } } ablas.rmatrixsyrk(nsample, nvars, 1.0, state.rk, 0, 0, 0, 0.0, state.uh, 0, 0, true); maxdiag = 0.0; for(i=0; i<=nsample-1; i++) { maxdiag = Math.Max(maxdiag, state.uh[i,i]); } maxdiag = apserv.coalesce(maxdiag, 1.0); // // Main cycle: // innerits = 0; outerits = 0; dtol = 1.0E5*math.machineepsilon; kicklength = math.machineepsilon; lambdav = 1.0E5*math.machineepsilon; terminationneeded = false; termcnt = 0; while( true ) { // // Save current point to X0 // for(i_=0; i_<=n-1;i_++) { state.x0[i_] = state.xc[i_]; } // // Calculate gradient at initial point, solve NNLS problem // to determine descent direction D subject to constraints. // // In order to do so we solve following constrained // minimization problem: // ( )^2 // min ( SUM(lambda[i]*A[i]) + G ) // ( ) // Here: // * G is a gradient (column vector) // * A[i] is a column vector of I-th constraint // * lambda[i] is a Lagrange multiplier corresponding to I-th constraint // // NOTE: all A[i] except for last one have only one element being set, // so we rely on sparse capabilities of NNLS solver. However, // in order to use these capabilities we have to reorder variables // in such way that sparse ones come first. // // After finding lambda[] coefficients, we can find constrained descent // direction by subtracting lambda[i]*A[i] from D=-G. We make use of the // fact that first NCandBnd columns are just columns of identity matrix, // so we can perform exact projection by explicitly setting elements of D // to zeros. // qpcalculategradfunc(sampleg, diagh, nsample, nvars, state.xc, ref state.gc, ref state.fc, ref state.tmp0); apserv.ivectorsetlengthatleast(ref state.tmpidx, n); apserv.rvectorsetlengthatleast(ref state.tmpd, n); apserv.rmatrixsetlengthatleast(ref state.tmpc2, n, 1); idx0 = 0; ncandbnd = 0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) ) { ncandbnd = ncandbnd+1; } } idx1 = ncandbnd; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) ) { // // Candidate for activation of boundary constraint, // comes first. // // NOTE: multiplication by -1 is due to the fact that // it is lower bound, and has specific direction // of constraint gradient. // state.tmpidx[idx0] = i; state.tmpd[idx0] = -state.gc[i]*-1; state.tmpc2[idx0,0] = 1.0*-1; idx0 = idx0+1; } else { // // We are far away from boundary. // state.tmpidx[idx1] = i; state.tmpd[idx1] = -state.gc[i]; state.tmpc2[idx1,0] = 1.0; idx1 = idx1+1; } } alglib.ap.assert(idx0==ncandbnd, "MinNSQP: integrity check failed"); alglib.ap.assert(idx1==n, "MinNSQP: integrity check failed"); snnls.snnlsinit(n, 1, n, state.nnls); snnls.snnlssetproblem(state.nnls, state.tmpc2, state.tmpd, ncandbnd, 1, n); snnls.snnlsdropnnc(state.nnls, ncandbnd); snnls.snnlssolve(state.nnls, ref state.tmplambdas); for(i=0; i<=n-1; i++) { state.d[i] = -state.gc[i]-state.tmplambdas[ncandbnd]; } for(i=0; i<=ncandbnd-1; i++) { if( (double)(state.tmplambdas[i])>(double)(0) ) { state.d[state.tmpidx[i]] = 0.0; } } // // Additional stage to "polish" D (improve situation // with sum-to-one constraint and boundary constraints) // and to perform additional integrity check. // // After this stage we are pretty sure that: // * if x[i]=0.0, then d[i]>=0.0 // * if d[i]<0.0, then x[i]>0.0 // v = 0.0; vv = 0.0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])<(double)(0.0) ) { state.d[i] = 0.0; } v = v+state.d[i]; vv = Math.Max(vv, Math.Abs(state.gc[i])); } alglib.ap.assert((double)(Math.Abs(v))<(double)(1.0E5*Math.Sqrt(n)*math.machineepsilon*Math.Max(vv, 1.0)), "MinNSQP: integrity check failed"); // // Decide whether we need "kick" stage: special stage // that moves us away from boundary constraints which are // not strictly active (i.e. such constraints that x[i]=0.0 and d[i]>0). // // If we need kick stage, we make a kick - and restart iteration. // If not, after this block we can rely on the fact that // for all x[i]=0.0 we have d[i]=0.0 // kickneeded = false; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])>(double)(0.0) ) { kickneeded = true; } } if( kickneeded ) { // // Perform kick. // Restart. // Do not increase outer iterations counter. // v = 0.0; for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])==(double)(0.0) && (double)(state.d[i])>(double)(0.0) ) { state.xc[i] = state.xc[i]+kicklength; } v = v+state.xc[i]; } alglib.ap.assert((double)(v)>(double)(0.0), "MinNSQP: integrity check failed"); for(i=0; i<=n-1; i++) { state.xc[i] = state.xc[i]/v; } apserv.inc(ref innerits); continue; } // // Calculate Cholesky decomposition of constrained Hessian // for Newton phase. // while( true ) { for(i=0; i<=n-1; i++) { // // Diagonal element // if( (double)(state.xc[i])>(double)(0.0) ) { state.ch[i,i] = state.uh[i,i]+lambdav*maxdiag; } else { state.ch[i,i] = 1.0; } // // Offdiagonal elements // for(j=i+1; j<=n-1; j++) { if( (double)(state.xc[i])>(double)(0.0) && (double)(state.xc[j])>(double)(0.0) ) { state.ch[i,j] = state.uh[i,j]; } else { state.ch[i,j] = 0.0; } } } apserv.inc(ref dbgncholesky); if( !trfac.spdmatrixcholeskyrec(ref state.ch, 0, n, true, ref state.tmp0) ) { // // Cholesky decomposition failed. // Increase LambdaV and repeat iteration. // Do not increase outer iterations counter. // lambdav = lambdav*10; continue; } break; } // // Newton phase // while( true ) { // // Calculate constrained (equality and sum-to-one) descent direction D. // // Here we use Sherman-Morrison update to calculate direction subject to // sum-to-one constraint. // qpcalculategradfunc(sampleg, diagh, nsample, nvars, state.xc, ref state.gc, ref state.fc, ref state.tmp0); for(i=0; i<=n-1; i++) { if( (double)(state.xc[i])>(double)(0.0) ) { state.invutc[i] = 1.0; state.d[i] = -state.gc[i]; } else { state.invutc[i] = 0.0; state.d[i] = 0.0; } } qpsolveut(state.ch, n, state.invutc); qpsolveut(state.ch, n, state.d); v = 0.0; vv = 0.0; for(i=0; i<=n-1; i++) { vv = vv+math.sqr(state.invutc[i]); v = v+state.invutc[i]*state.d[i]; } for(i=0; i<=n-1; i++) { state.d[i] = state.d[i]-v/vv*state.invutc[i]; } qpsolveu(state.ch, n, state.d); v = 0.0; k = 0; for(i=0; i<=n-1; i++) { v = v+state.d[i]; if( (double)(state.d[i])!=(double)(0.0) ) { k = k+1; } } if( k>0 && (double)(v)>(double)(0.0) ) { vv = v/k; for(i=0; i<=n-1; i++) { if( (double)(state.d[i])!=(double)(0.0) ) { state.d[i] = state.d[i]-vv; } } } // // Calculate length of D, maximum step and component which is // activated by this step. // // Break if D is exactly zero. We do not break here if DNrm is // small - this check is performed later. It is important to // perform last step with nearly-zero D, it allows us to have // extra-precision in solution which is often needed for convergence // of AGS algorithm. // dnrm = 0.0; for(i=0; i<=n-1; i++) { dnrm = dnrm+math.sqr(state.d[i]); } dnrm = Math.Sqrt(dnrm); actidx = -1; stpmax = 1.0E50; for(i=0; i<=n-1; i++) { if( (double)(state.d[i])<(double)(0.0) ) { v = stpmax; stpmax = apserv.safeminposrv(state.xc[i], -state.d[i], stpmax); if( (double)(stpmax)<(double)(v) ) { actidx = i; } } } if( (double)(dnrm)==(double)(0.0) ) { break; } // // Calculate trial function value at unconstrained full step. // If trial value is greater or equal to FC, terminate iterations. // for(i=0; i<=n-1; i++) { state.xn[i] = state.xc[i]+1.0*state.d[i]; } qpcalculatefunc(sampleg, diagh, nsample, nvars, state.xn, ref state.fn, ref state.tmp0); if( (double)(state.fn)>=(double)(state.fc) ) { break; } // // Perform step // Update Hessian // Update XC // // Break if: // a) no constraint was activated // b) norm of D is small enough // stp = Math.Min(1.0, stpmax); for(i=0; i<=n-1; i++) { state.xn[i] = Math.Max(state.xc[i]+stp*state.d[i], 0.0); } if( (double)(stp)==(double)(stpmax) && actidx>=0 ) { state.xn[actidx] = 0.0; } wasactivation = false; for(i=0; i<=n-1; i++) { state.tmpb[i] = (double)(state.xn[i])==(double)(0.0) && (double)(state.xc[i])!=(double)(0.0); wasactivation = wasactivation || state.tmpb[i]; } for(i_=0; i_<=n-1;i_++) { state.xc[i_] = state.xn[i_]; } if( !wasactivation ) { break; } if( (double)(dnrm)<=(double)(dtol) ) { break; } trfac.spdmatrixcholeskyupdatefixbuf(state.ch, n, true, state.tmpb, ref state.tmp0); } // // Compare status of boundary constraints - if nothing changed during // last outer iteration, TermCnt is increased. Otherwise it is reset // to zero. // // When TermCnt is large enough, we terminate algorithm. // werechanges = false; for(i=0; i<=n-1; i++) { werechanges = werechanges || Math.Sign(state.x0[i])!=Math.Sign(state.xc[i]); } if( !werechanges ) { apserv.inc(ref termcnt); } else { termcnt = 0; } if( termcnt>=2 ) { break; } // // Increase number of outer iterations. // Break if we performed too many. // apserv.inc(ref outerits); if( outerits==10 ) { break; } } // // Store result // for(i=0; i<=n-1; i++) { coeffs[i] = state.xc[i]; } }
public override alglib.apobject make_copy() { minnsqp _result = new minnsqp(); _result.fc = fc; _result.fn = fn; _result.xc = (double[])xc.Clone(); _result.xn = (double[])xn.Clone(); _result.x0 = (double[])x0.Clone(); _result.gc = (double[])gc.Clone(); _result.d = (double[])d.Clone(); _result.uh = (double[,])uh.Clone(); _result.ch = (double[,])ch.Clone(); _result.rk = (double[,])rk.Clone(); _result.invutc = (double[])invutc.Clone(); _result.tmp0 = (double[])tmp0.Clone(); _result.tmpidx = (int[])tmpidx.Clone(); _result.tmpd = (double[])tmpd.Clone(); _result.tmpc = (double[])tmpc.Clone(); _result.tmplambdas = (double[])tmplambdas.Clone(); _result.tmpc2 = (double[,])tmpc2.Clone(); _result.tmpb = (bool[])tmpb.Clone(); _result.nnls = (snnls.snnlssolver)nnls.make_copy(); return _result; }