예제 #1
0
 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];
 }
예제 #2
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];
            }
        }
예제 #3
0
 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;
 }