コード例 #1
0
ファイル: optimization.cs プロジェクト: orlovk/PtProject
        /*************************************************************************
        This function runs QPCholesky solver; it returns after optimization   process
        was completed. Following QP problem is solved:

            min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin))
            
        subject to boundary constraints.

        INPUT PARAMETERS:
            AC          -   for dense problems (AKind=0) contains system matrix in
                            the A-term of CQM object.  OTHER  TERMS  ARE  ACTIVELY
                            USED AND MODIFIED BY THE SOLVER!
            SparseAC    -   for sparse problems (AKind=1
            AKind       -   sparse matrix format:
                            * 0 for dense matrix
                            * 1 for sparse matrix
            SparseUpper -   which triangle of SparseAC stores matrix  -  upper  or
                            lower one (for dense matrices this  parameter  is  not
                            actual).
            BC          -   linear term, array[NC]
            BndLC       -   lower bound, array[NC]
            BndUC       -   upper bound, array[NC]
            SC          -   scale vector, array[NC]:
                            * I-th element contains scale of I-th variable,
                            * SC[I]>0
            XOriginC    -   origin term, array[NC]. Can be zero.
            NC          -   number of variables in the  original  formulation  (no
                            slack variables).
            CLEICC      -   linear equality/inequality constraints. Present version
                            of this function does NOT provide  publicly  available
                            support for linear constraints. This feature  will  be
                            introduced in the future versions of the function.
            NEC, NIC    -   number of equality/inequality constraints.
                            MUST BE ZERO IN THE CURRENT VERSION!!!
            Settings    -   QPCholeskySettings object initialized by one of the initialization
                            functions.
            SState      -   object which stores temporaries:
                            * if uninitialized object was passed, FirstCall parameter MUST
                              be set to True; object will be automatically initialized by the
                              function, and FirstCall will be set to False.
                            * if FirstCall=False, it is assumed that this parameter was already
                              initialized by previous call to this function with same
                              problem dimensions (variable count N).
            XS          -   initial point, array[NC]
            
            
        OUTPUT PARAMETERS:
            XS          -   last point
            TerminationType-termination type:
                            *
                            *
                            *

          -- ALGLIB --
             Copyright 14.05.2011 by Bochkanov Sergey
        *************************************************************************/
        public static void qpcholeskyoptimize(cqmodels.convexquadraticmodel a,
            double anorm,
            double[] b,
            double[] bndl,
            double[] bndu,
            double[] s,
            double[] xorigin,
            int n,
            double[,] cleic,
            int nec,
            int nic,
            qpcholeskybuffers sstate,
            ref double[] xsc,
            ref int terminationtype)
        {
            int i = 0;
            double noisetolerance = 0;
            bool havebc = new bool();
            double v = 0;
            int badnewtonits = 0;
            double maxscaledgrad = 0;
            double v0 = 0;
            double v1 = 0;
            int nextaction = 0;
            double fprev = 0;
            double fcur = 0;
            double fcand = 0;
            double noiselevel = 0;
            double d0 = 0;
            double d1 = 0;
            double d2 = 0;
            int actstatus = 0;
            int i_ = 0;

            terminationtype = 0;

            
            //
            // Allocate storage and prepare fields
            //
            apserv.rvectorsetlengthatleast(ref sstate.rctmpg, n);
            apserv.rvectorsetlengthatleast(ref sstate.tmp0, n);
            apserv.rvectorsetlengthatleast(ref sstate.tmp1, n);
            apserv.rvectorsetlengthatleast(ref sstate.gc, n);
            apserv.rvectorsetlengthatleast(ref sstate.pg, n);
            apserv.rvectorsetlengthatleast(ref sstate.xs, n);
            apserv.rvectorsetlengthatleast(ref sstate.xn, n);
            apserv.rvectorsetlengthatleast(ref sstate.workbndl, n);
            apserv.rvectorsetlengthatleast(ref sstate.workbndu, n);
            apserv.bvectorsetlengthatleast(ref sstate.havebndl, n);
            apserv.bvectorsetlengthatleast(ref sstate.havebndu, n);
            sstate.repinneriterationscount = 0;
            sstate.repouteriterationscount = 0;
            sstate.repncholesky = 0;
            noisetolerance = 10;
            
            //
            // 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:
            // * XS, which stores shifted XStart (if we don't have XStart,
            //   value of XS will be ignored later)
            // * WorkBndL, WorkBndU, which store modified boundary constraints.
            //
            havebc = false;
            for(i=0; i<=n-1; i++)
            {
                sstate.havebndl[i] = math.isfinite(bndl[i]);
                sstate.havebndu[i] = math.isfinite(bndu[i]);
                havebc = (havebc || sstate.havebndl[i]) || sstate.havebndu[i];
                if( sstate.havebndl[i] )
                {
                    sstate.workbndl[i] = bndl[i]-xorigin[i];
                }
                else
                {
                    sstate.workbndl[i] = Double.NegativeInfinity;
                }
                if( sstate.havebndu[i] )
                {
                    sstate.workbndu[i] = bndu[i]-xorigin[i];
                }
                else
                {
                    sstate.workbndu[i] = Double.PositiveInfinity;
                }
            }
            apserv.rmatrixsetlengthatleast(ref sstate.workcleic, nec+nic, n+1);
            for(i=0; i<=nec+nic-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += cleic[i,i_]*xorigin[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    sstate.workcleic[i,i_] = cleic[i,i_];
                }
                sstate.workcleic[i,n] = cleic[i,n]-v;
            }
            
            //
            // We have starting point in StartX, so we just have to shift and bound it
            //
            for(i=0; i<=n-1; i++)
            {
                sstate.xs[i] = xsc[i]-xorigin[i];
                if( sstate.havebndl[i] )
                {
                    if( (double)(sstate.xs[i])<(double)(sstate.workbndl[i]) )
                    {
                        sstate.xs[i] = sstate.workbndl[i];
                    }
                }
                if( sstate.havebndu[i] )
                {
                    if( (double)(sstate.xs[i])>(double)(sstate.workbndu[i]) )
                    {
                        sstate.xs[i] = sstate.workbndu[i];
                    }
                }
            }
            
            //
            // Handle special case - no constraints
            //
            if( !havebc && nec+nic==0 )
            {
                
                //
                // "Simple" unconstrained Cholesky
                //
                apserv.bvectorsetlengthatleast(ref sstate.tmpb, n);
                for(i=0; i<=n-1; i++)
                {
                    sstate.tmpb[i] = false;
                }
                sstate.repncholesky = sstate.repncholesky+1;
                cqmodels.cqmsetb(a, b);
                cqmodels.cqmsetactiveset(a, sstate.xs, sstate.tmpb);
                if( !cqmodels.cqmconstrainedoptimum(a, ref sstate.xn) )
                {
                    terminationtype = -5;
                    return;
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    xsc[i_] = sstate.xn[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    xsc[i_] = xsc[i_] + xorigin[i_];
                }
                sstate.repinneriterationscount = 1;
                sstate.repouteriterationscount = 1;
                terminationtype = 4;
                return;
            }
            
            //
            // Prepare "active set" structure
            //
            sactivesets.sasinit(n, sstate.sas);
            sactivesets.sassetbc(sstate.sas, sstate.workbndl, sstate.workbndu);
            sactivesets.sassetlcx(sstate.sas, sstate.workcleic, nec, nic);
            sactivesets.sassetscale(sstate.sas, s);
            if( !sactivesets.sasstartoptimization(sstate.sas, sstate.xs) )
            {
                terminationtype = -3;
                return;
            }
            
            //
            // Main cycle of CQP algorithm
            //
            terminationtype = 4;
            badnewtonits = 0;
            maxscaledgrad = 0.0;
            while( true )
            {
                
                //
                // Update iterations count
                //
                apserv.inc(ref sstate.repouteriterationscount);
                apserv.inc(ref sstate.repinneriterationscount);
                
                //
                // Phase 1.
                //
                // Determine active set.
                // Update MaxScaledGrad.
                //
                cqmodels.cqmadx(a, sstate.sas.xc, ref sstate.rctmpg);
                for(i_=0; i_<=n-1;i_++)
                {
                    sstate.rctmpg[i_] = sstate.rctmpg[i_] + b[i_];
                }
                sactivesets.sasreactivateconstraints(sstate.sas, sstate.rctmpg);
                v = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+math.sqr(sstate.rctmpg[i]*s[i]);
                }
                maxscaledgrad = Math.Max(maxscaledgrad, Math.Sqrt(v));
                
                //
                // Phase 2: perform penalized steepest descent step.
                //
                // NextAction control variable is set on exit from this loop:
                // * NextAction>0 in case we have to proceed to Phase 3 (Newton step)
                // * NextAction<0 in case we have to proceed to Phase 1 (recalculate active set)
                // * NextAction=0 in case we found solution (step along projected gradient is small enough)
                //
                while( true )
                {
                    
                    //
                    // Calculate constrained descent direction, store to PG.
                    // Successful termination if PG is zero.
                    //
                    cqmodels.cqmadx(a, sstate.sas.xc, ref sstate.gc);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.gc[i_] = sstate.gc[i_] + b[i_];
                    }
                    sactivesets.sasconstraineddescent(sstate.sas, sstate.gc, ref sstate.pg);
                    v0 = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v0 += sstate.pg[i_]*sstate.pg[i_];
                    }
                    if( (double)(v0)==(double)(0) )
                    {
                        
                        //
                        // Constrained derivative is zero.
                        // Solution found.
                        //
                        nextaction = 0;
                        break;
                    }
                    
                    //
                    // Build quadratic model of F along descent direction:
                    //     F(xc+alpha*pg) = D2*alpha^2 + D1*alpha + D0
                    // Store noise level in the XC (noise level is used to classify
                    // step as singificant or insignificant).
                    //
                    // In case function curvature is negative or product of descent
                    // direction and gradient is non-negative, iterations are terminated.
                    //
                    // NOTE: D0 is not actually used, but we prefer to maintain it.
                    //
                    fprev = modelvalue(a, b, sstate.sas.xc, n, ref sstate.tmp0);
                    fprev = fprev+penaltyfactor*maxscaledgrad*sactivesets.sasactivelcpenalty1(sstate.sas, sstate.sas.xc);
                    cqmodels.cqmevalx(a, sstate.sas.xc, ref v, ref noiselevel);
                    v0 = cqmodels.cqmxtadx2(a, sstate.pg);
                    d2 = v0;
                    v1 = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v1 += sstate.pg[i_]*sstate.gc[i_];
                    }
                    d1 = v1;
                    d0 = fprev;
                    if( (double)(d2)<=(double)(0) )
                    {
                        
                        //
                        // Second derivative is non-positive, function is non-convex.
                        //
                        terminationtype = -5;
                        nextaction = 0;
                        break;
                    }
                    if( (double)(d1)>=(double)(0) )
                    {
                        
                        //
                        // Second derivative is positive, first derivative is non-negative.
                        // Solution found.
                        //
                        nextaction = 0;
                        break;
                    }
                    
                    //
                    // Modify quadratic model - add penalty for violation of the active
                    // constraints.
                    //
                    // Boundary constraints are always satisfied exactly, so we do not
                    // add penalty term for them. General equality constraint of the
                    // form a'*(xc+alpha*d)=b adds penalty term:
                    //     P(alpha) = (a'*(xc+alpha*d)-b)^2
                    //              = (alpha*(a'*d) + (a'*xc-b))^2
                    //              = alpha^2*(a'*d)^2 + alpha*2*(a'*d)*(a'*xc-b) + (a'*xc-b)^2
                    // Each penalty term is multiplied by 100*Anorm before adding it to
                    // the 1-dimensional quadratic model.
                    //
                    // Penalization of the quadratic model improves behavior of the
                    // algorithm in the presense of the multiple degenerate constraints.
                    // In particular, it prevents algorithm from making large steps in
                    // directions which violate equality constraints.
                    //
                    for(i=0; i<=nec+nic-1; i++)
                    {
                        if( sstate.sas.activeset[n+i]>0 )
                        {
                            v0 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v0 += sstate.workcleic[i,i_]*sstate.pg[i_];
                            }
                            v1 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v1 += sstate.workcleic[i,i_]*sstate.sas.xc[i_];
                            }
                            v1 = v1-sstate.workcleic[i,n];
                            v = 100*anorm;
                            d2 = d2+v*math.sqr(v0);
                            d1 = d1+v*2*v0*v1;
                            d0 = d0+v*math.sqr(v1);
                        }
                    }
                    
                    //
                    // Try unbounded step.
                    // In case function change is dominated by noise or function actually increased
                    // instead of decreasing, we terminate iterations.
                    //
                    v = -(d1/(2*d2));
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.xn[i_] = sstate.sas.xc[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.xn[i_] = sstate.xn[i_] + v*sstate.pg[i_];
                    }
                    fcand = modelvalue(a, b, sstate.xn, n, ref sstate.tmp0);
                    fcand = fcand+penaltyfactor*maxscaledgrad*sactivesets.sasactivelcpenalty1(sstate.sas, sstate.xn);
                    if( (double)(fcand)>=(double)(fprev-noiselevel*noisetolerance) )
                    {
                        nextaction = 0;
                        break;
                    }
                    
                    //
                    // Save active set
                    // Perform bounded step with (possible) activation
                    //
                    actstatus = boundedstepandactivation(sstate.sas, sstate.xn, n, ref sstate.tmp0);
                    fcur = modelvalue(a, b, sstate.sas.xc, n, ref sstate.tmp0);
                    
                    //
                    // Depending on results, decide what to do:
                    // 1. In case step was performed without activation of constraints,
                    //    we proceed to Newton method
                    // 2. In case there was activated at least one constraint with ActiveSet[I]<0,
                    //    we proceed to Phase 1 and re-evaluate active set.
                    // 3. Otherwise (activation of the constraints with ActiveSet[I]=0)
                    //    we try Phase 2 one more time.
                    //
                    if( actstatus<0 )
                    {
                        
                        //
                        // Step without activation, proceed to Newton
                        //
                        nextaction = 1;
                        break;
                    }
                    if( actstatus==0 )
                    {
                        
                        //
                        // No new constraints added during last activation - only
                        // ones which were at the boundary (ActiveSet[I]=0), but
                        // inactive due to numerical noise.
                        //
                        // Now, these constraints are added to the active set, and
                        // we try to perform steepest descent (Phase 2) one more time.
                        //
                        continue;
                    }
                    else
                    {
                        
                        //
                        // Last step activated at least one significantly new
                        // constraint (ActiveSet[I]<0), we have to re-evaluate
                        // active set (Phase 1).
                        //
                        nextaction = -1;
                        break;
                    }
                }
                if( nextaction<0 )
                {
                    continue;
                }
                if( nextaction==0 )
                {
                    break;
                }
                
                //
                // Phase 3: fast equality-constrained solver
                //
                // NOTE: this solver uses Augmented Lagrangian algorithm to solve
                //       equality-constrained subproblems. This algorithm may
                //       perform steps which increase function values instead of
                //       decreasing it (in hard cases, like overconstrained problems).
                //
                //       Such non-monononic steps may create a loop, when Augmented
                //       Lagrangian algorithm performs uphill step, and steepest
                //       descent algorithm (Phase 2) performs downhill step in the
                //       opposite direction.
                //
                //       In order to prevent iterations to continue forever we
                //       count iterations when AL algorithm increased function
                //       value instead of decreasing it. When number of such "bad"
                //       iterations will increase beyong MaxBadNewtonIts, we will
                //       terminate algorithm.
                //
                fprev = modelvalue(a, b, sstate.sas.xc, n, ref sstate.tmp0);
                while( true )
                {
                    
                    //
                    // Calculate optimum subject to presently active constraints
                    //
                    sstate.repncholesky = sstate.repncholesky+1;
                    if( !constrainedoptimum(sstate.sas, a, anorm, b, ref sstate.xn, n, ref sstate.tmp0, ref sstate.tmpb, ref sstate.tmp1) )
                    {
                        terminationtype = -5;
                        sactivesets.sasstopoptimization(sstate.sas);
                        return;
                    }
                    
                    //
                    // Add constraints.
                    // If no constraints was added, accept candidate point XN and move to next phase.
                    //
                    if( boundedstepandactivation(sstate.sas, sstate.xn, n, ref sstate.tmp0)<0 )
                    {
                        break;
                    }
                }
                fcur = modelvalue(a, b, sstate.sas.xc, n, ref sstate.tmp0);
                if( (double)(fcur)>=(double)(fprev) )
                {
                    badnewtonits = badnewtonits+1;
                }
                if( badnewtonits>=maxbadnewtonits )
                {
                    
                    //
                    // Algorithm found solution, but keeps iterating because Newton
                    // algorithm performs uphill steps (noise in the Augmented Lagrangian
                    // algorithm). We terminate algorithm; it is considered normal
                    // termination.
                    //
                    break;
                }
            }
            sactivesets.sasstopoptimization(sstate.sas);
            
            //
            // Post-process: add XOrigin to XC
            //
            for(i=0; i<=n-1; i++)
            {
                if( sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.workbndl[i]) )
                {
                    xsc[i] = bndl[i];
                    continue;
                }
                if( sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.workbndu[i]) )
                {
                    xsc[i] = bndu[i];
                    continue;
                }
                xsc[i] = apserv.boundval(sstate.sas.xc[i]+xorigin[i], bndl[i], bndu[i]);
            }
        }
コード例 #2
0
ファイル: optimization.cs プロジェクト: orlovk/PtProject
 public override alglib.apobject make_copy()
 {
     qpcholeskybuffers _result = new qpcholeskybuffers();
     _result.sas = (sactivesets.sactiveset)sas.make_copy();
     _result.pg = (double[])pg.Clone();
     _result.gc = (double[])gc.Clone();
     _result.xs = (double[])xs.Clone();
     _result.xn = (double[])xn.Clone();
     _result.workbndl = (double[])workbndl.Clone();
     _result.workbndu = (double[])workbndu.Clone();
     _result.havebndl = (bool[])havebndl.Clone();
     _result.havebndu = (bool[])havebndu.Clone();
     _result.workcleic = (double[,])workcleic.Clone();
     _result.rctmpg = (double[])rctmpg.Clone();
     _result.tmp0 = (double[])tmp0.Clone();
     _result.tmp1 = (double[])tmp1.Clone();
     _result.tmpb = (bool[])tmpb.Clone();
     _result.repinneriterationscount = repinneriterationscount;
     _result.repouteriterationscount = repouteriterationscount;
     _result.repncholesky = repncholesky;
     return _result;
 }