Example #1
0
        /*************************************************************************
        This   function  updates  equality-constrained   Cholesky   matrix   after
        activation of the  new  equality  constraints.  Matrix  being  updated  is
        quadratic term of the function below

            f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            
        where A can be dense or sparse.

        This  function  uses  YIdx[]  array  (set by CNewtonBuild()  function)  to
        distinguish between active and inactive constraints.

        This function works as black box. It uses fields of SState which are marked
        as "Variables for constrained Newton phase", and only  this  function  and
        its friends know about these variables. Everyone else should use:
        * CNewtonBuild() to prepare initial Cholesky decomposition for step
        * CNewtonStep() to perform constrained Newton step
        * CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
          constraints were updated. In some cases it  is  possible to  efficiently
          re-calculate Cholesky decomposition if you know which  constraints  were
          activated. If efficient  re-calculation  is  impossible,  this  function
          returns False.

        INPUT PARAMETERS:
            SState  -   structure which stores model and temporaries for CN phase;
                        in particular, SAS.XC stores current point.
            Settings -  QQPSettings object which was  initialized  by  appropriate
                        construction function.
            NCUpdates-  counter which is incremented after each update (one update
                        means one variable being fixed)
            
        OUTPUT PARAMETERS:
            NCUpdates-  possibly updated counter
            
        RESULT:
            True, if Cholesky decomposition was successfully performed.
            False, if a) model age was too high, or b) particular  combination  of
            matrix type (sparse) and constraints (general linear) is not supported
            
        NOTE: this function may routinely return False.
              You should be able to handle such situations.
                    
          -- ALGLIB --
             Copyright 14.05.2014 by Bochkanov Sergey
        *************************************************************************/
        private static bool cnewtonupdate(qqpbuffers sstate,
            qqpsettings settings,
            ref int ncupdates)
        {
            bool result = new bool();
            int n = 0;
            int nfree = 0;
            int ntofix = 0;
            bool b = new bool();
            int ridx0 = 0;
            int ridx1 = 0;
            int i = 0;
            int k = 0;

            result = false;
            
            //
            // Cholesky updates for sparse problems are not supported
            //
            if( sstate.akind==1 )
            {
                return result;
            }
            
            //
            // Fetch often used fields
            //
            n = sstate.n;
            nfree = sstate.nfree;
            
            //
            // Determine variables to fix and move them to YIdx[NFree-NToFix:NFree-1]
            // Exit if CNModelAge increased too much.
            //
            apserv.ivectorsetlengthatleast(ref sstate.tmpcni, n);
            ridx0 = 0;
            ridx1 = nfree-1;
            for(i=0; i<=nfree-1; i++)
            {
                sstate.tmpcni[i] = -1;
            }
            for(k=0; k<=nfree-1; k++)
            {
                i = sstate.yidx[k];
                alglib.ap.assert(!sstate.havebndl[i] || (double)(sstate.sas.xc[i])>=(double)(sstate.bndl[i]), "CNewtonUpdate: internal error");
                alglib.ap.assert(!sstate.havebndu[i] || (double)(sstate.sas.xc[i])<=(double)(sstate.bndu[i]), "CNewtonUpdate: internal error");
                b = false;
                b = b || (sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i]));
                b = b || (sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i]));
                if( b )
                {
                    sstate.tmpcni[ridx1] = i;
                    ridx1 = ridx1-1;
                }
                else
                {
                    sstate.tmpcni[ridx0] = i;
                    ridx0 = ridx0+1;
                }
            }
            alglib.ap.assert(ridx0==ridx1+1, "CNewtonUpdate: internal error");
            ntofix = nfree-ridx0;
            if( ntofix==0 || ntofix==nfree )
            {
                return result;
            }
            if( sstate.cnmodelage+ntofix>settings.cnmaxupdates )
            {
                return result;
            }
            for(i=0; i<=nfree-1; i++)
            {
                sstate.yidx[i] = sstate.tmpcni[i];
            }
            
            //
            // Constrained Newton matrix: dense version.
            //
            if( sstate.akind==0 )
            {
                
                //
                // Update Cholesky matrix with SPDMatrixCholeskyUpdateFixBuf()
                //
                apserv.bvectorsetlengthatleast(ref sstate.tmpcnb, n);
                for(i=0; i<=n-1; i++)
                {
                    sstate.tmpcnb[i] = false;
                }
                for(i=nfree-ntofix; i<=nfree-1; i++)
                {
                    sstate.tmpcnb[sstate.yidx[i]] = true;
                }
                trfac.spdmatrixcholeskyupdatefixbuf(sstate.densez, n, true, sstate.tmpcnb, ref sstate.tmpcn);
                
                //
                // Update information stored in State and exit
                //
                sstate.nfree = nfree-ntofix;
                sstate.cnmodelage = sstate.cnmodelage+ntofix;
                ncupdates = ncupdates+ntofix;
                result = true;
                return result;
            }
            
            //
            // Unexpected :)
            //
            alglib.ap.assert(false, "CNewtonUpdate: internal error");
            return result;
        }
Example #2
0
        /*************************************************************************
        This   function prepares equality-constrained Newton step using previously
        calculated constrained Cholesky matrix of the problem

            f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            
        where A can be dense or sparse.

        As  input,  this  function  accepts  gradient  at the current location. As
        output, it returns step vector (replaces gradient).

        This function works as black box. It uses fields of SState which are marked
        as "Variables for constrained Newton phase", and only  this  function  and
        its friends know about these variables. Everyone else should use:
        * CNewtonBuild() to prepare initial Cholesky decomposition for step
        * CNewtonStep() to perform constrained Newton step
        * CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
          constraints were updated. In some cases it  is  possible to  efficiently
          re-calculate Cholesky decomposition if you know which  constraints  were
          activated. If efficient  re-calculation  is  impossible,  this  function
          returns False.

        INPUT PARAMETERS:
            SState  -   structure which stores model and temporaries for CN phase;
                        in particular, SAS.XC stores current point.
            Settings -  QQPSettings object which was  initialized  by  appropriate
                        construction function.
            GC       -  array[NMain+NSlack], gradient of the target function
            
        OUTPUT PARAMETERS:
            GC       -  array[NMain+NSlack], step vector (on success)
            
        RESULT:
            True, if step was successfully calculated.
            False, if step calculation failed:
            a) gradient was exactly zero,
            b) gradient norm was smaller than EpsG (stopping condition)
            c) all variables were equality-constrained
            
        NOTE: this function may routinely return False.
              You should be able to handle such situations.
                    
          -- ALGLIB --
             Copyright 14.05.2014 by Bochkanov Sergey
        *************************************************************************/
        private static bool cnewtonstep(qqpbuffers sstate,
            qqpsettings settings,
            double[] gc)
        {
            bool result = new bool();
            int i = 0;
            int n = 0;
            int nfree = 0;
            double v = 0;
            int i_ = 0;

            result = false;
            n = sstate.n;
            nfree = sstate.nfree;
            for(i=nfree; i<=n-1; i++)
            {
                gc[sstate.yidx[i]] = 0.0;
            }
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += gc[i_]*gc[i_];
            }
            if( (double)(Math.Sqrt(v))<=(double)(settings.epsg) )
            {
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                gc[i] = -gc[i];
            }
            if( sstate.akind==0 )
            {
                
                //
                // Dense Newton step.
                // Use straightforward Cholesky solver.
                //
                fbls.fblscholeskysolve(sstate.densez, 1.0, n, true, gc, ref sstate.tmpcn);
                result = true;
                return result;
            }
            if( sstate.akind==1 )
            {
                
                //
                // Sparse Newton step.
                //
                // We have T*T' = L*L' = U'*U (depending on specific triangle stored in SparseCCA).
                //
                if( sstate.sparseupper )
                {
                    sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 1, gc);
                    sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 0, gc);
                }
                else
                {
                    sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 0, gc);
                    sparse.sparsetrsv(sstate.sparsecca, sstate.sparseupper, false, 1, gc);
                }
                result = true;
                return result;
            }
            alglib.ap.assert(false, "CNewtonStep: internal error");
            return result;
        }
Example #3
0
        /*************************************************************************
        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);
        }
Example #4
0
        /*************************************************************************
        This function prepares data for  constrained  Newton  step  for  penalized
        quadratic model of the form

            f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            
        where A can be dense or sparse, and model is considered subject to equality
        constraints specified by SState.SAS.XC  object.  Constraint  is considered
        active if XC[i] is exactly BndL[i] or BndU[i],  i.e.  we  ignore  internal
        list of constraints monitored by SAS object. Our own  set  of  constraints
        includes all  constraints  stored  by  SAS,  but  also  may  include  some
        constraints which are inactive in SAS.

        "Preparation" means that Cholesky decomposition of  the  effective  system
        matrix is performed, and we can  perform  constrained  Newton  step.

        This function works as black box. It uses fields of SState which are marked
        as "Variables for constrained Newton phase", and only  this  function  and
        its friends know about these variables. Everyone else should use:
        * CNewtonBuild() to prepare initial Cholesky decomposition for step
        * CNewtonStep() to perform constrained Newton step
        * CNewtonUpdate() to update Cholesky matrix  after  point  was  moved  and
          constraints were updated. In some cases it  is  possible to  efficiently
          re-calculate Cholesky decomposition if you know which  constraints  were
          activated. If efficient  re-calculation  is  impossible,  this  function
          returns False.

        INPUT PARAMETERS:
            SState  -   structure which stores model and temporaries for CN phase;
                        in particular, SAS.XC stores current point.
            SparseSolver-which sparse solver to use for sparse model; ignored  for
                        dense QP. Can be:
                        * 2 -   SKS-based Cholesky
            NCholesky-  counter which is incremented after Cholesky (successful or
                        failed one)
            
        OUTPUT PARAMETERS:
            NCholesky-  possibly updated counter
            
        RESULT:
            True, if Cholesky decomposition was successfully performed.
            False, if a) matrix was semi-definite or indefinite, or b)  particular
            combination of matrix type (sparse) and constraints  (general  linear)
            is not supported.
            
        NOTE: this function may routinely return False, for indefinite matrices or
              for sparse problems with general linear constraints. You  should  be
              able to handle such situations.
                    
          -- ALGLIB --
             Copyright 14.05.2014 by Bochkanov Sergey
        *************************************************************************/
        private static bool cnewtonbuild(qqpbuffers sstate,
            int sparsesolver,
            ref int ncholesky)
        {
            bool result = new bool();
            int nec = 0;
            int nic = 0;
            int n = 0;
            int nmain = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            bool b = new bool();
            int ridx0 = 0;
            int ridx1 = 0;
            int nfree = 0;
            int i_ = 0;

            result = false;
            
            //
            // Fetch often used fields
            //
            n = sstate.n;
            nmain = sstate.nmain;
            nec = sstate.nec;
            nic = sstate.nic;
            
            //
            // Check problem properties.
            // Sparse problems with general linear constraints are not supported yet.
            //
            if( sstate.akind==1 && nec+nic>0 )
            {
                return result;
            }
            
            //
            // 1. Set CNModelAge to zero
            // 2. Generate YIdx - reordering of variables such that free variables
            //    come first and are ordered by ascending, fixed are last ones and
            //    have no particular ordering.
            //
            // This step is same for dense and sparse problems.
            //
            sstate.cnmodelage = 0;
            apserv.ivectorsetlengthatleast(ref sstate.yidx, n);
            ridx0 = 0;
            ridx1 = n-1;
            for(i=0; i<=n-1; i++)
            {
                sstate.yidx[i] = -1;
            }
            for(i=0; i<=n-1; i++)
            {
                alglib.ap.assert(!sstate.havebndl[i] || (double)(sstate.sas.xc[i])>=(double)(sstate.bndl[i]), "CNewtonBuild: internal error");
                alglib.ap.assert(!sstate.havebndu[i] || (double)(sstate.sas.xc[i])<=(double)(sstate.bndu[i]), "CNewtonBuild: internal error");
                b = false;
                b = b || (sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i]));
                b = b || (sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i]));
                if( b )
                {
                    sstate.yidx[ridx1] = i;
                    ridx1 = ridx1-1;
                }
                else
                {
                    sstate.yidx[ridx0] = i;
                    ridx0 = ridx0+1;
                }
            }
            alglib.ap.assert(ridx0==ridx1+1, "CNewtonBuild: internal error");
            nfree = ridx0;
            sstate.nfree = nfree;
            if( nfree==0 )
            {
                return result;
            }
            
            //
            // Constrained Newton matrix: dense version
            //
            if( sstate.akind==0 )
            {
                apserv.rmatrixsetlengthatleast(ref sstate.densez, n, n);
                apserv.rvectorsetlengthatleast(ref sstate.tmpcn, n);
                if( nec+nic>0 )
                {
                    
                    //
                    // Initialize Z with C'*C, add A
                    //
                    ablas.rmatrixsyrk(n, nec+nic, penaltyfactor, sstate.cleic, 0, 0, 2, 0.0, sstate.densez, 0, 0, true);
                    for(i=0; i<=nmain-1; i++)
                    {
                        for(j=i; j<=nmain-1; j++)
                        {
                            sstate.densez[i,j] = sstate.densez[i,j]+sstate.densea[i,j];
                        }
                    }
                }
                else
                {
                    
                    //
                    // No linear constraints, just set Z to A
                    //
                    alglib.ap.assert(n==nmain, "CNewtonBuild: integrity check failed");
                    for(i=0; i<=nmain-1; i++)
                    {
                        for(j=i; j<=nmain-1; j++)
                        {
                            sstate.densez[i,j] = sstate.densea[i,j];
                        }
                    }
                }
                for(i=1; i<=nfree-1; i++)
                {
                    alglib.ap.assert(sstate.yidx[i]>sstate.yidx[i-1], "CNewtonBuild: integrity check failed");
                }
                for(i=0; i<=nfree-1; i++)
                {
                    k = sstate.yidx[i];
                    for(j=i; j<=nfree-1; j++)
                    {
                        sstate.densez[i,j] = sstate.densez[k,sstate.yidx[j]];
                    }
                }
                apserv.rvectorsetlengthatleast(ref sstate.regdiag, n);
                for(i=0; i<=nfree-1; i++)
                {
                    v = 0.0;
                    for(j=0; j<=i-1; j++)
                    {
                        v = v+Math.Abs(sstate.densez[j,i]);
                    }
                    for(j=i; j<=nfree-1; j++)
                    {
                        v = v+Math.Abs(sstate.densez[i,j]);
                    }
                    if( (double)(v)==(double)(0) )
                    {
                        v = 1.0;
                    }
                    sstate.regdiag[i] = regz*v;
                }
                for(i=0; i<=nfree-1; i++)
                {
                    sstate.densez[i,i] = sstate.densez[i,i]+sstate.regdiag[i];
                }
                apserv.inc(ref ncholesky);
                if( !trfac.spdmatrixcholeskyrec(ref sstate.densez, 0, nfree, true, ref sstate.tmpcn) )
                {
                    return result;
                }
                for(i=nfree-1; i>=0; i--)
                {
                    for(i_=i; i_<=nfree-1;i_++)
                    {
                        sstate.tmpcn[i_] = sstate.densez[i,i_];
                    }
                    k = sstate.yidx[i];
                    for(j=k; j<=n-1; j++)
                    {
                        sstate.densez[k,j] = 0;
                    }
                    for(j=i; j<=nfree-1; j++)
                    {
                        sstate.densez[k,sstate.yidx[j]] = sstate.tmpcn[j];
                    }
                }
                for(i=nfree; i<=n-1; i++)
                {
                    k = sstate.yidx[i];
                    sstate.densez[k,k] = 1.0;
                    for(j=k+1; j<=n-1; j++)
                    {
                        sstate.densez[k,j] = 0;
                    }
                }
                result = true;
                return result;
            }
            
            //
            // Constrained Newton matrix: sparse version
            //
            if( sstate.akind==1 )
            {
                alglib.ap.assert(nec+nic==0, "CNewtonBuild: internal error");
                alglib.ap.assert(sparsesolver==2, "CNewtonBuild: internal error");
                
                //
                // Copy sparse A to Z and fill rows/columns corresponding to active
                // constraints by zeros. Diagonal elements corresponding to active
                // constraints are filled by unit values.
                //
                sparse.sparsecopytosksbuf(sstate.sparsea, sstate.sparsecca);
                apserv.rvectorsetlengthatleast(ref sstate.tmpcn, n);
                for(i=0; i<=n-1; i++)
                {
                    sstate.tmpcn[i] = 0;
                }
                for(i=nfree; i<=n-1; i++)
                {
                    sstate.tmpcn[sstate.yidx[i]] = 1;
                }
                for(i=0; i<=n-1; i++)
                {
                    k = sstate.sparsecca.ridx[i];
                    for(j=i-sstate.sparsecca.didx[i]; j<=i; j++)
                    {
                        if( (double)(sstate.tmpcn[i])!=(double)(0) || (double)(sstate.tmpcn[j])!=(double)(0) )
                        {
                            
                            //
                            // I-th or J-th variable is in active set (constrained)
                            //
                            if( i==j )
                            {
                                sstate.sparsecca.vals[k] = 1.0;
                            }
                            else
                            {
                                sstate.sparsecca.vals[k] = 0.0;
                            }
                        }
                        k = k+1;
                    }
                }
                
                //
                // Perform sparse Cholesky
                //
                apserv.inc(ref ncholesky);
                if( !trfac.sparsecholeskyskyline(sstate.sparsecca, nmain, sstate.sparseupper) )
                {
                    return result;
                }
                result = true;
                return result;
            }
            
            //
            // Unexpected :)
            //
            alglib.ap.assert(false, "CNewtonBuild: internal error");
            return result;
        }
Example #5
0
        /*************************************************************************
        First and second derivatives  of  the  "extended"  target  function  along
        specified direction. Target  function  is  called  "extended"  because  of
        additional slack variables and has form:

            f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            
        with gradient

            grad = A*x + b + penaltyfactor*C'*(C*x-b)
            
        Quadratic model has form

            F(x0+alpha*D) = D2*alpha^2 + D1*alpha

        INPUT PARAMETERS:
            SState  -   structure which is used to obtain quadratic term of the model
            X       -   current point, array[N]
            D       -   direction across which derivatives are calculated, array[N]
            G       -   gradient at current point (pre-calculated by caller), array[N]

        OUTPUT PARAMETERS:
            D1      -   linear coefficient
            D1Est   -   estimate of D1 sign,  accounting  for  possible  numerical
                        errors:
                        * >0    means "almost surely positive"
                        * <0    means "almost surely negative"
                        * =0    means "pessimistic estimate  of  numerical  errors
                                in D1 is larger than magnitude of D1 itself; it is
                                impossible to reliably distinguish D1 from zero".
            D2      -   quadratic coefficient
            D2Est   -   estimate of D2 sign,  accounting  for  possible  numerical
                        errors:
                        * >0    means "almost surely positive"
                        * <0    means "almost surely negative"
                        * =0    means "pessimistic estimate  of  numerical  errors
                                in D2 is larger than magnitude of D2 itself; it is
                                impossible to reliably distinguish D2 from zero".
                    
          -- ALGLIB --
             Copyright 14.05.2014 by Bochkanov Sergey
        *************************************************************************/
        private static void quadraticmodel(qqpbuffers sstate,
            double[] x,
            double[] d,
            double[] g,
            ref double d1,
            ref int d1est,
            ref double d2,
            ref int d2est)
        {
            int nec = 0;
            int nic = 0;
            int n = 0;
            int nmain = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double vv = 0;
            double mx = 0;
            double mb = 0;
            double md = 0;
            int i_ = 0;

            d1 = 0;
            d1est = 0;
            d2 = 0;
            d2est = 0;

            n = sstate.n;
            nmain = sstate.nmain;
            nec = sstate.nec;
            nic = sstate.nic;
            
            //
            // Maximums
            //
            mx = 0.0;
            md = 0.0;
            mb = 0.0;
            for(i=0; i<=n-1; i++)
            {
                mx = Math.Max(mx, Math.Abs(x[i]));
                md = Math.Max(md, Math.Abs(d[i]));
            }
            for(i=0; i<=nmain-1; i++)
            {
                mb = Math.Max(mb, Math.Abs(sstate.b[i]));
            }
            
            //
            // D2
            //
            if( sstate.akind==0 )
            {
                
                //
                // Dense matrix A
                //
                d2 = 0.0;
                for(i=0; i<=nmain-1; i++)
                {
                    v = d[i];
                    vv = 0.0;
                    d2 = d2+0.5*v*v*sstate.densea[i,i];
                    for(j=i+1; j<=nmain-1; j++)
                    {
                        vv = vv+sstate.densea[i,j]*d[j];
                    }
                    d2 = d2+v*vv;
                }
            }
            else
            {
                
                //
                // Sparse matrix A
                //
                alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in TargetGradient");
                d2 = 0.5*sparse.sparsevsmv(sstate.sparsea, sstate.sparseupper, d);
            }
            for(i=0; i<=nec+nic-1; i++)
            {
                
                //
                // NOTE: there is no "V:=V-CLEIC[I,N]" line, and it is not an error!
                //       We estimate curvature information here, which is not dependent
                //       on right part.
                //
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += sstate.cleic[i,i_]*d[i_];
                }
                d2 = d2+v*v*penaltyfactor*0.5;
            }
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += d[i_]*g[i_];
            }
            d1 = v;
            
            //
            // Error estimates
            //
            optserv.estimateparabolicmodel(sstate.absasum, sstate.absasum2, mx, mb, md, d1, d2, ref d1est, ref d2est);
        }
Example #6
0
        /*************************************************************************
        Gradient of "extended" (N>=NMain variables, original + slack ones)  target
        function:

            f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            
        which is equal to

            grad = A*x + b + penaltyfactor*C'*(C*x-b)

        Here:
        * x is array[N] (that's why problem is called extended - N>=NMain)
        * A is array[NMain,NMain]
        * b is array[NMain]
        * C is array[NEC+NIC,N]

        INPUT PARAMETERS:
            SState  -   structure which stores function terms (not modified)
            X       -   location
            G       -   possibly preallocated buffer

        OUTPUT PARAMETERS:
            G       -   array[N], gradient
                    
          -- ALGLIB --
             Copyright 21.12.2013 by Bochkanov Sergey
        *************************************************************************/
        private static void targetgradient(qqpbuffers sstate,
            double[] x,
            ref double[] g)
        {
            int nec = 0;
            int nic = 0;
            int n = 0;
            int nmain = 0;
            int i = 0;
            double v = 0;
            int i_ = 0;

            n = sstate.n;
            nmain = sstate.nmain;
            nec = sstate.nec;
            nic = sstate.nic;
            apserv.rvectorsetlengthatleast(ref g, n);
            if( sstate.akind==0 )
            {
                
                //
                // Dense matrix A
                //
                ablas.rmatrixmv(nmain, nmain, sstate.densea, 0, 0, 0, x, 0, ref g, 0);
            }
            else
            {
                
                //
                // Sparse matrix A
                //
                alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in TargetGradient");
                sparse.sparsesmv(sstate.sparsea, sstate.sparseupper, x, ref g);
            }
            for(i_=0; i_<=nmain-1;i_++)
            {
                g[i_] = g[i_] + sstate.b[i_];
            }
            for(i=nmain; i<=n-1; i++)
            {
                g[i] = 0.0;
            }
            for(i=0; i<=nec+nic-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += sstate.cleic[i,i_]*x[i_];
                }
                v = v-sstate.cleic[i,n];
                v = v*penaltyfactor;
                for(i_=0; i_<=n-1;i_++)
                {
                    g[i_] = g[i_] + v*sstate.cleic[i,i_];
                }
            }
        }
Example #7
0
        /*************************************************************************
        Target function at point PROJ(X+Stp*D), where PROJ(.) is a projection into
        feasible set.

        NOTE: if Stp=0, D is not referenced at all. Thus,  there  is  no  need  to
              fill it by some meaningful values for Stp=0.

        This subroutine uses temporary buffer Tmp, which is automatically  resized
        if needed.

          -- ALGLIB --
             Copyright 21.12.2013 by Bochkanov Sergey
        *************************************************************************/
        private static double projectedtargetfunction(qqpbuffers sstate,
            double[] x,
            double[] d,
            double stp,
            ref double[] tmp0)
        {
            double result = 0;
            int nec = 0;
            int nic = 0;
            int n = 0;
            int nmain = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double vv = 0;
            int i_ = 0;

            n = sstate.n;
            nmain = sstate.nmain;
            nec = sstate.nec;
            nic = sstate.nic;
            apserv.rvectorsetlengthatleast(ref tmp0, n);
            
            //
            // Calculate projected point
            //
            for(i=0; i<=n-1; i++)
            {
                if( (double)(stp)!=(double)(0) )
                {
                    v = x[i]+stp*d[i];
                }
                else
                {
                    v = x[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;
            }
            
            //
            // Function value at the Tmp0:
            //
            // f(x) = 0.5*x'*A*x + b'*x + penaltyfactor*0.5*(C*x-b)'*(C*x-b)
            //
            result = 0.0;
            for(i=0; i<=nmain-1; i++)
            {
                result = result+sstate.b[i]*tmp0[i];
            }
            if( sstate.akind==0 )
            {
                
                //
                // Dense matrix A
                //
                for(i=0; i<=nmain-1; i++)
                {
                    v = tmp0[i];
                    result = result+0.5*v*v*sstate.densea[i,i];
                    vv = 0.0;
                    for(j=i+1; j<=nmain-1; j++)
                    {
                        vv = vv+sstate.densea[i,j]*tmp0[j];
                    }
                    result = result+v*vv;
                }
            }
            else
            {
                
                //
                // sparse matrix A
                //
                alglib.ap.assert(sstate.akind==1, "QQPOptimize: unexpected AKind in ProjectedTargetFunction");
                result = result+0.5*sparse.sparsevsmv(sstate.sparsea, sstate.sparseupper, tmp0);
            }
            for(i=0; i<=nec+nic-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += sstate.cleic[i,i_]*tmp0[i_];
                }
                result = result+0.5*penaltyfactor*math.sqr(v-sstate.cleic[i,n]);
            }
            return result;
        }
Example #8
0
        /*************************************************************************
        This function runs QQP 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.

        IMPORTANT: UNLIKE MANY OTHER SOLVERS, THIS FUNCTION DOES NOT  REQUIRE  YOU
                   TO INITIALIZE STATE OBJECT. IT CAN BE AUTOMATICALLY INITIALIZED
                   DURING SOLUTION PROCESS.

        INPUT PARAMETERS:
            AC          -   for dense problems (AKind=0) A-term of CQM object
                            contains system matrix. Other terms are unspecified
                            and should not be referenced.
            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    -   QQPSettings object initialized by one of the initialization
                            functions.
            SState      -   object which stores temporaries:
                            * uninitialized object is automatically initialized
                            * previously allocated memory is reused as much
                              as possible
            XS          -   initial point, array[NC]
            
            
        OUTPUT PARAMETERS:
            XS          -   last point
            TerminationType-termination type:
                            *
                            *
                            *

          -- ALGLIB --
             Copyright 14.05.2011 by Bochkanov Sergey
        *************************************************************************/
        public static void qqpoptimize(cqmodels.convexquadraticmodel ac,
            sparse.sparsematrix sparseac,
            int akind,
            bool sparseupper,
            double[] bc,
            double[] bndlc,
            double[] bnduc,
            double[] sc,
            double[] xoriginc,
            int nc,
            double[,] cleicc,
            int nec,
            int nic,
            qqpsettings settings,
            qqpbuffers sstate,
            double[] xs,
            ref int terminationtype)
        {
            int n = 0;
            int nmain = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double vv = 0;
            double d2 = 0;
            double d1 = 0;
            int d1est = 0;
            int d2est = 0;
            bool needact = new bool();
            double reststp = 0;
            double fullstp = 0;
            double stpmax = 0;
            double stp = 0;
            int stpcnt = 0;
            int cidx = 0;
            double cval = 0;
            int cgcnt = 0;
            int cgmax = 0;
            int newtcnt = 0;
            int sparsesolver = 0;
            double beta = 0;
            bool b = new bool();
            double fprev = 0;
            double fcur = 0;
            int i_ = 0;

            terminationtype = 0;

            
            //
            // Primary checks
            //
            alglib.ap.assert(akind==0 || akind==1, "QQPOptimize: incorrect AKind");
            sstate.nmain = nc;
            sstate.nslack = nic;
            sstate.n = nc+nic;
            sstate.nec = nec;
            sstate.nic = nic;
            sstate.akind = akind;
            n = sstate.n;
            nmain = sstate.nmain;
            terminationtype = 0;
            sstate.repinneriterationscount = 0;
            sstate.repouteriterationscount = 0;
            sstate.repncholesky = 0;
            sstate.repncupdates = 0;
            
            //
            // Several checks
            // * matrix size
            // * scale vector
            // * consistency of bound constraints
            // * consistency of settings
            //
            if( akind==1 )
            {
                alglib.ap.assert(sparse.sparsegetnrows(sparseac)==nmain, "QQPOptimize: rows(SparseAC)<>NMain");
                alglib.ap.assert(sparse.sparsegetncols(sparseac)==nmain, "QQPOptimize: cols(SparseAC)<>NMain");
            }
            for(i=0; i<=nmain-1; i++)
            {
                alglib.ap.assert(math.isfinite(sc[i]) && (double)(sc[i])>(double)(0), "QQPOptimize: incorrect scale");
            }
            for(i=0; i<=nmain-1; i++)
            {
                if( math.isfinite(bndlc[i]) && math.isfinite(bnduc[i]) )
                {
                    if( (double)(bndlc[i])>(double)(bnduc[i]) )
                    {
                        terminationtype = -3;
                        return;
                    }
                }
            }
            alglib.ap.assert(settings.cgphase || settings.cnphase, "QQPOptimize: both phases (CG and Newton) are inactive");
            
            //
            // Allocate data structures
            //
            apserv.rvectorsetlengthatleast(ref sstate.bndl, n);
            apserv.rvectorsetlengthatleast(ref sstate.bndu, n);
            apserv.bvectorsetlengthatleast(ref sstate.havebndl, n);
            apserv.bvectorsetlengthatleast(ref sstate.havebndu, n);
            apserv.rvectorsetlengthatleast(ref sstate.xs, n);
            apserv.rvectorsetlengthatleast(ref sstate.xp, n);
            apserv.rvectorsetlengthatleast(ref sstate.gc, n);
            apserv.rvectorsetlengthatleast(ref sstate.cgc, n);
            apserv.rvectorsetlengthatleast(ref sstate.cgp, n);
            apserv.rvectorsetlengthatleast(ref sstate.dc, n);
            apserv.rvectorsetlengthatleast(ref sstate.dp, n);
            apserv.rvectorsetlengthatleast(ref sstate.tmp0, n);
            apserv.rvectorsetlengthatleast(ref sstate.stpbuf, 15);
            sactivesets.sasinit(n, sstate.sas);
            
            //
            // Scale/shift problem coefficients:
            //
            //     min { 0.5*(x-x0)'*A*(x-x0) + b'*(x-x0) }
            //
            // becomes (after transformation "x = S*y+x0")
            //
            //     min { 0.5*y'*(S*A*S)*y + (S*b)'*y
            //
            // Modified A_mod=S*A*S and b_mod=S*(b+A*x0) are
            // stored into SState.DenseA and SState.B.
            //
            // NOTE: DenseA/DenseB are arrays whose lengths are
            //       NMain, not N=NMain+NSlack! We store reduced 
            //       matrix and vector because extend parts (last
            //       NSlack rows/columns) are exactly zero.
            //       
            //
            apserv.rvectorsetlengthatleast(ref sstate.b, nmain);
            for(i=0; i<=nmain-1; i++)
            {
                sstate.b[i] = sc[i]*bc[i];
            }
            if( akind==0 )
            {
                
                //
                // Dense QP problem - just copy and scale.
                //
                apserv.rmatrixsetlengthatleast(ref sstate.densea, nmain, nmain);
                cqmodels.cqmgeta(ac, ref sstate.densea);
                sstate.absamax = 0;
                sstate.absasum = 0;
                sstate.absasum2 = 0;
                for(i=0; i<=nmain-1; i++)
                {
                    for(j=0; j<=nmain-1; j++)
                    {
                        v = sc[i]*sstate.densea[i,j]*sc[j];
                        sstate.densea[i,j] = v;
                        sstate.absamax = Math.Max(sstate.absamax, v);
                        sstate.absasum = sstate.absasum+v;
                        sstate.absasum2 = sstate.absasum2+v*v;
                    }
                }
            }
            else
            {
                
                //
                // Sparse QP problem - a bit tricky. Depending on format of the
                // input we use different strategies for copying matrix:
                // * SKS matrices are copied to SKS format
                // * anything else is copied to CRS format
                //
                alglib.ap.assert(akind==1, "QQPOptimize: unexpected AKind (internal error)");
                sparse.sparsecopytosksbuf(sparseac, sstate.sparsea);
                if( sparseupper )
                {
                    sparse.sparsetransposesks(sstate.sparsea);
                }
                sstate.sparseupper = false;
                sstate.absamax = 0;
                sstate.absasum = 0;
                sstate.absasum2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    k = sstate.sparsea.ridx[i];
                    for(j=i-sstate.sparsea.didx[i]; j<=i; j++)
                    {
                        v = sc[i]*sstate.sparsea.vals[k]*sc[j];
                        sstate.sparsea.vals[k] = v;
                        if( i==j )
                        {
                            
                            //
                            // Diagonal terms are counted only once
                            //
                            sstate.absamax = Math.Max(sstate.absamax, v);
                            sstate.absasum = sstate.absasum+v;
                            sstate.absasum2 = sstate.absasum2+v*v;
                        }
                        else
                        {
                            
                            //
                            // Offdiagonal terms are counted twice
                            //
                            sstate.absamax = Math.Max(sstate.absamax, v);
                            sstate.absasum = sstate.absasum+2*v;
                            sstate.absasum2 = sstate.absasum2+2*v*v;
                        }
                        k = k+1;
                    }
                }
            }
            
            //
            // Load box constraints into State structure.
            //
            // We apply transformation to variables: y=(x-x_origin)/s,
            // each of the constraints is appropriately shifted/scaled.
            //
            for(i=0; i<=nmain-1; i++)
            {
                sstate.havebndl[i] = math.isfinite(bndlc[i]);
                if( sstate.havebndl[i] )
                {
                    sstate.bndl[i] = (bndlc[i]-xoriginc[i])/sc[i];
                }
                else
                {
                    alglib.ap.assert(Double.IsNegativeInfinity(bndlc[i]), "QQPOptimize: incorrect lower bound");
                    sstate.bndl[i] = Double.NegativeInfinity;
                }
                sstate.havebndu[i] = math.isfinite(bnduc[i]);
                if( sstate.havebndu[i] )
                {
                    sstate.bndu[i] = (bnduc[i]-xoriginc[i])/sc[i];
                }
                else
                {
                    alglib.ap.assert(Double.IsPositiveInfinity(bnduc[i]), "QQPOptimize: incorrect upper bound");
                    sstate.bndu[i] = Double.PositiveInfinity;
                }
            }
            for(i=nmain; i<=n-1; i++)
            {
                sstate.havebndl[i] = true;
                sstate.bndl[i] = 0.0;
                sstate.havebndu[i] = false;
                sstate.bndu[i] = Double.PositiveInfinity;
            }
            
            //
            // Shift/scale linear constraints with transformation y=(x-x_origin)/s:
            // * constraint "c[i]'*x = b[i]" becomes "(s[i]*c[i])'*x = b[i]-c[i]'*x_origin".
            // * after constraint is loaded into SState.CLEIC, it is additionally normalized
            //
            apserv.rmatrixsetlengthatleast(ref sstate.cleic, nec+nic, n+1);
            for(i=0; i<=nec+nic-1; i++)
            {
                v = 0;
                vv = 0;
                for(j=0; j<=nmain-1; j++)
                {
                    sstate.cleic[i,j] = cleicc[i,j]*sc[j];
                    vv = vv+math.sqr(sstate.cleic[i,j]);
                    v = v+cleicc[i,j]*xoriginc[j];
                }
                vv = Math.Sqrt(vv);
                for(j=nmain; j<=n-1; j++)
                {
                    sstate.cleic[i,j] = 0.0;
                }
                sstate.cleic[i,n] = cleicc[i,nmain]-v;
                if( i>=nec )
                {
                    sstate.cleic[i,nmain+i-nec] = 1.0;
                }
                if( (double)(vv)>(double)(0) )
                {
                    for(j=0; j<=n; j++)
                    {
                        sstate.cleic[i,j] = sstate.cleic[i,j]/vv;
                    }
                }
            }
            
            //
            // Process initial point:
            // * first NMain components are equal to XS-XOriginC
            // * last NIC components are deduced from linear constraints
            // * make sure that boundary constraints are preserved by transformation
            //
            for(i=0; i<=nmain-1; i++)
            {
                sstate.xs[i] = (xs[i]-xoriginc[i])/sc[i];
                if( sstate.havebndl[i] && (double)(sstate.xs[i])<(double)(sstate.bndl[i]) )
                {
                    sstate.xs[i] = sstate.bndl[i];
                }
                if( sstate.havebndu[i] && (double)(sstate.xs[i])>(double)(sstate.bndu[i]) )
                {
                    sstate.xs[i] = sstate.bndu[i];
                }
                if( sstate.havebndl[i] && (double)(xs[i])==(double)(bndlc[i]) )
                {
                    sstate.xs[i] = sstate.bndl[i];
                }
                if( sstate.havebndu[i] && (double)(xs[i])==(double)(bnduc[i]) )
                {
                    sstate.xs[i] = sstate.bndu[i];
                }
            }
            for(i=0; i<=nic-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=nmain-1;i_++)
                {
                    v += sstate.xs[i_]*sstate.cleic[nec+i,i_];
                }
                sstate.xs[nmain+i] = Math.Max(sstate.cleic[nec+i,n]-v, 0.0);
            }
            
            //
            // Prepare "active set" structure
            //
            sactivesets.sassetbc(sstate.sas, sstate.bndl, sstate.bndu);
            sactivesets.sassetlcx(sstate.sas, sstate.cleic, 0, 0);
            if( !sactivesets.sasstartoptimization(sstate.sas, sstate.xs) )
            {
                terminationtype = -3;
                return;
            }
            
            //
            // Select sparse direct solver
            //
            if( akind==1 )
            {
                sparsesolver = settings.sparsesolver;
                if( sparsesolver==0 )
                {
                    sparsesolver = 1;
                }
                if( sparse.sparseissks(sstate.sparsea) )
                {
                    sparsesolver = 2;
                }
                sparsesolver = 2;
                alglib.ap.assert(sparsesolver==1 || sparsesolver==2, "QQPOptimize: incorrect SparseSolver");
            }
            else
            {
                sparsesolver = 0;
            }
            
            //
            // Main loop.
            //
            // Following variables are used:
            // * GC stores current gradient (unconstrained)
            // * CGC stores current gradient (constrained)
            // * DC stores current search direction
            // * CGP stores constrained gradient at previous point
            //   (zero on initial entry)
            // * DP stores previous search direction
            //   (zero on initial entry)
            //
            cgmax = settings.cgminits;
            sstate.repinneriterationscount = 0;
            sstate.repouteriterationscount = 0;
            while( true )
            {
                if( settings.maxouterits>0 && sstate.repouteriterationscount>=settings.maxouterits )
                {
                    terminationtype = 5;
                    break;
                }
                if( sstate.repouteriterationscount>0 )
                {
                    
                    //
                    // Check EpsF- and EpsX-based stopping criteria.
                    // Because problem was already scaled, we do not scale step before checking its length.
                    // NOTE: these checks are performed only after at least one outer iteration was made.
                    //
                    if( (double)(settings.epsf)>(double)(0) )
                    {
                        
                        //
                        // NOTE 1: here we rely on the fact that ProjectedTargetFunction() ignore D when Stp=0
                        // NOTE 2: code below handles situation when update increases function value instead
                        //         of decreasing it.
                        //
                        fprev = projectedtargetfunction(sstate, sstate.xp, sstate.dc, 0.0, ref sstate.tmp0);
                        fcur = projectedtargetfunction(sstate, sstate.sas.xc, sstate.dc, 0.0, ref sstate.tmp0);
                        if( (double)(fprev-fcur)<=(double)(settings.epsf*Math.Max(Math.Abs(fprev), Math.Max(Math.Abs(fcur), 1.0))) )
                        {
                            terminationtype = 1;
                            break;
                        }
                    }
                    if( (double)(settings.epsx)>(double)(0) )
                    {
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+math.sqr(sstate.xp[i]-sstate.sas.xc[i]);
                        }
                        if( (double)(Math.Sqrt(v))<=(double)(settings.epsx) )
                        {
                            terminationtype = 2;
                            break;
                        }
                    }
                }
                apserv.inc(ref sstate.repouteriterationscount);
                for(i_=0; i_<=n-1;i_++)
                {
                    sstate.xp[i_] = sstate.sas.xc[i_];
                }
                if( !settings.cgphase )
                {
                    cgmax = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    sstate.cgp[i] = 0.0;
                    sstate.dp[i] = 0.0;
                }
                for(cgcnt=0; cgcnt<=cgmax-1; cgcnt++)
                {
                    
                    //
                    // Calculate unconstrained gradient GC for "extended" QP problem
                    // Determine active set, current constrained gradient CGC.
                    // Check gradient-based stopping condition.
                    //
                    // NOTE: because problem was scaled, we do not have to apply scaling
                    //       to gradient before checking stopping condition.
                    //
                    targetgradient(sstate, sstate.sas.xc, ref sstate.gc);
                    sactivesets.sasreactivateconstraints(sstate.sas, sstate.gc);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.cgc[i_] = sstate.gc[i_];
                    }
                    sactivesets.sasconstraineddirection(sstate.sas, ref sstate.cgc);
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += sstate.cgc[i_]*sstate.cgc[i_];
                    }
                    if( (double)(Math.Sqrt(v))<=(double)(settings.epsg) )
                    {
                        terminationtype = 4;
                        break;
                    }
                    
                    //
                    // Prepare search direction DC and explore it.
                    //
                    // We try to use CGP/DP to prepare conjugate gradient step,
                    // but we resort to steepest descent step (Beta=0) in case
                    // we are at I-th boundary, but DP[I]<>0.
                    //
                    // Such approach allows us to ALWAYS have feasible DC, with
                    // guaranteed compatibility with both feasible area and current
                    // active set.
                    //
                    // Automatic CG reset performed every time DP is incompatible
                    // with current active set and/or feasible area. We also
                    // perform reset every QuickQPRestartCG iterations.
                    //
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.dc[i_] = -sstate.cgc[i_];
                    }
                    v = 0.0;
                    vv = 0.0;
                    b = false;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+sstate.cgc[i]*sstate.cgc[i];
                        vv = vv+sstate.cgp[i]*sstate.cgp[i];
                        b = b || ((sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i])) && (double)(sstate.dp[i])!=(double)(0));
                        b = b || ((sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i])) && (double)(sstate.dp[i])!=(double)(0));
                    }
                    b = b || (double)(vv)==(double)(0);
                    b = b || cgcnt%quickqprestartcg==0;
                    if( !b )
                    {
                        beta = v/vv;
                    }
                    else
                    {
                        beta = 0.0;
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.dc[i_] = sstate.dc[i_] + beta*sstate.dp[i_];
                    }
                    sactivesets.sasconstraineddirection(sstate.sas, ref sstate.dc);
                    sactivesets.sasexploredirection(sstate.sas, sstate.dc, ref stpmax, ref cidx, ref cval);
                    
                    //
                    // Build quadratic model of F along descent direction:
                    //
                    //     F(xc+alpha*D) = D2*alpha^2 + D1*alpha
                    //
                    // Terminate algorithm if needed.
                    //
                    // NOTE: we do not maintain constant term D0
                    //
                    quadraticmodel(sstate, sstate.sas.xc, sstate.dc, sstate.gc, ref d1, ref d1est, ref d2, ref d2est);
                    if( (double)(d1)==(double)(0) && (double)(d2)==(double)(0) )
                    {
                        
                        //
                        // D1 and D2 are exactly zero, success.
                        // After this if-then we assume that D is non-zero.
                        //
                        terminationtype = 4;
                        break;
                    }
                    if( d1est>=0 )
                    {
                        
                        //
                        // Numerical noise is too large, it means that we are close
                        // to minimum - and that further improvement is impossible.
                        //
                        // After this if-then we assume that D1 is definitely negative
                        // (even under presence of numerical errors).
                        //
                        terminationtype = 7;
                        break;
                    }
                    if( d2est<=0 && cidx<0 )
                    {
                        
                        //
                        // Function is unbounded from below:
                        // * D1<0 (verified by previous block)
                        // * D2Est<=0, which means that either D2<0 - or it can not
                        //   be reliably distinguished from zero.
                        // * step is unconstrained
                        //
                        // If these conditions are true, we abnormally terminate QP
                        // algorithm with return code -4
                        //
                        terminationtype = -4;
                        break;
                    }
                    
                    //
                    // Perform step along DC.
                    //
                    // In this block of code we maintain two step length:
                    // * RestStp -  restricted step, maximum step length along DC which does
                    //              not violate constraints
                    // * FullStp -  step length along DC which minimizes quadratic function
                    //              without taking constraints into account. If problem is
                    //              unbounded from below without constraints, FullStp is
                    //              forced to be RestStp.
                    //
                    // So, if function is convex (D2>0):
                    // * FullStp = -D1/(2*D2)
                    // * RestStp = restricted FullStp
                    // * 0<=RestStp<=FullStp
                    //
                    // If function is non-convex, but bounded from below under constraints:
                    // * RestStp = step length subject to constraints
                    // * FullStp = RestStp
                    //
                    // After RestStp and FullStp are initialized, we generate several trial
                    // steps which are different multiples of RestStp and FullStp.
                    //
                    if( d2est>0 )
                    {
                        alglib.ap.assert((double)(d1)<(double)(0), "QQPOptimize: internal error");
                        fullstp = -(d1/(2*d2));
                        needact = (double)(fullstp)>=(double)(stpmax);
                        if( needact )
                        {
                            alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=3, "QQPOptimize: StpBuf overflow");
                            reststp = stpmax;
                            stp = reststp;
                            sstate.stpbuf[0] = reststp*4;
                            sstate.stpbuf[1] = fullstp;
                            sstate.stpbuf[2] = fullstp/4;
                            stpcnt = 3;
                        }
                        else
                        {
                            reststp = fullstp;
                            stp = fullstp;
                            stpcnt = 0;
                        }
                    }
                    else
                    {
                        alglib.ap.assert(cidx>=0, "QQPOptimize: internal error");
                        alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=2, "QQPOptimize: StpBuf overflow");
                        reststp = stpmax;
                        fullstp = stpmax;
                        stp = reststp;
                        needact = true;
                        sstate.stpbuf[0] = 4*reststp;
                        stpcnt = 1;
                    }
                    findbeststepandmove(sstate, sstate.sas, sstate.dc, stp, needact, cidx, cval, sstate.stpbuf, stpcnt, ref sstate.activated, ref sstate.tmp0);
                    
                    //
                    // Update CG information.
                    //
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.dp[i_] = sstate.dc[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sstate.cgp[i_] = sstate.cgc[i_];
                    }
                    
                    //
                    // Update iterations counter
                    //
                    sstate.repinneriterationscount = sstate.repinneriterationscount+1;
                }
                if( terminationtype!=0 )
                {
                    break;
                }
                cgmax = settings.cgmaxits;
                
                //
                // Generate YIdx - reordering of variables for constrained Newton phase.
                // Free variables come first, fixed are last ones.
                //
                newtcnt = 0;
                while( true )
                {
                    
                    //
                    // Skip iteration if constrained Newton is turned off.
                    //
                    if( !settings.cnphase )
                    {
                        break;
                    }
                    
                    //
                    // At the first iteration   - build Cholesky decomposition of Hessian.
                    // At subsequent iterations - refine Hessian by adding new constraints.
                    //
                    // Loop is terminated in following cases:
                    // * Hessian is not positive definite subject to current constraints
                    //   (termination during initial decomposition)
                    // * there were no new constraints being activated
                    //   (termination during update)
                    // * all constraints were activated during last step
                    //   (termination during update)
                    // * CNMaxUpdates were performed on matrix
                    //   (termination during update)
                    //
                    if( newtcnt==0 )
                    {
                        
                        //
                        // Perform initial Newton step. If Cholesky decomposition fails,
                        // increase number of CG iterations to CGMaxIts - it should help
                        // us to find set of constraints which will make matrix positive
                        // definite.
                        //
                        b = cnewtonbuild(sstate, sparsesolver, ref sstate.repncholesky);
                        if( b )
                        {
                            cgmax = settings.cgminits;
                        }
                    }
                    else
                    {
                        b = cnewtonupdate(sstate, settings, ref sstate.repncupdates);
                    }
                    if( !b )
                    {
                        break;
                    }
                    apserv.inc(ref newtcnt);
                    
                    //
                    // Calculate gradient GC.
                    //
                    targetgradient(sstate, sstate.sas.xc, ref sstate.gc);
                    
                    //
                    // Bound-constrained Newton step
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        sstate.dc[i] = sstate.gc[i];
                    }
                    if( !cnewtonstep(sstate, settings, sstate.dc) )
                    {
                        break;
                    }
                    quadraticmodel(sstate, sstate.sas.xc, sstate.dc, sstate.gc, ref d1, ref d1est, ref d2, ref d2est);
                    if( d1est>=0 || d2est<=0 )
                    {
                        break;
                    }
                    alglib.ap.assert((double)(d1)<(double)(0), "QQPOptimize: internal error");
                    fullstp = -(d1/(2*d2));
                    sactivesets.sasexploredirection(sstate.sas, sstate.dc, ref stpmax, ref cidx, ref cval);
                    needact = (double)(fullstp)>=(double)(stpmax);
                    if( needact )
                    {
                        alglib.ap.assert(alglib.ap.len(sstate.stpbuf)>=3, "QQPOptimize: StpBuf overflow");
                        reststp = stpmax;
                        stp = reststp;
                        sstate.stpbuf[0] = reststp*4;
                        sstate.stpbuf[1] = fullstp;
                        sstate.stpbuf[2] = fullstp/4;
                        stpcnt = 3;
                    }
                    else
                    {
                        reststp = fullstp;
                        stp = fullstp;
                        stpcnt = 0;
                    }
                    findbeststepandmove(sstate, sstate.sas, sstate.dc, stp, needact, cidx, cval, sstate.stpbuf, stpcnt, ref sstate.activated, ref sstate.tmp0);
                }
                if( terminationtype!=0 )
                {
                    break;
                }
            }
            
            //
            // Stop optimization and unpack results.
            //
            // Add XOriginC to XS and make sure that boundary constraints are
            // both (a) satisfied, (b) preserved. Former means that "shifted"
            // point is feasible, while latter means that point which was exactly
            // at the boundary before shift will be exactly at the boundary
            // after shift.
            //
            sactivesets.sasstopoptimization(sstate.sas);
            for(i=0; i<=nmain-1; i++)
            {
                xs[i] = sc[i]*sstate.sas.xc[i]+xoriginc[i];
                if( sstate.havebndl[i] && (double)(xs[i])<(double)(bndlc[i]) )
                {
                    xs[i] = bndlc[i];
                }
                if( sstate.havebndu[i] && (double)(xs[i])>(double)(bnduc[i]) )
                {
                    xs[i] = bnduc[i];
                }
                if( sstate.havebndl[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndl[i]) )
                {
                    xs[i] = bndlc[i];
                }
                if( sstate.havebndu[i] && (double)(sstate.sas.xc[i])==(double)(sstate.bndu[i]) )
                {
                    xs[i] = bnduc[i];
                }
            }
        }
Example #9
0
 public override alglib.apobject make_copy()
 {
     qqpbuffers _result = new qqpbuffers();
     _result.n = n;
     _result.nmain = nmain;
     _result.nslack = nslack;
     _result.nec = nec;
     _result.nic = nic;
     _result.akind = akind;
     _result.densea = (double[,])densea.Clone();
     _result.sparsea = (sparse.sparsematrix)sparsea.make_copy();
     _result.sparseupper = sparseupper;
     _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.havebndl = (bool[])havebndl.Clone();
     _result.havebndu = (bool[])havebndu.Clone();
     _result.cleic = (double[,])cleic.Clone();
     _result.xs = (double[])xs.Clone();
     _result.gc = (double[])gc.Clone();
     _result.xp = (double[])xp.Clone();
     _result.dc = (double[])dc.Clone();
     _result.dp = (double[])dp.Clone();
     _result.cgc = (double[])cgc.Clone();
     _result.cgp = (double[])cgp.Clone();
     _result.sas = (sactivesets.sactiveset)sas.make_copy();
     _result.activated = (bool[])activated.Clone();
     _result.nfree = nfree;
     _result.cnmodelage = cnmodelage;
     _result.densez = (double[,])densez.Clone();
     _result.sparsecca = (sparse.sparsematrix)sparsecca.make_copy();
     _result.yidx = (int[])yidx.Clone();
     _result.regdiag = (double[])regdiag.Clone();
     _result.regx0 = (double[])regx0.Clone();
     _result.tmpcn = (double[])tmpcn.Clone();
     _result.tmpcni = (int[])tmpcni.Clone();
     _result.tmpcnb = (bool[])tmpcnb.Clone();
     _result.tmp0 = (double[])tmp0.Clone();
     _result.stpbuf = (double[])stpbuf.Clone();
     _result.sbuf = (sparse.sparsebuffers)sbuf.make_copy();
     _result.repinneriterationscount = repinneriterationscount;
     _result.repouteriterationscount = repouteriterationscount;
     _result.repncholesky = repncholesky;
     _result.repncupdates = repncupdates;
     return _result;
 }