Exemple #1
0
    def SolveOuter(self, **kwargs):

        nlp = self.nlp
        n = nlp.n

        err = min(nlp.stop_d, nlp.stop_c, nlp.stop_p)
        self.mu_min = min(self.mu_min, (err / (1 + self.muerrfact)) / 2)

        # Measure solve time
        t = cputime()

        # Solve sequence of inner iterations
        while (not self.optimal) and (self.mu >= self.mu_min) and \
                (self.iter < self.maxiter):
            self.SolveInner()  #stopTol=max(1.0e-7, 5*self.mu))
            #self.z = self.PrimalMultipliers(self.x)
            #res, self.optimal = self.AtOptimality(self.x, self.z)
            self.UpdateMu()

        self.tsolve = cputime() - t  # Solve time
        if self.optimal:
            print 'First-order optimal solution found'
        elif self.iter >= self.maxiter:
            print 'Maximum number of iterations reached'
        else:
            print 'Reached smallest allowed value of barrier parameter'
            #print '(mu = %8.2e, mu_min = %8.2e)' % (self.mu, self.mu_min)
        return
Exemple #2
0
def gltr_implicit(H, g, **kwargs):
    G = pygltr.PyGltrContext(g, **kwargs)
    H.to_csr()
    t = cputime()
    G.implicit_solve(lambda v: MatVecProd(H,v))
    t = cputime() - t
    return (G.m, G.mult, G.snorm, G.niter, G.nc, G.ierr, t)
Exemple #3
0
def gltr_implicit(H, g, **kwargs):
    G = pygltr.PyGltrContext(g, **kwargs)
    H.to_csr()
    t = cputime()
    G.implicit_solve(lambda v: MatVecProd(H, v))
    t = cputime() - t
    return (G.m, G.mult, G.snorm, G.niter, G.nc, G.ierr, t)
Exemple #4
0
    def SolveOuter(self, **kwargs):

        nlp = self.nlp
        n = nlp.n

        err = min(nlp.stop_d, nlp.stop_c, nlp.stop_p)
        self.mu_min = min(self.mu_min, (err/(1 + self.muerrfact))/2)

        # Measure solve time
        t = cputime()

        # Solve sequence of inner iterations
        while (not self.optimal) and (self.mu >= self.mu_min) and \
                (self.iter < self.maxiter):
            self.SolveInner() #stopTol=max(1.0e-7, 5*self.mu))
            #self.z = self.PrimalMultipliers(self.x)
            #res, self.optimal = self.AtOptimality(self.x, self.z)
            self.UpdateMu()

        self.tsolve = cputime() - t    # Solve time
        if self.optimal:
            print 'First-order optimal solution found'
        elif self.iter >= self.maxiter:
            print 'Maximum number of iterations reached'
        else:
            print 'Reached smallest allowed value of barrier parameter'
            #print '(mu = %8.2e, mu_min = %8.2e)' % (self.mu, self.mu_min)
        return
Exemple #5
0
    def solve(self):

        tstart = cputime()

        # Initial LBFGS matrix is the identity. In other words,
        # the initial search direction is the steepest descent direction.

        # This is the original L-BFGS stopping condition.
        #stoptol = self.nlp.stop_d * max(1.0, norms.norm2(self.x))
        stoptol = max(self.abstol, self.reltol * self.g0)

        while self.gnorm > stoptol and self.iter < self.maxiter:

            if not self.silent:
                print '%-5d  %-12g  %-12g' % (self.iter, self.f, self.gnorm)

            # Obtain search direction
            d = self.lbfgs.matvec(-self.g)

            # Prepare for modified More-Thuente linesearch
            if self.iter == 0:
                stp0 = 1.0/self.gnorm
            else:
                stp0 = 1.0
            SWLS = StrongWolfeLineSearch(self.f,
                                         self.x,
                                         self.g,
                                         d,
                                         lambda z: self.nlp.obj(z),
                                         lambda z: self.nlp.grad(z),
                                         stp = stp0)
            # Perform linesearch
            SWLS.search()

            # SWLS.x  contains the new iterate
            # SWLS.g  contains the objective gradient at the new iterate
            # SWLS.f  contains the objective value at the new iterate
            s = SWLS.x - self.x
            self.x = SWLS.x
            y = SWLS.g - self.g
            self.g = SWLS.g
            self.gnorm = norms.norm2(self.g)
            self.f = SWLS.f
            #stoptol = self.nlp.stop_d * max(1.0, norms.norm2(self.x))

            # Update inverse Hessian approximation using the most recent pair
            self.lbfgs.store(s, y)
            self.iter += 1

            # Code for testing the LBFGS implementation
            self.alt_lbfgs.store(s, y)

        self.tsolve = cputime() - tstart
        self.converged = (self.iter < self.maxiter)
Exemple #6
0
    def solve(self):

        tstart = cputime()

        # Initial LBFGS matrix is the identity. In other words,
        # the initial search direction is the steepest descent direction.

        # This is the original L-BFGS stopping condition.
        #stoptol = self.nlp.stop_d * max(1.0, norms.norm2(self.x))
        stoptol = max(self.abstol, self.reltol * self.g0)

        while self.gnorm > stoptol and self.iter < self.maxiter:

            if not self.silent:
                print '%-5d  %-12g  %-12g' % (self.iter, self.f, self.gnorm)

            # Obtain search direction
            d = self.lbfgs.matvec(-self.g)

            # Prepare for modified More-Thuente linesearch
            if self.iter == 0:
                stp0 = 1.0 / self.gnorm
            else:
                stp0 = 1.0
            SWLS = StrongWolfeLineSearch(self.f,
                                         self.x,
                                         self.g,
                                         d,
                                         lambda z: self.nlp.obj(z),
                                         lambda z: self.nlp.grad(z),
                                         stp=stp0)
            # Perform linesearch
            SWLS.search()

            # SWLS.x  contains the new iterate
            # SWLS.g  contains the objective gradient at the new iterate
            # SWLS.f  contains the objective value at the new iterate
            s = SWLS.x - self.x
            self.x = SWLS.x
            y = SWLS.g - self.g
            self.g = SWLS.g
            self.gnorm = norms.norm2(self.g)
            self.f = SWLS.f
            #stoptol = self.nlp.stop_d * max(1.0, norms.norm2(self.x))

            # Update inverse Hessian approximation using the most recent pair
            self.lbfgs.store(s, y)
            self.iter += 1

        self.tsolve = cputime() - tstart
        self.converged = (self.iter < self.maxiter)
Exemple #7
0
def pass_to_trunk(nlp, **kwargs):

    if nlp.nbounds > 0 or nlp.m > 0:         # Check for unconstrained problem
        sys.stderr.write('%s has %d bounds and %d general constraints\n' % (ProblemName, nlp.nbounds, nlp.m))
        return None

    verbose = kwargs.get('verbose', False)

    t = cputime()
    tr = TR(Delta=1.0, eta1=0.05, eta2=0.9, gamma1=0.25, gamma2=2.5)

    # When instantiating TrunkFramework of TrunkLbfgsFramework,
    # we select a trust-region subproblem solver of our choice.
    TRNK = solver(nlp, tr, TRSolver, **kwargs)
    TRNK.TR.Delta = 0.1 * TRNK.gnorm         # Reset initial trust-region radius
    t_setup = cputime() - t                  # Setup time

    if verbose:
        print
        print '------------------------------------------'
        print 'LDFP: Solving problem %-s with parameters' % ProblemName
        hdr = 'eta1 = %-g  eta2 = %-g  gamma1 = %-g  gamma2 = %-g Delta0 = %-g'
        print hdr % (tr.eta1, tr.eta2, tr.gamma1, tr.gamma2, tr.Delta)
        print '------------------------------------------'
        print

    TRNK.Solve()

    # Output final statistics
    if verbose:
        print
        print 'Final variables:', TRNK.x
        print
        print '-------------------------------'
        print 'Trunk: End of Execution'
        print '  Problem                     : %-s' % ProblemName
        print '  Dimension                   : %-d' % nlp.n
        print '  Initial/Final Objective     : %-g/%-g' % (TRNK.f0, TRNK.f)
        print '  Initial/Final Gradient Norm : %-g/%-g' % (TRNK.g0, TRNK.gnorm)
        print '  Number of iterations        : %-d' % TRNK.iter
        print '  Number of function evals    : %-d' % TRNK.nlp.feval
        print '  Number of gradient evals    : %-d' % TRNK.nlp.geval
        print '  Number of Hessian  evals    : %-d' % TRNK.nlp.Heval
        print '  Number of matvec products   : %-d' % TRNK.nlp.Hprod
        print '  Total/Average Lanczos iter  : %-d/%-g' % (TRNK.cgiter, (float(TRNK.cgiter)/TRNK.iter))
        print '  Setup/Solve time            : %-gs/%-gs' % (t_setup, TRNK.tsolve)
        print '  Total time                  : %-gs' % (t_setup + TRNK.tsolve)
        print '-------------------------------'

    return (t_setup, TRNK)
Exemple #8
0
 def FindFeasible(self):
     """
     If rhs was specified, obtain x_feasible satisfying the constraints
     """
     n = self.n
     if self.debug: self._write('Obtaining feasible solution...\n')
     self.t_feasible = cputime()
     self.rhs[n:] = self.b
     self.Proj.solve(self.rhs)
     self.x_feasible = self.Proj.x[:n].copy()
     self.t_feasible = cputime() - self.t_feasible
     self.CheckAccurate()
     if self.debug:
         self._write(' done (%-5.2fs)\n' % self.t_feasible)
     return
Exemple #9
0
 def FindFeasible(self):
     """
     If rhs was specified, obtain x_feasible satisfying the constraints
     """
     n = self.n
     if self.debug: self._write('Obtaining feasible solution...\n')
     self.t_feasible = cputime()
     self.rhs[n:] = self.b
     self.Proj.solve(self.rhs)
     self.x_feasible = self.Proj.x[:n].copy()
     self.t_feasible = cputime() - self.t_feasible
     self.CheckAccurate()
     if self.debug:
         self._write(' done (%-5.2fs)\n' % self.t_feasible)
     return
Exemple #10
0
def pass_to_funnel(nlp, **kwargs):

    verbose = (kwargs['print_level'] == 2)
    qn = kwargs.pop('quasi_newton')

    t = cputime()
    if qn:
        funn = StructuredLDFPFunnel(nlp, logger_name='funnel.solver', **kwargs)
        # funn = LDFPFunnel(nlp, logger_name='funnel.solver', **kwargs)
    else:
        funn = Funnel(nlp, logger_name='funnel.solver', **kwargs)
    t_setup = cputime() - t    # Setup time.

    funn.solve() #reg=1.0e-8)

    return (t_setup, funn)
Exemple #11
0
def pass_to_trunk(nlp, showbanner=True):

    if nlp.nbounds > 0 or nlp.m > 0:  # Check for unconstrained problem
        sys.stderr.write('%s has %d bounds and %d general constraints\n' %
                         (ProblemName, nlp.nbounds, nlp.m))
        return None

    t = cputime()
    tr = TR(Delta=1.0, eta1=0.05, eta2=0.9, gamma1=0.25, gamma2=2.5)

    # When instantiating TrunkFramework of TrunkLbfgsFramework,
    # we select a trust-region subproblem solver of our choice.
    TRNK = solver(nlp, tr, TRSolver, silent=False, ny=True, inexact=True)
    t_setup = cputime() - t  # Setup time

    if showbanner:
        print
        print '------------------------------------------'
        print 'Trunk: Solving problem %-s with parameters' % ProblemName
        hdr = 'eta1 = %-g  eta2 = %-g  gamma1 = %-g  gamma2 = %-g Delta0 = %-g'
        print hdr % (tr.eta1, tr.eta2, tr.gamma1, tr.gamma2, tr.Delta)
        print '------------------------------------------'
        print

    TRNK.Solve()

    # Output final statistics
    print
    print 'Final variables:', TRNK.x
    print
    print '-------------------------------'
    print 'Trunk: End of Execution'
    print '  Problem                     : %-s' % ProblemName
    print '  Dimension                   : %-d' % nlp.n
    print '  Initial/Final Objective     : %-g/%-g' % (TRNK.f0, TRNK.f)
    print '  Initial/Final Gradient Norm : %-g/%-g' % (TRNK.g0, TRNK.gnorm)
    print '  Number of iterations        : %-d' % TRNK.iter
    print '  Number of function evals    : %-d' % TRNK.nlp.feval
    print '  Number of gradient evals    : %-d' % TRNK.nlp.geval
    print '  Number of Hessian  evals    : %-d' % TRNK.nlp.Heval
    print '  Number of matvec products   : %-d' % TRNK.nlp.Hprod
    print '  Total/Average Lanczos iter  : %-d/%-g' % (TRNK.total_cgiter, (
        float(TRNK.total_cgiter) / TRNK.iter))
    print '  Setup/Solve time            : %-gs/%-gs' % (t_setup, TRNK.tsolve)
    print '  Total time                  : %-gs' % (t_setup + TRNK.tsolve)
    print '-------------------------------'
    return TRNK
Exemple #12
0
def pass_to_elastic(nlp, **kwargs):

    verbose = (kwargs.get('print_level') >= 2)
    kwargs.pop('print_level')
    maxiter = kwargs.get('maxit', 200)

    t = cputime()
    eif = EIF(nlp, maxiter=maxiter, **kwargs)
    t_setup = cputime() - t  # Setup time.

    if verbose:
        nlp.display_basic_info()

    try:
        eif.solve()
    except:
        pass

    return (t_setup, eif)
Exemple #13
0
def pass_to_elastic(nlp, **kwargs):

    verbose = (kwargs.get('print_level') >= 2)
    kwargs.pop('print_level')
    maxiter = kwargs.get('maxit',200)

    t = cputime()
    eif = EIF(nlp, maxiter=maxiter, **kwargs)
    t_setup = cputime() - t    # Setup time.

    if verbose:
        nlp.display_basic_info()

    try:
        eif.solve()
    except:
        pass

    return (t_setup, eif)
Exemple #14
0
    def Factorize(self):
        """
        Assemble projection matrix and factorize it

           P = [ G   A^T ]
               [ A    0  ],

        where G is the preconditioner, or the identity matrix if no
        preconditioner was given.
        """
        if self.A is None:
            raise ValueError, 'No linear equality constraints were specified'

        # Form projection matrix
        P = spmatrix.ll_mat_sym(self.n + self.m, self.nnzA + self.n)
        if self.precon is not None:
            P[:self.n,:self.n] = self.precon
        else:
            r = range(self.n)
            P.put(1, r, r)
            #for i in range(self.n):
            #    P[i,i] = 1
        P[self.n:,:self.n] = self.A

        # Add regularization if requested.
        if self.dreg > 0.0:
            r = range(self.n, self.n + self.m)
            P.put(-self.dreg, r, r)

        if self.debug:
                msg = 'Factorizing projection matrix '
                msg += '(size %-d, nnz = %-d)...\n' %  (P.shape[0],P.nnz)
                self._write(msg)
        self.t_fact = cputime()
        self.Proj = LBLContext(P)
        self.t_fact = cputime() - self.t_fact
        if self.debug:
                msg = ' done (%-5.2fs)\n' % self.t_fact
                self._write(msg)
        self.factorized = True
        return
Exemple #15
0
    def Factorize(self):
        """
        Assemble projection matrix and factorize it

           P = [ G   A^T ]
               [ A    0  ],

        where G is the preconditioner, or the identity matrix if no
        preconditioner was given.
        """
        if self.A is None:
            raise ValueError, 'No linear equality constraints were specified'

        # Form projection matrix
        P = spmatrix.ll_mat_sym(self.n + self.m, self.nnzA + self.n)
        if self.precon is not None:
            P[:self.n, :self.n] = self.precon
        else:
            r = range(self.n)
            P.put(1, r, r)
            #for i in range(self.n):
            #    P[i,i] = 1
        P[self.n:, :self.n] = self.A

        # Add regularization if requested.
        if self.dreg > 0.0:
            r = range(self.n, self.n + self.m)
            P.put(-self.dreg, r, r)

        if self.debug:
            msg = 'Factorizing projection matrix '
            msg += '(size %-d, nnz = %-d)...\n' % (P.shape[0], P.nnz)
            self._write(msg)
        self.t_fact = cputime()
        self.Proj = LBLContext(P)
        self.t_fact = cputime() - self.t_fact
        if self.debug:
            msg = ' done (%-5.2fs)\n' % self.t_fact
            self._write(msg)
        self.factorized = True
        return
Exemple #16
0
def SolveSystem(A, rhs, itref_threshold=1.0e-6, nitrefmax=5, **kwargs):

    # Obtain Sils context object
    t = cputime()
    LBL = LBLContext(A, **kwargs)
    t_analyze = cputime() - t

    # Solve system and compute residual
    t = cputime()
    LBL.solve(rhs)
    t_solve = cputime() - t_analyze

    # Compute residual norm
    nrhsp1 = norms.norm_infty(rhs) + 1
    nr = norms.norm2(LBL.residual)/nrhsp1

    # If residual not small, perform iterative refinement
    LBL.refine(rhs, tol = itref_threshold, nitref=nitrefmax)
    nr1 = norms.norm_infty(LBL.residual)/nrhsp1

    return (LBL.x, LBL.residual, nr, nr1, t_analyze, t_solve, LBL.neig)
Exemple #17
0
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:
          :itermax:  The maximum allowed number of iterations (default: 10n)

          :tolerance:  Stopping tolerance (default: 1.0e-6)

          :PredictorCorrector:  Use the predictor-corrector method
                                (default: `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        Upon exit, the following members of the class instance are set:

        x..............final iterate
        y..............final value of the Lagrange multipliers associated
                       to A1 x + A2 s = b
        z..............final value of the Lagrange multipliers associated
                       to s>=0
        obj_value......final cost
        iter...........total number of iterations
        kktResid.......final relative residual
        solve_time.....time to solve the LP
        status.........string describing the exit status
        short_status...short version of status, used for printing.
        """
        lp = self.lp
        itermax = kwargs.get('itermax', max(100,10*lp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape ; on = lp.original_n
        A = self.A ; b = self.b ; c = self.c ; H = self.H
        regpr = self.regpr ; regdu = self.regdu
        regpr_min = self.regpr_min ; regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        # set_initial_guess() initializes self.LBL which is reused below.
        (x,y,z) = self.set_initial_guess(self.lp, **kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:] ; ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        col_scale = np.empty(n)

        # Allocate room for right-hand side of linear systems.
        rhs = np.zeros(n+m)
        finished = False
        iter = 0

        # Acceptance thresholds for primal and dual reg parameters.
        #t1 = t2 = 0.99

        solve_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if self.verbose and iter % 20 == 0:
                sys.stdout.write('\n' + self.header + '\n')
                sys.stdout.write('-' * len(self.header) + '\n')

            # Compute residuals.
            pFeas = A*x - b
            comp = s*z ; sz = sum(comp)                     # comp   = S z
            dFeas = y*A ; dFeas[:on] -= self.c              # dFeas1 = A1'y - c
            dFeas[on:] += z                                 # dFeas2 = A2'y + z
            mu = sz/ns

            # Compute residual norms and scaled residual norms.
            # We don't need to keep both the scaled and unscaled residuals
            # store.
            #pResid = norm_infty(pFeas + regdu * r)/(1+self.normc)
            #dResid = norm_infty(dFeas - regpr * q)/(1+self.normb)
            pResid = norm2(pFeas) ; spResid = pResid/(1+self.normc)
            cResid = norm2(comp)  ; scResid = cResid/self.normbc
            dResid = norm2(dFeas) ; sdResid = dResid/(1+self.normb)

            # Compute relative duality gap.
            cx = np.dot(c,x[:on])
            by = np.dot(b,y)
            rgap  = cx - by
            #rgap += regdu * (rNorm**2 + np.dot(r,y))
            rgap  = abs(rgap) / (1 + abs(cx))
            rgap2 = mu /(1 + abs(cx))

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)
            #kktResid = max(pResid, cResid, dResid)

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status= 'iter'
                finished = True
                continue

            # Adjust regularization parameters
            #mu = sum(comp)/ns
            #if mu < 1:
            #    regpr = sqrt(mu)
            #    regdu = sqrt(mu)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            if iter == 0:
                regpr = self.regpr ; regdu = self.regdu
                if regpr > 0:
                    q = dFeas/regpr ; qNorm = norm2(q) ; rho_q = regpr * qNorm
                else:
                    q = dFeas ; qNorm = norm2(q) ; rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas/regdu ; rNorm = norm2(r) ; del_r = regdu * rNorm
                else:
                    r = -pFeas ; rNorm = norm2(r) ; del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu
            else:
                # Adjust regularization parameters.
                #regpr = max(min(regpr/10, regpr**(1.1)), regpr_min)
                #regdu = max(min(regdu/10, regdu**(1.1)), regdu_min)
                # 1) rho+ |dx| <= const * s'z
                # 2) del+ |dy| <= const * s'z
                if regdu > 0:
                    regdu = min(regdu/10, sz/normdy/10, (sz/normdy)**(1.1))
                    regdu = max(regdu, regdu_min)
                if regpr > 0:
                    regpr = min(regpr/10, sz/normdx/10, (sz/normdx)**(1.1))
                    regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    #if mu < 1.0e-8 * mu0 and rho_q > 1.0e+3 * kktResid * self.normbc: #* mu * self.normbc:
                    if mu < 1.0e-8 * mu0 and rho_q > 1.0e+2 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter-1:
                            if pr_infeas_count > 6:
                                status = 'Problem seems to be (locally) dual infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter

                    #if mu < 1.0e-8 * mu0 and del_r > 1.0e+3 * kktResid * self.normbc: # * mu * self.normbc:
                    if mu < 1.0e-8 * mu0 and del_r > 1.0e+2 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter-1:
                            if du_infeas_count > 6:
                                status='Problem seems to be (locally) primal infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter

            # Display objective and residual data.
            if self.verbose:
                sys.stdout.write(self.format1 % (iter, cx, pResid, dResid,
                                                 cResid, rgap, qNorm, rNorm))

            # Record some quantities for display
            mins = np.min(s)
            minz = np.min(z)
            maxs = np.max(s)

            # Repeatedly assemble system and compute step until primal and
            # dual regularization parameters have appropriate values.

            # Reset primal and dual regularization parameters to best guess
            #if iter > 0:
            #    regpr = max(regpr_min, 0.5*sigma*dResid/normds)
            #    regdu = max(regdu_min, 0.5*sigma*pResid/normdy)

            step_acceptable = False

            while not step_acceptable:

                # Solve the linear system
                #
                # [-pI          0          A1'] [∆x]   [c - A1' y             ]
                # [ 0   -(S^{-1} Z + pI)   A2'] [∆s] = [  - A2' y - µ S^{-1} e]
                # [ A1          A2         dI ] [∆y]   [b - A1 x - A2 s       ]
                #
                # where s are the slack variables, p is the primal
                # regularization parameter, d is the dual regularization
                # parameter, and  A = [ A1  A2 ]  where the columns of A1
                # correspond to the original problem variables and those of A2
                # correspond to slack variables.
                #
                # We recover ∆z = -z - S^{-1} (Z ∆s + µ e).
                # Compute augmented matrix and factorize it.
                factorized = False
                nb_bump = 0
                while not factorized and nb_bump < 5:

                    if self.stabilize:
                        col_scale[:on] = sqrt(regpr)
                        col_scale[on:] = np.sqrt(z/s + regpr)
                        H.put(-sqrt(regdu), range(n))
                        H.put( sqrt(regdu), range(n,n+m))
                        AA = self.A.copy()
                        AA.col_scale(1/col_scale)
                        H[n:,:n] = AA
                    else:
                        if regpr > 0: H.put(-regpr,       range(on))
                        H.put(-z/s - regpr, range(on,n))
                        if regdu > 0: H.put(regdu,        range(n,n+m))

                    #if iter == 5:
                    #    # Export current matrix to file for futher inspection.
                    #    import os
                    #    name = os.path.basename(self.lp.name)
                    #    fname = '.'.join(name.split('.')[:-1]) + '.mtx'
                    #    H.exportMmf(fname)

                    self.LBL.factorize(H)
                    factorized = True

                    # If the augmented matrix does not have full rank, bump up
                    # regularization parameters.
                    if not self.LBL.isFullRank:
                        if self.verbose:
                            sys.stderr.write('Primal-Dual Matrix ')
                            sys.stderr.write('Rank Deficient')
                        if regdu == 0.0:
                            sys.stderr.write('... No regularization in effect')
                            sys.stderr.write('... bailing out\n')
                            factorized = False
                            nb_bump = 5
                            continue
                        else:
                            sys.stderr.write('... bumping up reg parameters\n')
                        regpr *= 10 ; regdu *= 10
                        nb_bump += 1
                        factorized = False

                # Abandon if regularization is unsuccessful.
                if not self.LBL.isFullRank and nb_bump >= 5:
                    status = 'Unable to regularize sufficiently.'
                    short_status = 'degn'
                    finished = True
                    continue  # Does this get us out of the outer while?

                # Compute duality measure.
                mu = sz/ns

                if PredictorCorrector:
                    # Use Mehrotra predictor-corrector method.
                    # Compute affine-scaling step, i.e. with centering = 0.
                    rhs[:n]    = -dFeas
                    rhs[on:n] += z
                    rhs[n:]    = -pFeas

                    # if 'stabilize' is on, must scale right-hand side.
                    if self.stabilize:
                        rhs[:n] /= col_scale
                        rhs[n:] /= sqrt(regdu)

                    (step, nres, neig) = self.solveSystem(rhs)

                    # Unscale step if 'stabilize' is on.
                    if self.stabilize:
                        step[:n] *= sqrt(regdu) / col_scale

                    # Recover dx and dz.
                    dx = step[:n]
                    ds = dx[on:]
                    dz = -z * (1 + ds/s)

                    # Compute largest allowed primal and dual stepsizes.
                    (alpha_p, ip) = self.maxStepLength(s, ds)
                    (alpha_d, ip) = self.maxStepLength(z, dz)

                    # Estimate duality gap after affine-scaling step.
                    muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns
                    sigma = (muAff/mu)**3

                    # Incorporate predictor information for corrector step.
                    comp += ds*dz
                else:
                    # Use long-step method: Compute centering parameter.
                    sigma = min(0.1, 100*mu)

                # Assemble right-hand side with centering information.
                comp -= sigma * mu

                if PredictorCorrector:
                    # Only update rhs[on:n]; the rest of rhs did not change.
                    if self.stabilize:
                        rhs[on:n] += (comp/s - z)/col_scale[on:n]
                    else:
                        rhs[on:n] += comp/s - z
                else:
                    rhs[:n]    = -dFeas
                    rhs[on:n] += comp/s
                    rhs[n:]    = -pFeas

                    # If 'stabilize' is on, must scale right-hand side.
                    # In the predictor-corrector method, this has already been
                    # done.
                    if self.stabilize:
                        rhs[:n] /= col_scale
                        rhs[n:] /= sqrt(regdu)

                # Solve augmented system.
                (step, nres, neig) = self.solveSystem(rhs)

                # Unscale step if 'stabilize' is on.
                if self.stabilize:
                    step[:n] *= sqrt(regdu) / col_scale

                # Recover step.
                dx = step[:n]
                ds = dx[on:]
                dy = step[n:]

                normds = norm2(ds) ; normdy = norm2(dy) ; normdx = norm2(dx)
                step_acceptable = True  # Must get rid of this

            # End while not step_acceptable

            # Recover step in z.
            dz = -(comp + z*ds)/s

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0-mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult*mu_tmp - s[ip]*zip)/(alpha_p*ds[ip]*zip)
                    alpha_p *= max(1-mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult*mu_tmp - z[id]*sid)/(alpha_d*dz[id]*sid)
                    alpha_d *= max(1-mult, gamma_d)

                if ip==id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            if self.verbose:
                sys.stdout.write(self.format2 % (mu, alpha_p, alpha_d,
                                                 nres, regpr, regdu, rho_q,
                                                 del_r, mins, minz, maxs))

            # Update primal variables and slacks.
            x += alpha_p * dx

            # Update dual variables.
            y += alpha_d * dy
            z += alpha_d * dz

            # Update perturbation vectors.
            q *= (1-alpha_p) ; q += alpha_p * dx
            r *= (1-alpha_d) ; r += alpha_d * dy
            qNorm = norm2(q) ; rNorm = norm2(r)
            rho_q = regpr * qNorm/(1+self.normc) ; rho_q_min = min(rho_q_min, rho_q)
            del_r = regdu * rNorm/(1+self.normb) ; del_r_min = min(del_r_min, del_r)

            iter += 1

        solve_time = cputime() - solve_time

        if self.verbose:
            sys.stdout.write('\n')
            sys.stdout.write('-' * len(self.header) + '\n')

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid ; self.cResid = cResid ; self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = np.dot(self.c, x[:on]) + self.c0
        return
Exemple #18
0
    def Solve(self):

        # Find feasible solution
        if self.A is not None:
            if self.factorize and not self.factorized: self.Factorize()
            if self.b is not None: self.FindFeasible()

        n = self.n
        m = self.m
        nMatvec = 0
        alpha = beta = omega = 0.0

        self.t_solve = cputime()

        # Obtain fixed vector r0 = projected initial residual
        # (initial x = 0 in homogeneous problem.)
        if self.A is not None:
            self.rhs[:n] = self.r
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            r0 = self.Proj.x[:n].copy()
            Btv = self.r - r0
        else:
            r0 = self.c

        # Initialize search direction
        self.p = self.r

        # Further initializations
        rr0 = rr00 = numpy.dot(self.r, r0)
        residNorm = self.residNorm0 = sqrt(rr0)
        stopTol = self.abstol + self.reltol * self.residNorm0
        finished = False

        if self.debug:
            self._write(self.header)
            self._write('-' * len(self.header) + '\n')

        if self.debug:
            self._write(self.fmt % (nMatvec, residNorm, rr0, alpha, omega))

        while not finished:

            # Project p
            self.rhs[:n] = self.p
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.Pp = self.Proj.x[:n]

            # Compute alpha and s
            if self._matvec_found:
                # Here we must copy Ap to prevent it from being overwritten
                # in the next matvec. We still need Ap when we update p below.
                self.Ap = self.matvec(self.Pp).copy()
            else:
                self.H.matvec(self.Pp, self.Ap)
            nMatvec += 1

            alpha = rr0 / numpy.dot(r0, self.Ap)
            self.s = self.r - alpha * self.Ap

            # Project s
            self.rhs[:n] = self.s - Btv  # Iterative semi-refinement
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.Ps = self.Proj.x[:n].copy()
            Btv = self.s - self.Ps

            residNorm = sqrt(numpy.dot(self.s, self.Ps))

            # Test for termination in the CGS process
            if residNorm <= stopTol or nMatvec > self.nMatvecMax:
                self.x += alpha * self.Pp
                if nMatvec > self.nMatvecMax:
                    reason = 'matvec'
                else:
                    reason = 's small'
                finished = True

            else:

                # Project  A*Ps
                if self._matvec_found:
                    self.As = self.matvec(self.Ps)
                else:
                    self.H.matvec(self.Ps, self.As)
                nMatvec += 1
                self.rhs[:n] = self.As
                self.rhs[n:] = 0.0
                self.Proj.solve(self.rhs)

                # Compute omega and update x
                sAs = numpy.dot(self.Ps, self.As)
                AsPAs = numpy.dot(self.As, self.Proj.x[:n])
                omega = sAs / AsPAs
                self.x += alpha * self.Pp + omega * self.Ps

                # Check for termination
                if nMatvec > self.nMatvecMax:
                    finished = True
                    reason = 'matvec'

                else:

                    # Update residual
                    self.r = self.s - omega * self.As
                    rr0_next = numpy.dot(self.r, r0)
                    beta = alpha / omega * rr0_next / rr0
                    rr0 = rr0_next
                    self.p -= omega * self.Ap
                    self.p *= beta
                    self.p += self.r

                    # Check for termination in the Bi-CGSTAB process
                    if abs(rr0) < 1.0e-12 * rr00:
                        self.rhs[:n] = self.r
                        self.rhs[n:] = 0.0
                        self.Proj.solve(self.rhs)
                        rPr = numpy.dot(self.r, self.Proj.x[:n])
                        if sqrt(rPr) <= stopTol:
                            finished = True
                            reason = 'r small'

            # Display current iteration info
            if self.debug:
                self._write(self.fmt % (nMatvec, residNorm, rr0, alpha, omega))

        # End while

        # Obtain final solution x
        if self.x_feasible is not None:
            self.x += self.x_feasible

        if self.A is not None:
            # Find (weighted) least-squares Lagrange multipliers from
            #   [ G  B^T ] [w]   [c - Hx]
            #   [ B   0  ] [v] = [  0   ]
            if self._matvec_found:
                self.rhs[:n] = -self.matvec(self.x)
            else:
                self.H.matvec(-self.x, self.rhs[:n])
            self.rhs[:n] += self.c
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.v = self.Proj.x[n:].copy()

        self.t_solve = cputime() - self.t_solve
        self.converged = (nMatvec < self.nMatvecMax)
        self.nMatvec = nMatvec
        self.residNorm = residNorm
        self.status = reason

        return
Exemple #19
0
    def solve(self, **kwargs):
        """
        Solve current problem with trust-funnel framework.

        :keywords:
            :ny: Enable Nocedal-Yuan backtracking linesearch.

        :returns:
            This method sets the following members of the instance:
            :f: Final objective value
            :optimal: Flag indicating whether normal stopping conditions were
                      attained
            :pResid: Final primal residual
            :dResid: Final dual residual
            :niter: Total number of iterations
            :tsolve: Solve time.

        """

        ny = kwargs.get('ny', True)
        reg = kwargs.get('reg', 0.0)
        #ny = False

        tsolve = cputime()

        # Set some shortcuts.
        nlp = self.nlp
        n = nlp.n
        m = nlp.m
        x = self.x
        f = self.f
        c = self.cons(x)
        y = nlp.pi0.copy()
        self.it = 0

        # Initialize some constants.
        kappa_n = 1.0e+2   # Factor of pNorm in normal step TR radius.
        kappa_b = 0.99     # Fraction of TR to compute tangential step.
        kappa_delta = 0.1  # Progress factor to compute tangential step.

        # Trust-region parameters.
        eta_1 = 1.0e-5
        eta_2 = 0.95
        eta_3 = 0.5
        gamma_1 = 0.25
        gamma_3 = 2.5

        kappa_tx1 = 0.9 # Factor of theta_max in max acceptable infeasibility.
        kappa_tx2 = 0.5 # Convex combination factor of theta and thetaTrial.

        # Compute constraint violation.
        theta = 0.5 * np.dot(c,c)

        # Set initial funnel radius.
        kappa_ca = 1.0e+3    # Max initial funnel radius.
        kappa_cr = 2.0       # Infeasibility tolerance factor.
        theta_max = max(kappa_ca, kappa_cr * theta)

        # Evaluate first-order derivatives.
        g = nlp.grad(x)
        J = self.jac(x)
        Jop = PysparseLinearOperator(J)

        # Initial radius for f- and c-iterations.
        Delta_f = max(self.Delta_f, .1 * np.linalg.norm(g))
        Delta_c = max(self.Delta_c, .1 * sqrt(2*theta))

        # Reset initial multipliers to least-squares estimates by
        # approximately solving:
        #   [ I   J' ] [ w ]   [ -g ]
        #   [ J   0  ] [ y ] = [  0 ].
        # This is equivalent to solving
        #   minimize |g + J'y|.
        if m > 0:
            y, _, _ = self.lsq(Jop.T, -g, reg=reg)

        pNorm = cNorm = 0
        if m > 0:
            pNorm = np.linalg.norm(c);
            cNorm = np.linalg.norm(c, np.inf)
            grad_lag = g + Jop.T * y
        else:
            grad_lag = g.copy()
        dNorm = np.linalg.norm(grad_lag)/(1 + np.linalg.norm(y))

        # Display current info if requested.
        self.log.info(self.hdr)
        self.log.info(self.linefmt1 % (0, ' ', ' ', ' ', ' ', f, pNorm,
                                       dNorm, Delta_f, Delta_c, theta_max, 0))

        # Compute primal stopping tolerance.
        stop_p = max(self.atol, self.stop_p * pNorm)
        self.log.debug('pNorm = %8.2e, cNorm = %8.2e, dNorm = %8.e2' % (pNorm,cNorm,dNorm))

        optimal = (pNorm <= stop_p) and (dNorm <= self.stop_d)
        self.log.debug('optimal: %s' % repr(optimal))

        # Start of main iteration.
        while not optimal and (self.it < self.maxit):

            self.it += 1
            Delta = min(Delta_f, Delta_c)
            cgiter = 0

            # 1. Compute normal step as an (approximate) solution to
            #    minimize |c + J n|  subject to  |n| <= min(Delta_c, kN |c|).

            if self.it > 1 and \
                    pNorm <= stop_p and \
                    dNorm >= 1.0e+4 * self.stop_d:

                self.log.debug('Setting nStep=0 b/c need to work on optimality')
                nStep = np.zeros(n)
                nStepNorm = 0.0
                n_end = '0'
                m_xpn = 0       # Model value at x+n.

            else:

                nStep_max = min(Delta_c, kappa_n * pNorm)
                nStep, nStepNorm, lsq_status = self.lsq(Jop, -c,
                                                        radius=nStep_max,
                                                        reg=reg)

                if lsq_status == 'residual small':
                    n_end = 'r'
                elif lsq_status == 'trust-region boundary active':
                    n_end = 'b'
                else:
                    n_end = '?'

                # Evaluate the model of the obective after the normal step.
                _Hv = self.hprod(x, y, nStep) # H*nStep
                m_xpn = np.dot(g, nStep) + 0.5 * np.dot(nStep, _Hv)

            self.log.debug('Normal step norm = %8.2e' % nStepNorm)
            self.log.debug('Model value: %9.2e' % m_xpn)

            # 2. Compute tangential step if normal step is not too long.

            if nStepNorm <= kappa_b * Delta:

                # 2.1. Compute Lagrange multiplier estimates and dual residuals
                #      by minimizing |(g + H n) + J'y|

                if nStepNorm == 0.0:
                    gN = g   # Note: this is just a pointer ; g will not be modified below.
                else:
                    gN = g + _Hv

                y_new, y_norm, _ = self.lsq(Jop.T, -gN, reg=reg)
                r = gN + Jop.T * y_new

                # Here Nick does iterative refinement to improve r and y_new...

                # Compute dual optimality measure.
                residNorm = np.linalg.norm(r)
                norm_gN = np.linalg.norm(gN)
                pi = 0.0
                if residNorm > 0:
                    pi = abs(np.dot(gN, r))/residNorm

                # 2.2. If the dual residuals are large, compute a suitable
                #      tangential step as a solution to:
                #      minimize    g't + 1/2 t' H t
                #      subject to  Jt = 0, |n+t| <= Delta.

                if pi > self.forcing(3, theta):

                    self.log.debug('Computing tStep...')
                    Delta_within = Delta - nStepNorm

                    Hop = SimpleLinearOperator(n, n,
                                               lambda v: self.hprod(x,y_new,v),
                                               symmetric=True)
                    PPCG = ProjectedCG(gN, Hop,
                                       A=J.matrix if m > 0 else None,
                                       radius=Delta_within, dreg=reg)
                    PPCG.Solve()
                    tStep = PPCG.step
                    tStepNorm = PPCG.stepNorm
                    cgiter = PPCG.iter

                    self.log.debug('|t| = %8.2e' % tStepNorm)

                    if PPCG.status == 'residual small':
                        t_end = 'r'
                    elif PPCG.onBoundary and not PPCG.infDescent:
                        t_end = 'b'
                    elif PPCG.infDescent:
                        t_end = '-'
                    elif PPCG.status == 'max iter':
                        t_end = '>'
                    else:
                        t_end = '?'

                    # Compute total step and model decrease.
                    step = nStep + tStep
                    stepNorm = np.linalg.norm(step)
                    _Hv = self.hprod(x,y,step)    # y or y_new?
                    m_xps = np.dot(g, step) + 0.5 * np.dot(step, _Hv)

                else:

                    self.log.debug('Setting tStep=0 b/c pi is sufficiently small')
                    tStepNorm = 0
                    t_end = '0'
                    step = nStep
                    stepNorm = nStepNorm
                    m_xps = m_xpn

                y = y_new

            else:

                # No need to compute a tangential step.
                self.log.debug('Setting tStep=0 b/c the normal step is too large')
                t_end = '0'
                y = np.zeros(m)
                tStepNorm = 0.0
                step = nStep
                stepNorm = nStepNorm
                m_xps = m_xpn

            self.log.debug('Model decrease = %9.2e' % m_xps)

            # Compute trial point and evaluate local data.
            xTrial = x + step
            fTrial = nlp.obj(xTrial)
            cTrial = self.cons(xTrial)
            thetaTrial = 0.5 * np.dot(cTrial, cTrial)
            delta_f = -m_xps           # Overall improvement in the model.
            delta_ft = m_xpn - m_xps   # Improvement due to tangential step.
            Jspc = c + Jop * step

            # Compute improvement in linearized feasibility.
            delta_feas = theta - 0.5 * np.dot(Jspc, Jspc)

            # Decide whether to consider the current iteration
            # an f- or a c-iteration.

            if tStepNorm > 0 and (delta_f >= self.forcing(2, theta)) and \
                    delta_f >= kappa_delta * delta_ft and \
                    thetaTrial <= theta_max:

                # Step 3. Consider that this is an f-iteration.
                it_type = 'f'

                # Decide whether trial point is accepted.
                ratio = (f - fTrial)/delta_f

                self.log.debug('f-iter ratio = %9.2e' % ratio)

                if ratio >= eta_1:    # Successful step.
                    suc = 's'
                    x = xTrial
                    f = fTrial
                    c = cTrial
                    self.step = step.copy()
                    theta = thetaTrial

                    # Decide whether to update f-trust-region radius.
                    if ratio >= eta_2:
                        suc = 'v'
                        Delta_f = min(max(Delta_f, gamma_3 * stepNorm),
                                      1.0e+10)

                    # Decide whether to update c-trust-region radius.
                    if thetaTrial < eta_3 * theta_max:
                        ns = nStepNorm if nStepNorm > 0 else Delta_c
                        Delta_c = min(max(Delta_c, gamma_3 * ns),
                                      1.0e+10)

                    self.log.debug('New Delta_f = %8.2e' % Delta_f)
                    self.log.debug('New Delta_c = %8.2e' % Delta_c)

                else:                 # Unsuccessful step (ratio < eta_1).

                    attempt_SOC = True
                    suc = 'u'

                    if attempt_SOC:

                        self.log.debug('  Attempting second-order correction')

                        # Attempt a second-order correction by solving
                        # minimize |cTrial + J n|  subject to  |n| <= Delta_c.
                        socStep, socStepNorm, socStatus = self.lsq(Jop,
                                                                   -cTrial,
                                                                   radius=Delta_c,
                                                                   reg=reg)

                        if socStatus != 'trust-region boundary active':

                            # Consider SOC step as candidate step.
                            xSoc = xTrial + socStep
                            fSoc = nlp.obj(xSoc) ; cSoc = self.cons(xSoc)
                            thetaSoc = 0.5 * np.dot(cSoc, cSoc)
                            ratio = (f - fSoc)/delta_f

                            # Decide whether to accept SOC step.
                            if ratio >= eta_1 and thetaSoc <= theta_max:
                                suc = '2'
                                x = xSoc
                                f = fSoc
                                c = cSoc
                                theta = thetaSoc
                                self.step = step + socStep
                            else:

                                # Backtracking linesearch a la Nocedal & Yuan.
                                # Abandon SOC step. Backtrack from x+step.
                                if ny:
                                    (x, f, alpha) = self.nyf(x, f, fTrial,
                                                             g, step)
                                    #g = nlp.grad(x)
                                    c = self.cons(x)
                                    theta = 0.5 * np.dot(c,c)
                                    self.step = step + alpha * socStep
                                    Delta_f = min(alpha, .8) * stepNorm
                                    suc = 'y'

                                else:
                                    Delta_f = gamma_1 * Delta_f

                        else:  # SOC step lies on boundary of trust region.
                            Delta_f = gamma_1 * Delta_f

                    else:  # SOC step not attempted.

                        # Backtracking linesearch a la Nocedal & Yuan.
                        if ny:
                            (x, f, alpha) = self.nyf(x, f, fTrial, g, step)
                            #g = nlp.grad(x)
                            c = self.cons(x)
                            theta = 0.5 * np.dot(c,c)
                            self.step = alpha * step
                            Delta_f = min(alpha, .8) * stepNorm
                            suc = 'y'
                        else:
                            Delta_f = gamma_1 * Delta_f


            else:

                # Step 4. Consider that this is a c-iteration.
                it_type = 'c'

                # Display information.
                self.log.debug('c-iteration because ')
                if tStepNorm == 0.0:
                    self.log.debug('|t|=0')
                if delta_f < self.forcing(2, theta):
                    self.log.debug('delta_f=%8.2e < forcing=%8.2e'\
                                    % (delta_f, self.forcing(2, theta)))
                if delta_f < kappa_delta * delta_ft:
                    self.log.debug('delta_f=%8.2e < frac * delta_ft=%8.2e' \
                                    % (delta_f, delta_ft))
                if thetaTrial > theta_max:
                    self.log.debug('thetaTrial=%8.2e > theta_max=%8.2e' \
                                    % (thetaTrial, theta_max))

                # Step 4.1. Check trial point for acceptability.
                if delta_feas < 0:
                    self.log.debug(' !!! Warning: delta_feas is negative !!!')

                ratio = (theta - thetaTrial + 1.0e-16)/(delta_feas + 1.0e-16)
                self.log.debug('c-iter ratio = %9.2e' % ratio)

                if ratio >= eta_1:         # Successful step.
                    x = xTrial
                    f = fTrial
                    c = cTrial
                    self.step = step.copy()
                    suc = 's'

                    # Step 4.2. Update Delta_c.
                    if ratio >= eta_2:     # Very successful step.
                        ns = nStepNorm if nStepNorm > 0 else Delta_c
                        Delta_c = min(max(Delta_c, gamma_3 * ns),
                                      1.0e+10)
                        suc = 'v'

                    # Step 4.3. Update maximum acceptable infeasibility.
                    theta_max = max(kappa_tx1 * theta_max,
                                    kappa_tx2*theta + (1-kappa_tx2)*thetaTrial)
                    theta = thetaTrial

                else:                      # Unsuccessful step.

                    # Backtracking linesearch a la Nocedal & Yuan.
                    ns = nStepNorm if nStepNorm > 0 else Delta_c
                    if ny:
                        (x, c, theta, alpha) = self.nyc(x, theta, thetaTrial,
                                                        c, Jop.T*c, step)
                        f = nlp.obj(x)
                        #g = nlp.grad(x)
                        self.step = alpha * step
                        Delta_c = min(alpha, .8) * ns
                        suc = 'y'
                    else:
                        Delta_c = gamma_1 * ns #Delta_c
                        suc = 'u'

                self.log.debug('New Delta_c = %8.2e' % Delta_c)
                self.log.debug('New theta_max = %8.2e' % theta_max)

            # Step 5. Book keeping.
            if ratio >= eta_1 or ny:
                g = nlp.grad(x)
                J = self.jac(x)
                Jop = PysparseLinearOperator(J)
                self.post_iteration()

                pNorm = cNorm = 0
                if m > 0:
                    pNorm = np.linalg.norm(c)
                    cNorm = np.linalg.norm(c, np.inf)
                    grad_lag = g + Jop.T * y
                else:
                    grad_lag = g.copy()
                dNorm = np.linalg.norm(grad_lag)/(1 + np.linalg.norm(y))

            if self.it % 20 == 0:
                self.log.info(self.hdr)
            self.log.info(self.linefmt % (self.it, it_type, suc, n_end,
                                          t_end, f, pNorm, dNorm, Delta_f,
                                          Delta_c, theta_max, cgiter))

            optimal = (pNorm <= stop_p) and (dNorm <= self.stop_d)
        # End while.

        self.tsolve = cputime() - tsolve

        if optimal:
            self.status = 0  # Successful solve.
            self.log.info('Found an optimal solution! Yeah!')
        else:
            self.status = 1 # Refine this in the future.

        self.x = x
        self.f = f
        self.optimal = optimal
        self.pResid = pNorm
        self.dResid = dNorm
        self.niter = self.it

        return
Exemple #20
0
nA = A.shape[0]
nC = C.shape[0]
K = spmatrix.ll_mat_sym(nA + nC, A.nnz + C.nnz + min(nA,nC))
K[:nA,:nA] = A
K[nA:,nA:] = C
K[nA:,nA:].scale(-1.0)
idx = np.arange(min(nA,nC), dtype=np.int)
K.put(1, nA+idx, idx)

# Create right-hand side rhs=K*e
e = np.ones(nA+nC)
rhs = np.empty(nA+nC)
K.matvec(e,rhs)

# Factorize and solve Kx = rhs, knowing K is sqd
t = cputime()
P = LBLContext(K, sqd=True)
t = cputime() - t
sys.stderr.write('Factorization time with sqd=True : %5.2fs   ' % t )
P.solve(rhs, get_resid=False)
sys.stderr.write('Error: %7.1e\n' % np.linalg.norm(P.x - e, ord=np.Inf))

# Do it all over again, pretending we don't know K is sqd
t = cputime()
P = LBLContext(K)
t = cputime() - t
sys.stderr.write('Factorization time with sqd=False: %5.2fs   ' % t )
P.solve(rhs, get_resid=False)
sys.stderr.write('Error: %7.1e\n' % np.linalg.norm(P.x - e, ord=np.Inf))

try:
Exemple #21
0
opts_solve = {}
if options.maxiter is not None:
    opts_solve['itermax'] = options.maxiter
if options.tol is not None:
    opts_solve['tolerance'] = options.tol

# Set printing standards for arrays.
numpy.set_printoptions(precision=3, linewidth=80, threshold=10, edgeitems=3)

if not options.verbose:
    sys.stderr.write(hdr + '\n' + '-'*len(hdr) + '\n')

for probname in args:

    t_setup = cputime()
    qp = SlackFramework(probname)
    t_setup = cputime() - t_setup

    # isqp() should be implemented in the near future.
    #if not qp.isqp():
    #    sys.stderr.write('Problem %s is not a linear program\n' % probname)
    #    qp.close()
    #    continue

    # Pass problem to RegQP.
    regqp = RegQPInteriorPointSolver(qp,
                                     scale=not options.no_scale,
                                     verbose=options.verbose,
                                     **opts_init)
    
Exemple #22
0
Fichier : lp.py Projet : mpf/nlpy
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:

          :itermax:             The maximum allowed number of iterations
                                (default 10n)
          :tolerance:           Stopping tolerance (default 1.0e-6)
          :PredictorCorrector:  Use the predictor-corrector method
                                (default `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        :return:

            :x:            final iterate
            :y:            final value of the Lagrange multipliers associated
                           to `A1 x + A2 s = b`
            :z:            final value of the Lagrange multipliers associated
                           to `s >= 0`
            :obj_value:    final cost
            :iter:         total number of iterations
            :kktResid:     final relative residual
            :solve_time:   time to solve the LP
            :status:       string describing the exit status
            :short_status: short version of status, used for printing.
        """
        lp = self.lp
        itermax = kwargs.get('itermax', max(100,10*lp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape ; on = lp.original_n
        A = self.A ; b = self.b ; c = self.c ; H = self.H
        regpr = self.regpr ; regdu = self.regdu
        regpr_min = self.regpr_min ; regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        # set_initial_guess() initializes self.LBL which is reused below.
        (x,y,z) = self.set_initial_guess(self.lp, **kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:] ; ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        col_scale = np.empty(n)

        # Allocate room for right-hand side of linear systems.
        rhs = np.zeros(n+m)
        finished = False
        iter = 0

        # Acceptance thresholds for primal and dual reg parameters.
        #t1 = t2 = 0.99

        solve_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if self.verbose and iter % 20 == 0:
                sys.stdout.write('\n' + self.header + '\n')
                sys.stdout.write('-' * len(self.header) + '\n')

            # Compute residuals.
            pFeas = A*x - b
            comp = s*z ; sz = sum(comp)                     # comp   = S z
            dFeas = y*A ; dFeas[:on] -= self.c              # dFeas1 = A1'y - c
            dFeas[on:] += z                                 # dFeas2 = A2'y + z
            mu = sz/ns

            # Compute residual norms and scaled residual norms.
            pResid = norm2(pFeas) ; spResid = pResid/(1+self.normb+self.normA)
            cResid = norm2(comp)  ; scResid = cResid/self.normbc
            dResid = norm2(dFeas) ; sdResid = dResid/(1+self.normc+self.normA)

            # Compute relative duality gap.
            cx = np.dot(c,x[:on])
            by = np.dot(b,y)
            rgap  = cx - by
            rgap  = abs(rgap) / (1 + abs(cx))
            rgap2 = mu /(1 + abs(cx))

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)
            #kktResid = max(pResid, cResid, dResid)

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status= 'iter'
                finished = True
                continue

            # Adjust regularization parameters
            #mu = sum(comp)/ns
            #if mu < 1:
            #    regpr = sqrt(mu)
            #    regdu = sqrt(mu)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            if iter == 0:
                regpr = self.regpr ; regdu = self.regdu
                if regpr > 0:
                    q = dFeas/regpr ; qNorm = norm2(q) ; rho_q = regpr * qNorm
                else:
                    q = dFeas ; qNorm = norm2(q) ; rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas/regdu ; rNorm = norm2(r) ; del_r = regdu * rNorm
                else:
                    r = -pFeas ; rNorm = norm2(r) ; del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu
            else:
                # Adjust regularization parameters.
                #regpr = max(min(regpr/10, regpr**(1.1)), regpr_min)
                #regdu = max(min(regdu/10, regdu**(1.1)), regdu_min)
                # 1) rho+ |dx| <= const * s'z
                # 2) del+ |dy| <= const * s'z
                if regdu > 0:
                    regdu = min(regdu/10, sz/normdy/10, (sz/normdy)**(1.1))
                    regdu = max(regdu, regdu_min)
                if regpr > 0:
                    regpr = min(regpr/10, sz/normdx/10, (sz/normdx)**(1.1))
                    regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    #if mu < 1.0e-8 * mu0 and rho_q > 1.0e+3 * kktResid * self.normbc: #* mu * self.normbc:
                    if mu < 1.0e-8 * mu0 and rho_q > 1.0e+2 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter-1:
                            if pr_infeas_count > 6:
                                status = 'Problem seems to be (locally) dual infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter

                    #if mu < 1.0e-8 * mu0 and del_r > 1.0e+3 * kktResid * self.normbc: # * mu * self.normbc:
                    if mu < 1.0e-8 * mu0 and del_r > 1.0e+2 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter-1:
                            if du_infeas_count > 6:
                                status='Problem seems to be (locally) primal infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter

            # Display objective and residual data.
            if self.verbose:
                sys.stdout.write(self.format1 % (iter, cx, pResid, dResid,
                                                 cResid, rgap, qNorm, rNorm))

            # Record some quantities for display
            mins = np.min(s)
            minz = np.min(z)
            maxs = np.max(s)

            # Repeatedly assemble system and compute step until primal and
            # dual regularization parameters have appropriate values.

            # Reset primal and dual regularization parameters to best guess
            #if iter > 0:
            #    regpr = max(regpr_min, 0.5*sigma*dResid/normds)
            #    regdu = max(regdu_min, 0.5*sigma*pResid/normdy)

            step_acceptable = False

            while not step_acceptable:

                # Solve the linear system
                #
                # [-pI          0          A1'] [∆x]   [c - A1' y             ]
                # [ 0   -(S^{-1} Z + pI)   A2'] [∆s] = [  - A2' y - µ S^{-1} e]
                # [ A1          A2         dI ] [∆y]   [b - A1 x - A2 s       ]
                #
                # where s are the slack variables, p is the primal
                # regularization parameter, d is the dual regularization
                # parameter, and  A = [ A1  A2 ]  where the columns of A1
                # correspond to the original problem variables and those of A2
                # correspond to slack variables.
                #
                # We recover ∆z = -z - S^{-1} (Z ∆s + µ e).
                # Compute augmented matrix and factorize it.
                factorized = False
                nb_bump = 0
                while not factorized and nb_bump < 5:

                    if self.stabilize:
                        col_scale[:on] = sqrt(regpr)
                        col_scale[on:] = np.sqrt(z/s + regpr)
                        H.put(-sqrt(regdu), range(n))
                        H.put( sqrt(regdu), range(n,n+m))
                        AA = self.A.copy()
                        AA.col_scale(1/col_scale)
                        H[n:,:n] = AA
                    else:
                        if regpr > 0: H.put(-regpr,       range(on))
                        H.put(-z/s - regpr, range(on,n))
                        if regdu > 0: H.put(regdu,        range(n,n+m))

                    #if iter == 5:
                    #    # Export current matrix to file for futher inspection.
                    #    import os
                    #    name = os.path.basename(self.lp.name)
                    #    fname = '.'.join(name.split('.')[:-1]) + '.mtx'
                    #    H.exportMmf(fname)

                    self.LBL.factorize(H)
                    factorized = True

                    # If the augmented matrix does not have full rank, bump up
                    # regularization parameters.
                    if not self.LBL.isFullRank:
                        if self.verbose:
                            sys.stderr.write('Primal-Dual Matrix ')
                            sys.stderr.write('Rank Deficient')
                        if regdu == 0.0:
                            sys.stderr.write('... No regularization in effect')
                            sys.stderr.write('... bailing out\n')
                            factorized = False
                            nb_bump = 5
                            continue
                        else:
                            sys.stderr.write('... bumping up reg parameters\n')
                        regpr *= 10 ; regdu *= 10
                        nb_bump += 1
                        factorized = False

                # Abandon if regularization is unsuccessful.
                if not self.LBL.isFullRank and nb_bump >= 5:
                    status = 'Unable to regularize sufficiently.'
                    short_status = 'degn'
                    finished = True
                    continue  # Does this get us out of the outer while?

                # Compute duality measure.
                mu = sz/ns

                if PredictorCorrector:
                    # Use Mehrotra predictor-corrector method.
                    # Compute affine-scaling step, i.e. with centering = 0.
                    rhs[:n]    = -dFeas
                    rhs[on:n] += z
                    rhs[n:]    = -pFeas

                    # if 'stabilize' is on, must scale right-hand side.
                    if self.stabilize:
                        rhs[:n] /= col_scale
                        rhs[n:] /= sqrt(regdu)

                    (step, nres, neig) = self.solveSystem(rhs)

                    # Unscale step if 'stabilize' is on.
                    if self.stabilize:
                        step[:n] *= sqrt(regdu) / col_scale

                    # Recover dx and dz.
                    dx = step[:n]
                    ds = dx[on:]
                    dz = -z * (1 + ds/s)

                    # Compute largest allowed primal and dual stepsizes.
                    (alpha_p, ip) = self.maxStepLength(s, ds)
                    (alpha_d, ip) = self.maxStepLength(z, dz)

                    # Estimate duality gap after affine-scaling step.
                    muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns
                    sigma = (muAff/mu)**3

                    # Incorporate predictor information for corrector step.
                    comp += ds*dz
                else:
                    # Use long-step method: Compute centering parameter.
                    sigma = min(0.1, 100*mu)

                # Assemble right-hand side with centering information.
                comp -= sigma * mu

                if PredictorCorrector:
                    # Only update rhs[on:n]; the rest of rhs did not change.
                    if self.stabilize:
                        rhs[on:n] += (comp/s - z)/col_scale[on:n]
                    else:
                        rhs[on:n] += comp/s - z
                else:
                    rhs[:n]    = -dFeas
                    rhs[on:n] += comp/s
                    rhs[n:]    = -pFeas

                    # If 'stabilize' is on, must scale right-hand side.
                    # In the predictor-corrector method, this has already been
                    # done.
                    if self.stabilize:
                        rhs[:n] /= col_scale
                        rhs[n:] /= sqrt(regdu)

                # Solve augmented system.
                (step, nres, neig) = self.solveSystem(rhs)

                # Unscale step if 'stabilize' is on.
                if self.stabilize:
                    step[:n] *= sqrt(regdu) / col_scale

                # Recover step.
                dx = step[:n]
                ds = dx[on:]
                dy = step[n:]

                normds = norm2(ds) ; normdy = norm2(dy) ; normdx = norm2(dx)
                step_acceptable = True  # Must get rid of this

            # End while not step_acceptable

            # Recover step in z.
            dz = -(comp + z*ds)/s

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0-mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult*mu_tmp - s[ip]*zip)/(alpha_p*ds[ip]*zip)
                    alpha_p *= max(1-mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult*mu_tmp - z[id]*sid)/(alpha_d*dz[id]*sid)
                    alpha_d *= max(1-mult, gamma_d)

                if ip==id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            if self.verbose:
                sys.stdout.write(self.format2 % (mu, alpha_p, alpha_d,
                                                 nres, regpr, regdu, rho_q,
                                                 del_r, mins, minz, maxs))

            # Update primal variables and slacks.
            x += alpha_p * dx

            # Update dual variables.
            y += alpha_d * dy
            z += alpha_d * dz

            # Update perturbation vectors.
            q *= (1-alpha_p) ; q += alpha_p * dx
            r *= (1-alpha_d) ; r += alpha_d * dy
            qNorm = norm2(q) ; rNorm = norm2(r)
            rho_q = regpr * qNorm/(1+self.normc) ; rho_q_min = min(rho_q_min, rho_q)
            del_r = regdu * rNorm/(1+self.normb) ; del_r_min = min(del_r_min, del_r)

            iter += 1

        solve_time = cputime() - solve_time

        if self.verbose:
            sys.stdout.write('\n')
            sys.stdout.write('-' * len(self.header) + '\n')

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid ; self.cResid = cResid ; self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = np.dot(self.c, x[:on]) + self.c0
        return
Exemple #23
0
    def __init__(self, qp, **kwargs):
        """
        Solve a convex quadratic program of the form::

           minimize    c' x + 1/2 x' Q x
           subject to  A1 x + A2 s = b,                                 (QP)
                       s >= 0,

        where Q is a symmetric positive semi-definite matrix, the variables
        x are the original problem variables and s are slack variables. Any
        quadratic program may be converted to the above form by instantiation
        of the `SlackFramework` class. The conversion to the slack formulation
        is mandatory in this implementation.

        The method is a variant of Mehrotra's predictor-corrector method where
        steps are computed by solving the primal-dual system in augmented form.

        Primal and dual regularization parameters may be specified by the user
        via the opional keyword arguments `regpr` and `regdu`. Both should be
        positive real numbers and should not be "too large". By default they are
        set to 1.0 and updated at each iteration.

        If `scale` is set to `True`, (QP) is scaled automatically prior to
        solution so as to equilibrate the rows and columns of the constraint
        matrix [A1 A2].

        Advantages of this method are that it is not sensitive to dense columns
        in A, no special treatment of the unbounded variables x is required, and
        a sparse symmetric quasi-definite system of equations is solved at each
        iteration. The latter, although indefinite, possesses a Cholesky-like
        factorization. Those properties makes the method typically more robust
        that a standard predictor-corrector implementation and the linear system
        solves are often much faster than in a traditional interior-point method
        in augmented form.

        :keywords:
            :scale: Perform row and column equilibration of the constraint
                    matrix [A1 A2] prior to solution (default: `True`).

            :regpr: Initial value of primal regularization parameter
                    (default: `1.0`).

            :regdu: Initial value of dual regularization parameter
                    (default: `1.0`).

            :bump_max: Max number of times regularization parameters are
                       increased when a factorization fails (default 5).

            :logger_name: Name of a logger to control output.

            :verbose: Turn on verbose mode (default `False`).
        """

        if not isinstance(qp, SlackFramework):
            msg = 'Input problem must be an instance of SlackFramework'
            raise ValueError, msg

        # Grab logger if one was configured.
        logger_name = kwargs.get('logger_name', 'cqp.solver')
        self.log = logging.getLogger(logger_name)

        self.verbose = kwargs.get('verbose', True)
        scale = kwargs.get('scale', True)

        self.qp = qp
        self.A = qp.A()               # Constraint matrix
        if not isinstance(self.A, PysparseMatrix):
            self.A = PysparseMatrix(matrix=self.A)

        m, n = self.A.shape ; on = qp.original_n
        # Record number of slack variables in QP
        self.nSlacks  = qp.n - on

        # Collect basic info about the problem.
        zero = np.zeros(n)

        self.b  = -qp.cons(zero)                  # Right-hand side
        self.c0 =  qp.obj(zero)                   # Constant term in objective
        self.c  =  qp.grad(zero[:on])             # Cost vector
        self.Q  =  PysparseMatrix(matrix=qp.hess(zero[:on],
                                                 np.zeros(qp.original_m)))

        # Apply in-place problem scaling if requested.
        self.prob_scaled = False
        if scale:
            self.t_scale = cputime()
            self.scale()
            self.t_scale = cputime() - self.t_scale
        else:
            # self.scale() sets self.normQ to the Frobenius norm of Q
            # and self.normA to the Frobenius norm of A as a by-product.
            # If we're not scaling, set normQ and normA manually.
            self.normQ = self.Q.matrix.norm('fro')
            self.normA = self.A.matrix.norm('fro')

        self.normb  = norm_infty(self.b)
        self.normc  = norm_infty(self.c)
        self.normbc = 1 + max(self.normb, self.normc)

        # Initialize augmented matrix.
        self.H = self.initialize_kkt_matrix()

        # It will be more efficient to keep the diagonal of Q around.
        self.diagQ = self.Q.take(range(qp.original_n))

        # We perform the analyze phase on the augmented system only once.
        # self.LBL will be initialized in solve().
        self.LBL = None

        # Set regularization parameters.
        self.regpr = kwargs.get('regpr', 1.0) ; self.regpr_min = 1.0e-8
        self.regdu = kwargs.get('regdu', 1.0) ; self.regdu_min = 1.0e-8

        # Max number of times regularization parameters are increased.
        self.bump_max = kwargs.get('bump_max', 5)

        # Check input parameters.
        if self.regpr < 0.0: self.regpr = 0.0
        if self.regdu < 0.0: self.regdu = 0.0

        # Initialize format strings for display
        fmt_hdr = '%-4s  %9s' + '  %-8s'*6 + '  %-7s  %-4s  %-4s' + '  %-8s'*8
        self.header = fmt_hdr % ('Iter', 'Cost', 'pResid', 'dResid', 'cResid',
                                 'rGap', 'qNorm', 'rNorm', 'Mu', 'AlPr', 'AlDu',
                                 'LS Resid', 'RegPr', 'RegDu', 'Rho q', 'Del r',
                                 'Min(s)', 'Min(z)', 'Max(s)')
        self.format1  = '%-4d  %9.2e'
        self.format1 += '  %-8.2e' * 6
        self.format2  = '  %-7.1e  %-4.2f  %-4.2f'
        self.format2 += '  %-8.2e' * 8

        self.cond_history = []
        self.berr_history = []
        self.derr_history = []
        self.nrms_history = []
        self.lres_history = []

        if self.verbose: self.display_stats()

        return
Exemple #24
0
    def __init__(self, qp, **kwargs):
        """
        Solve a convex quadratic program of the form::

           minimize    c' x + 1/2 x' Q x
           subject to  A1 x + A2 s = b,                                 (QP)
                       s >= 0,

        where Q is a symmetric positive semi-definite matrix, the variables
        x are the original problem variables and s are slack variables. Any
        quadratic program may be converted to the above form by instantiation
        of the `SlackFramework` class. The conversion to the slack formulation
        is mandatory in this implementation.

        The method is a variant of Mehrotra's predictor-corrector method where
        steps are computed by solving the primal-dual system in augmented form.

        Primal and dual regularization parameters may be specified by the user
        via the opional keyword arguments `regpr` and `regdu`. Both should be
        positive real numbers and should not be "too large". By default they are
        set to 1.0 and updated at each iteration.

        If `scale` is set to `True`, (QP) is scaled automatically prior to
        solution so as to equilibrate the rows and columns of the constraint
        matrix [A1 A2].

        Advantages of this method are that it is not sensitive to dense columns
        in A, no special treatment of the unbounded variables x is required, and
        a sparse symmetric quasi-definite system of equations is solved at each
        iteration. The latter, although indefinite, possesses a Cholesky-like
        factorization. Those properties makes the method typically more robust
        that a standard predictor-corrector implementation and the linear system
        solves are often much faster than in a traditional interior-point method
        in augmented form.

        :keywords:
            :scale: Perform row and column equilibration of the constraint
                    matrix [A1 A2] prior to solution (default: `True`).

            :regpr: Initial value of primal regularization parameter
                    (default: `1.0`).

            :regdu: Initial value of dual regularization parameter
                    (default: `1.0`).

            :verbose: Turn on verbose mode (default `False`).
        """

        if not isinstance(qp, SlackFramework):
            msg = 'Input problem must be an instance of SlackFramework'
            raise ValueError, msg

        self.verbose = kwargs.get('verbose', True)
        scale = kwargs.get('scale', True)

        self.qp = qp
        self.A = qp.A()  # Constraint matrix
        if not isinstance(self.A, PysparseMatrix):
            self.A = PysparseMatrix(matrix=self.A)

        m, n = self.A.shape
        on = qp.original_n
        # Record number of slack variables in QP
        self.nSlacks = qp.n - on

        # Collect basic info about the problem.
        zero = np.zeros(n)

        self.b = -qp.cons(zero)  # Right-hand side
        self.c0 = qp.obj(zero)  # Constant term in objective
        self.c = qp.grad(zero[:on])  # Cost vector
        self.Q = PysparseMatrix(
            matrix=qp.hess(zero[:on], np.zeros(qp.original_m)))

        # Apply in-place problem scaling if requested.
        self.prob_scaled = False
        if scale:
            self.t_scale = cputime()
            self.scale()
            self.t_scale = cputime() - self.t_scale
        else:
            # self.scale() sets self.normQ to the Frobenius norm of Q
            # as a by-product. If we're not scaling, set normQ manually.
            self.normQ = self.Q.matrix.norm('fro')

        self.normb = norm_infty(self.b)
        self.normc = norm_infty(self.c)
        self.normbc = 1 + max(self.normb, self.normc)

        # Initialize augmented matrix
        self.H = PysparseMatrix(size=n + m,
                                sizeHint=n + m + self.A.nnz + self.Q.nnz,
                                symmetric=True)

        # The (1,1) block will always be Q (save for its diagonal).
        self.H[:on, :on] = -self.Q

        # The (2,1) block will always be A. We store it now once and for all.
        self.H[n:, :n] = self.A

        # It will be more efficient to keep the diagonal of Q around.
        self.diagQ = self.Q.take(range(qp.original_n))

        # We perform the analyze phase on the augmented system only once.
        # self.LBL will be initialized in solve().
        self.LBL = None

        # Set regularization parameters.
        self.regpr = kwargs.get('regpr', 1.0)
        self.regpr_min = 1.0e-8
        self.regdu = kwargs.get('regdu', 1.0)
        self.regdu_min = 1.0e-8

        # Check input parameters.
        if self.regpr < 0.0: self.regpr = 0.0
        if self.regdu < 0.0: self.regdu = 0.0

        # Initialize format strings for display
        fmt_hdr = '%-4s  %9s' + '  %-8s' * 6 + '  %-7s  %-4s  %-4s' + '  %-8s' * 8
        self.header = fmt_hdr % ('Iter', 'Cost', 'pResid', 'dResid', 'cResid',
                                 'rGap', 'qNorm', 'rNorm', 'Mu', 'AlPr',
                                 'AlDu', 'LS Resid', 'RegPr', 'RegDu', 'Rho q',
                                 'Del r', 'Min(s)', 'Min(z)', 'Max(s)')
        self.format1 = '%-4d  %9.2e'
        self.format1 += '  %-8.2e' * 6
        self.format2 = '  %-7.1e  %-4.2f  %-4.2f'
        self.format2 += '  %-8.2e' * 8 + '\n'

        if self.verbose: self.display_stats()

        return
Exemple #25
0
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:
          :itermax:  The maximum allowed number of iterations (default: 10n)

          :tolerance:  Stopping tolerance (default: 1.0e-6)

          :PredictorCorrector:  Use the predictor-corrector method
                                (default: `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        Upon exit, the following members of the class instance are set:

        * x..............final iterate
        * y..............final value of the Lagrange multipliers associated
                         to A1 x + A2 s = b
        * z..............final value of the Lagrange multipliers associated
                         to s>=0
        * obj_value......final cost
        * iter...........total number of iterations
        * kktResid.......final relative residual
        * solve_time.....time to solve the QP
        * status.........string describing the exit status.
        * short_status...short version of status, used for printing.

        """
        qp = self.qp
        itermax = kwargs.get('itermax', max(100, 10 * qp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape
        on = qp.original_n
        A = self.A
        b = self.b
        c = self.c
        Q = self.Q
        diagQ = self.diagQ
        H = self.H

        regpr = self.regpr
        regdu = self.regdu
        regpr_min = self.regpr_min
        regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        (x, y, z) = self.set_initial_guess(self.qp, **kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:]
        ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        col_scale = np.empty(n)

        # Allocate room for right-hand side of linear systems.
        rhs = np.zeros(n + m)
        finished = False
        iter = 0

        setup_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if self.verbose and iter % 20 == 0:
                sys.stdout.write('\n' + self.header + '\n')
                sys.stdout.write('-' * len(self.header) + '\n')

            # Compute residuals.
            pFeas = A * x - b
            comp = s * z
            sz = sum(comp)  # comp   = S z
            Qx = Q * x[:on]
            dFeas = y * A
            dFeas[:on] -= self.c + Qx  # dFeas1 = A1'y - c - Qx
            dFeas[on:] += z  # dFeas2 = A2'y + z

            # Compute duality measure.
            if ns > 0:
                mu = sz / ns
            else:
                mu = 0.0

            # Compute residual norms and scaled residual norms.
            #pResid = norm_infty(pFeas + regdu * r)/(1+self.normc+self.normQ)
            #dResid = norm_infty(dFeas - regpr * q)/(1+self.normb+self.normQ)
            pResid = norm2(pFeas)
            spResid = pResid / (1 + self.normc + self.normQ)
            dResid = norm2(dFeas)
            sdResid = dResid / (1 + self.normb + self.normQ)
            if ns > 0:
                cResid = norm_infty(comp) / (self.normbc + self.normQ)
            else:
                cResid = 0.0

            # Compute relative duality gap.
            cx = np.dot(c, x[:on])
            xQx = np.dot(x[:on], Qx)
            by = np.dot(b, y)
            rgap = cx + xQx - by
            #rgap += regdu * (rNorm**2 + np.dot(r,y))
            rgap = abs(rgap) / (1 + abs(cx) + self.normQ)
            rgap2 = mu / (1 + abs(cx) + self.normQ)

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)
            #kktResid = max(pResid, cResid, dResid)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            # Should probably get rid of q when regpr=0 and of r when regdu=0.
            if iter == 0:
                if regpr > 0:
                    q = dFeas / regpr
                    qNorm = dResid / regpr
                    rho_q = dResid
                else:
                    q = dFeas
                    qNorm = dResid
                    rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas / regdu
                    rNorm = pResid / regdu
                    del_r = pResid
                else:
                    r = -pFeas
                    rNorm = pResid
                    del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu
            else:
                regdu = min(regdu / 10, sz / normdy / 10, (sz / normdy)**(1.1))
                regdu = max(regdu, regdu_min)
                regpr = min(regpr / 10, sz / normdx / 10, (sz / normdx)**(1.1))
                regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    if mu < 1.0e-8 * mu0 and rho_q > 1.0e+2 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter - 1:
                            if pr_infeas_count > 6:
                                status = 'Problem seems to be (locally) dual infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter

                    if mu < 1.0e-8 * mu0 and del_r > 1.0e+2 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter - 1:
                            if du_infeas_count > 6:
                                status = 'Problem seems to be (locally) primal infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter

            # Display objective and residual data.
            if self.verbose:
                sys.stdout.write(self.format1 %
                                 (iter, cx + 0.5 * xQx, pResid, dResid, cResid,
                                  rgap, qNorm, rNorm))

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status = 'iter'
                finished = True
                continue

            # Record some quantities for display
            if ns > 0:
                mins = np.min(s)
                minz = np.min(z)
                maxs = np.max(s)
            else:
                mins = minz = maxs = 0

            # Solve the linear system
            #
            # [ -(Q+pI)      0             A1' ] [∆x] = [c + Q x - A1' y       ]
            # [  0      -(S^{-1} Z + pI)   A2' ] [∆s]   [  - A2' y - µ S^{-1} e]
            # [  A1          A2            dI  ] [∆y]   [ b - A1 x - A2 s      ]
            #
            # where s are the slack variables, p is the primal regularization
            # parameter, d is the dual regularization parameter, and
            # A = [ A1  A2 ]  where the columns of A1 correspond to the original
            # problem variables and those of A2 correspond to slack variables.
            #
            # We recover ∆z = -z - S^{-1} (Z ∆s + µ e).

            # Compute augmented matrix and factorize it.
            factorized = False
            nb_bump = 0
            while not factorized and nb_bump < 5:

                H.put(-diagQ - regpr, range(on))
                H.put(-z / s - regpr, range(on, n))
                H.put(regdu, range(n, n + m))
                self.LBL.factorize(H)
                factorized = True

                # If the augmented matrix does not have full rank, bump up the
                # regularization parameters.
                if not self.LBL.isFullRank:
                    if self.verbose:
                        sys.stderr.write('Primal-Dual Matrix Rank Deficient')
                        sys.stderr.write('... bumping up reg parameters\n')
                    regpr *= 100
                    regdu *= 100
                    nb_bump += 1
                    factorized = False

            # Abandon if regularization is unsuccessful.
            if not self.LBL.isFullRank and nb_bump == 5:
                status = 'Unable to regularize sufficiently.'
                short_status = 'degn'
                finished = True
                continue

            if PredictorCorrector:
                # Use Mehrotra predictor-corrector method.
                # Compute affine-scaling step, i.e. with centering = 0.
                rhs[:n] = -dFeas
                rhs[on:n] += z
                rhs[n:] = -pFeas

                (step, nres, neig) = self.solveSystem(rhs)

                # Recover dx and dz.
                dx = step[:n]
                ds = dx[on:]
                dz = -z * (1 + ds / s)

                # Compute largest allowed primal and dual stepsizes.
                (alpha_p, ip) = self.maxStepLength(s, ds)
                (alpha_d, ip) = self.maxStepLength(z, dz)

                # Estimate duality gap after affine-scaling step.
                muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz) / ns
                sigma = (muAff / mu)**3

                # Incorporate predictor information for corrector step.
                comp += ds * dz
            else:
                # Use long-step method: Compute centering parameter.
                sigma = min(0.1, 100 * mu)

            # Assemble right-hand side with centering information.
            comp -= sigma * mu

            if PredictorCorrector:
                # Only update rhs[on:n]; the rest of the vector did not change.
                rhs[on:n] += comp / s - z
            else:
                rhs[:n] = -dFeas
                rhs[on:n] += comp / s
                rhs[n:] = -pFeas

            # Solve augmented system.
            (step, nres, neig) = self.solveSystem(rhs)

            # Recover step.
            dx = step[:n]
            ds = dx[on:]
            dy = step[n:]
            dz = -(comp + z * ds) / s

            normds = norm2(ds)
            normdy = norm2(dy)
            normdx = norm2(dx)

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0 - mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz) / ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult * mu_tmp - s[ip] * zip) / (alpha_p *
                                                               ds[ip] * zip)
                    alpha_p *= max(1 - mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult * mu_tmp - z[id] * sid) / (alpha_d *
                                                               dz[id] * sid)
                    alpha_d *= max(1 - mult, gamma_d)

                if ip == id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            if self.verbose:
                sys.stdout.write(self.format2 %
                                 (mu, alpha_p, alpha_d, nres, regpr, regdu,
                                  rho_q, del_r, mins, minz, maxs))

            # Update iterates and perturbation vectors.
            x += alpha_p * dx  # This also updates slack variables.
            y += alpha_d * dy
            z += alpha_d * dz
            q *= (1 - alpha_p)
            q += alpha_p * dx
            r *= (1 - alpha_d)
            r += alpha_d * dy
            qNorm = norm2(q)
            rNorm = norm2(r)
            rho_q = regpr * qNorm / (1 + self.normc)
            rho_q_min = min(rho_q_min, rho_q)
            del_r = regdu * rNorm / (1 + self.normb)
            del_r_min = min(del_r_min, del_r)
            iter += 1

        solve_time = cputime() - setup_time

        if self.verbose:
            sys.stdout.write('\n')
            sys.stdout.write('-' * len(self.header) + '\n')

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid
        self.cResid = cResid
        self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = self.c0 + cx + 0.5 * xQx

        return
Exemple #26
0
def test_pycfs(ProblemList, plist=[0, 2, 5, 10], latex=False):

    if len(ProblemList) == 0:
        usage()
        sys.exit(1)
    
    if latex:
        # For LaTeX output
        fmt = '%-12s & %-5s & %-6s & %-2s & %-4s & %-8s & %-4s & %-8s & %-6s & %-6s\\\\\n'
        fmt1 = '%-12s & %-5d & %-6d & '
        fmt2 = '%-2d & %-4d & %-8.1e & %-4d & %-8.1e & %-6.2f & %-6.2f\\\\\n'
        hline = '\\hline\n'
        skip = '&&&'
    else:
        # For ASCII output
        fmt = '%-12s %-5s %-6s %-2s %-4s %-8s %-4s %-8s %-6s %-6s\n'
        fmt1 = '%-12s %-5d %-6d '
        fmt2 = '%-2d %-4d %-8.1e %-4d %-8.1e %-6.2f %-6.2f\n'
        skip = ' ' * 26


    header = fmt % ('Name','Size','nnz','p','info','shift','iter','relResid','fact','solve')
    lhead = len(header)
    if not latex: hline = '-' * lhead + '\n'
    sys.stderr.write(hline + header + hline)

    time_list = {}        # Record timings
    iter_list = {}        # Record number of iterations

    for problem in ProblemList:

        A = spmatrix.ll_mat_from_mtx(problem)
        (m, n) = A.shape
        if m != n: break

        prob = os.path.basename(problem)
        if prob[-4:] == '.mtx': prob = prob[:-4]

        # Right-hand side is Ae, as in Icfs.
        e = numpy.ones(n, 'd');
        b = numpy.ones(n, 'd');
        A.matvec(e, b)

        sys.stdout.write(fmt1 % (prob, n, A.nnz))
        advance = False

        # Call icfs and pcg
        tlist = []
        ilist = []
        for pval in plist:
            t0 = cputime()
            P = pycfs.PycfsContext(A, mem=pval)
            t_fact = cputime() - t0
            P.solve(b)
            t_solve = P.tsolve
            tlist.append(t_fact + t_solve)
            ilist.append(P.iter)

            if advance: sys.stdout.write(skip)
            sys.stdout.write(fmt2 % (pval, P.info, P.shift, P.iter, P.relres, t_fact, t_solve))
            advance = True
            
        time_list[prob] = tlist
        iter_list[prob] = ilist
        sys.stderr.write(hline)

    return (time_list,iter_list)
Exemple #27
0
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:
          :itermax:  The maximum allowed number of iterations (default: 10n)

          :tolerance:  Stopping tolerance (default: 1.0e-6)

          :PredictorCorrector:  Use the predictor-corrector method
                                (default: `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        Upon exit, the following members of the class instance are set:

        * x..............final iterate
        * y..............final value of the Lagrange multipliers associated
                         to A1 x + A2 s = b
        * z..............final value of the Lagrange multipliers associated
                         to s>=0
        * obj_value......final cost
        * iter...........total number of iterations
        * kktResid.......final relative residual
        * solve_time.....time to solve the QP
        * status.........string describing the exit status.
        * short_status...short version of status, used for printing.

        """
        qp = self.qp
        itermax = kwargs.get('itermax', max(100,10*qp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape ; on = qp.original_n
        A = self.A ; b = self.b ; c = self.c ; Q = self.Q ; diagQ = self.diagQ
        H = self.H

        regpr = self.regpr ; regdu = self.regdu
        regpr_min = self.regpr_min ; regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        (x,y,z) = self.set_initial_guess(self.qp, **kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:] ; ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        col_scale = np.empty(n)

        # Allocate room for right-hand side of linear systems.
        rhs = np.zeros(n+m)
        finished = False
        iter = 0

        setup_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if self.verbose and iter % 20 == 0:
                sys.stdout.write('\n' + self.header + '\n')
                sys.stdout.write('-' * len(self.header) + '\n')

            # Compute residuals.
            pFeas = A*x - b
            comp = s*z ; sz = sum(comp)                 # comp   = S z
            Qx = Q*x[:on]
            dFeas = y*A ; dFeas[:on] -= self.c + Qx     # dFeas1 = A1'y - c - Qx
            dFeas[on:] += z                             # dFeas2 = A2'y + z

            # Compute duality measure.
            if ns > 0:
                mu = sz/ns
            else:
                mu = 0.0

            # Compute residual norms and scaled residual norms.
            #pResid = norm_infty(pFeas + regdu * r)/(1+self.normc+self.normQ)
            #dResid = norm_infty(dFeas - regpr * q)/(1+self.normb+self.normQ)
            pResid = norm2(pFeas) ; spResid = pResid/(1+self.normc+self.normQ)
            dResid = norm2(dFeas) ; sdResid = dResid/(1+self.normb+self.normQ)
            if ns > 0:
                cResid = norm_infty(comp)/(self.normbc+self.normQ)
            else:
                cResid = 0.0

            # Compute relative duality gap.
            cx = np.dot(c,x[:on])
            xQx = np.dot(x[:on],Qx)
            by = np.dot(b,y)
            rgap  = cx + xQx - by
            #rgap += regdu * (rNorm**2 + np.dot(r,y))
            rgap  = abs(rgap) / (1 + abs(cx) + self.normQ)
            rgap2 = mu / (1 + abs(cx) + self.normQ)

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)
            #kktResid = max(pResid, cResid, dResid)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            # Should probably get rid of q when regpr=0 and of r when regdu=0.
            if iter == 0:
                if regpr > 0:
                    q =  dFeas/regpr ; qNorm = dResid/regpr ; rho_q = dResid
                else:
                    q =  dFeas ; qNorm = dResid ; rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas/regdu ; rNorm = pResid/regdu ; del_r = pResid
                else:
                    r = -pFeas ; rNorm = pResid ; del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu
            else:
                regdu = min(regdu/10, sz/normdy/10, (sz/normdy)**(1.1))
                regdu = max(regdu, regdu_min)
                regpr = min(regpr/10, sz/normdx/10, (sz/normdx)**(1.1))
                regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    if mu < 1.0e-8 * mu0 and rho_q > 1.0e+2 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter-1:
                            if pr_infeas_count > 6:
                                status = 'Problem seems to be (locally) dual infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter

                    if mu < 1.0e-8 * mu0 and del_r > 1.0e+2 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter-1:
                            if du_infeas_count > 6:
                                status='Problem seems to be (locally) primal infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter

            # Display objective and residual data.
            if self.verbose:
                sys.stdout.write(self.format1 % (iter, cx + 0.5 * xQx, pResid,
                                                 dResid, cResid, rgap, qNorm,
                                                 rNorm))

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status = 'iter'
                finished = True
                continue

            # Record some quantities for display
            if ns > 0:
                mins = np.min(s)
                minz = np.min(z)
                maxs = np.max(s)
            else:
                mins = minz = maxs = 0

            # Solve the linear system
            #
            # [ -(Q+pI)      0             A1' ] [∆x] = [c + Q x - A1' y       ]
            # [  0      -(S^{-1} Z + pI)   A2' ] [∆s]   [  - A2' y - µ S^{-1} e]
            # [  A1          A2            dI  ] [∆y]   [ b - A1 x - A2 s      ]
            #
            # where s are the slack variables, p is the primal regularization
            # parameter, d is the dual regularization parameter, and
            # A = [ A1  A2 ]  where the columns of A1 correspond to the original
            # problem variables and those of A2 correspond to slack variables.
            #
            # We recover ∆z = -z - S^{-1} (Z ∆s + µ e).

            # Compute augmented matrix and factorize it.
            factorized = False
            nb_bump = 0
            while not factorized and nb_bump < 5:

                H.put(-diagQ - regpr,    range(on))
                H.put(-z/s   - regpr,  range(on,n))
                H.put(regdu,          range(n,n+m))
                self.LBL.factorize(H)
                factorized = True

                # If the augmented matrix does not have full rank, bump up the
                # regularization parameters.
                if not self.LBL.isFullRank:
                    if self.verbose:
                        sys.stderr.write('Primal-Dual Matrix Rank Deficient')
                        sys.stderr.write('... bumping up reg parameters\n')
                    regpr *= 100 ; regdu *= 100
                    nb_bump += 1
                    factorized = False

            # Abandon if regularization is unsuccessful.
            if not self.LBL.isFullRank and nb_bump == 5:
                status = 'Unable to regularize sufficiently.'
                short_status = 'degn'
                finished = True
                continue

            if PredictorCorrector:
                # Use Mehrotra predictor-corrector method.
                # Compute affine-scaling step, i.e. with centering = 0.
                rhs[:n]    = -dFeas
                rhs[on:n] += z
                rhs[n:]    = -pFeas

                (step, nres, neig) = self.solveSystem(rhs)

                # Recover dx and dz.
                dx = step[:n]
                ds = dx[on:]
                dz = -z * (1 + ds/s)

                # Compute largest allowed primal and dual stepsizes.
                (alpha_p, ip) = self.maxStepLength(s, ds)
                (alpha_d, ip) = self.maxStepLength(z, dz)

                # Estimate duality gap after affine-scaling step.
                muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns
                sigma = (muAff/mu)**3

                # Incorporate predictor information for corrector step.
                comp += ds*dz
            else:
                # Use long-step method: Compute centering parameter.
                sigma = min(0.1, 100*mu)

            # Assemble right-hand side with centering information.
            comp -= sigma * mu

            if PredictorCorrector:
                # Only update rhs[on:n]; the rest of the vector did not change.
                rhs[on:n] += comp/s - z
            else:
                rhs[:n]    = -dFeas
                rhs[on:n] += comp/s
                rhs[n:]    = -pFeas

            # Solve augmented system.
            (step, nres, neig) = self.solveSystem(rhs)

            # Recover step.
            dx = step[:n]
            ds = dx[on:]
            dy = step[n:]
            dz = -(comp + z*ds)/s

            normds = norm2(ds) ; normdy = norm2(dy) ; normdx = norm2(dx)

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0-mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult*mu_tmp - s[ip]*zip)/(alpha_p*ds[ip]*zip)
                    alpha_p *= max(1-mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult*mu_tmp - z[id]*sid)/(alpha_d*dz[id]*sid)
                    alpha_d *= max(1-mult, gamma_d)

                if ip==id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            if self.verbose:
                sys.stdout.write(self.format2 % (mu, alpha_p, alpha_d,
                                                 nres, regpr, regdu, rho_q,
                                                 del_r, mins, minz, maxs))

            # Update iterates and perturbation vectors.
            x += alpha_p * dx    # This also updates slack variables.
            y += alpha_d * dy
            z += alpha_d * dz
            q *= (1-alpha_p) ; q += alpha_p * dx
            r *= (1-alpha_d) ; r += alpha_d * dy
            qNorm = norm2(q) ; rNorm = norm2(r)
            rho_q = regpr * qNorm/(1+self.normc) ; rho_q_min = min(rho_q_min, rho_q)
            del_r = regdu * rNorm/(1+self.normb) ; del_r_min = min(del_r_min, del_r)
            iter += 1

        solve_time = cputime() - setup_time

        if self.verbose:
            sys.stdout.write('\n')
            sys.stdout.write('-' * len(self.header) + '\n')

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid ; self.cResid = cResid ; self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = self.c0 + cx + 0.5 * xQx

        return
Exemple #28
0
    def Solve(self):
        if self.A is not None:
            if self.factorize and not self.factorized: self.Factorize()
            if self.b is not None: self.FindFeasible()

        n = self.n
        m = self.m
        xNorm2 = 0.0   # Squared norm of current iterate x, not counting x_feas

        # Obtain initial projected residual
        self.t_solve = cputime()
        if self.A is not None:
            if self.b is not None:
                self.rhs[:n] = self.c + self.H * self.x_feasible
                self.rhs[n:] = 0.0
            else:
                self.rhs[:n] = self.c
            self.Proj.solve( self.rhs )
            r = g = self.Proj.x[:n]
            self.v = self.Proj.x[n:]

            #self.CheckAccurate()

        else:
            g = self.c
            r = g.copy()

        # Initialize search direction
        p = -g
        pHp = None

        self.residNorm0 = numpy.dot(r,g)
        rg  = self.residNorm0
        threshold = max( self.abstol, self.reltol * sqrt(self.residNorm0) )
        iter = 0
        onBoundary = False

        if self.debug:
            self._write( self.header )
            self._write( '-' * len(self.header) + '\n' )
            self._write( self.fmt1 % (iter, rg) )

        while sqrt(rg) > threshold and iter < self.maxiter and not onBoundary:

            Hp = self.H * p
            pHp = numpy.dot(p,Hp)

            # Display current iteration info
            if self.debug: self._write( self.fmt % (iter, rg, pHp) )

            if self.radius is not None:
                # Compute steplength to the boundary
                sigma = self.to_boundary(self.x, p, self.radius, ss=xNorm2)
            elif pHp <= 0.0:
                self._write('Problem is not second-order sufficient\n')
                status = 'problem not SOS'
                self.infDescent = True
                self.dir = p
                continue

            alpha = rg/pHp

            if self.radius is not None and (pHp <= 0.0 or alpha > sigma):
                # p is a direction of singularity or negative curvature or
                # next iterate will lie past the boundary of the trust region
                # Move to boundary of trust-region
                self.x += sigma * p
                xNorm2 = self.radius * self.radius
                status = 'on boundary (sigma = %g)' % sigma
                self.infDescent = True
                onBoundary = True
                continue

            # Make sure nonnegativity bounds remain enforced, if requested
            if (self.btol is not None) and (self.cur_iter is not None):
                stepBnd = self.ftb(self.x, p)
                if stepBnd < alpha:
                    self.x += stepBnd * p
                    status = 'on boundary'
                    onBoundary = True
                    continue

            # Move on
            self.x += alpha * p
            r += alpha * Hp

            if self.A is not None:
                # Project current residual
                self.rhs[:n] = r
                self.Proj.solve( self.rhs )

                # Perform actual iterative refinement, if necessary
                #self.Proj.refine( self.rhs, nitref=self.max_itref,
                #                  tol=self.itref_tol )

                # Obtain new projected gradient
                g = self.Proj.x[:n]
                if self.precon is not None:
                    # Prepare for iterative semi-refinement
                    self.A.matvec_transp( self.Proj.x[n:], self.v )
            else:
                g = r

            rg_next = numpy.dot(r,g)
            beta = rg_next/rg
            p = -g + beta * p
            if self.precon is not None:
                # Perform iterative semi-refinement
                r = r - self.v
            else:
                r = g
            rg = rg_next

            if self.radius is not None:
                xNorm2 = numpy.dot( self.x, self.x )
            iter += 1

        # Output info about the last iteration
        if self.debug and iter > 0:
            self._write( self.fmt % (iter, rg, pHp) )

        # Obtain final solution x
        self.xNorm2 = xNorm2
        self.stepNorm = sqrt(xNorm2)
        if self.x_feasible is not None:
            self.x += self.x_feasible

        if self.A is not None:
            # Find (weighted) least-squares Lagrange multipliers
            self.rhs[:n] = - self.c - self.H * self.x
            self.rhs[n:] = 0.0
            self.Proj.solve( self.rhs )
            self.v = self.Proj.x[n:].copy()

        self.t_solve = cputime() - self.t_solve

        self.step = self.x  # Alias for consistency with TruncatedCG.
        self.onBoundary = onBoundary
        self.converged = (iter < self.maxiter)
        if iter < self.maxiter and not onBoundary:
            status = 'residual small'
        elif iter >= self.maxiter:
            status = 'max iter'
        self.iter = iter
        self.nMatvec = iter
        self.residNorm = sqrt(rg)
        self.status = status

        return
Exemple #29
0
def test_pycfs(ProblemList, plist=[0, 2, 5, 10], latex=False):

    if len(ProblemList) == 0:
        usage()
        sys.exit(1)

    if latex:
        # For LaTeX output
        fmt = '%-12s & %-5s & %-6s & %-2s & %-4s & %-8s & %-4s & %-8s & %-6s & %-6s\\\\\n'
        fmt1 = '%-12s & %-5d & %-6d & '
        fmt2 = '%-2d & %-4d & %-8.1e & %-4d & %-8.1e & %-6.2f & %-6.2f\\\\\n'
        hline = '\\hline\n'
        skip = '&&&'
    else:
        # For ASCII output
        fmt = '%-12s %-5s %-6s %-2s %-4s %-8s %-4s %-8s %-6s %-6s\n'
        fmt1 = '%-12s %-5d %-6d '
        fmt2 = '%-2d %-4d %-8.1e %-4d %-8.1e %-6.2f %-6.2f\n'
        skip = ' ' * 26

    header = fmt % ('Name', 'Size', 'nnz', 'p', 'info', 'shift', 'iter',
                    'relResid', 'fact', 'solve')
    lhead = len(header)
    if not latex: hline = '-' * lhead + '\n'
    sys.stderr.write(hline + header + hline)

    time_list = {}  # Record timings
    iter_list = {}  # Record number of iterations

    for problem in ProblemList:

        A = spmatrix.ll_mat_from_mtx(problem)
        (m, n) = A.shape
        if m != n: break

        prob = os.path.basename(problem)
        if prob[-4:] == '.mtx': prob = prob[:-4]

        # Right-hand side is Ae, as in Icfs.
        e = numpy.ones(n, 'd')
        b = numpy.ones(n, 'd')
        A.matvec(e, b)

        sys.stdout.write(fmt1 % (prob, n, A.nnz))
        advance = False

        # Call icfs and pcg
        tlist = []
        ilist = []
        for pval in plist:
            t0 = cputime()
            P = pycfs.PycfsContext(A, mem=pval)
            t_fact = cputime() - t0
            P.solve(b)
            t_solve = P.tsolve
            tlist.append(t_fact + t_solve)
            ilist.append(P.iter)

            if advance: sys.stdout.write(skip)
            sys.stdout.write(
                fmt2 %
                (pval, P.info, P.shift, P.iter, P.relres, t_fact, t_solve))
            advance = True

        time_list[prob] = tlist
        iter_list[prob] = ilist
        sys.stderr.write(hline)

    return (time_list, iter_list)
Exemple #30
0
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:

          :itermax:  The maximum allowed number of iterations (default: 10n)
          :tolerance:  Stopping tolerance (default: 1.0e-6)
          :PredictorCorrector:  Use the predictor-corrector method
                                (default: `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        :returns:

            :x:            final iterate
            :y:            final value of the Lagrange multipliers associated
                           to `A1 x + A2 s = b`
            :z:            final value of the Lagrange multipliers associated
                           to `s >= 0`
            :obj_value:    final cost
            :iter:         total number of iterations
            :kktResid:     final relative residual
            :solve_time:   time to solve the QP
            :status:       string describing the exit status.
            :short_status: short version of status, used for printing.

        """
        qp = self.qp
        itermax = kwargs.get('itermax', max(100, 10 * qp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape
        on = qp.original_n
        A = self.A
        b = self.b
        c = self.c
        Q = self.Q
        diagQ = self.diagQ
        H = self.H

        regpr = self.regpr
        regdu = self.regdu
        regpr_min = self.regpr_min
        regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        (x, y, z) = self.set_initial_guess(**kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:]
        ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        # Allocate room for right-hand side of linear systems.
        rhs = self.initialize_rhs()
        finished = False
        iter = 0

        setup_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if iter % 50 == 0:
                self.log.info(self.header)
                self.log.info('-' * len(self.header))

            # Compute residuals.
            pFeas = A * x - b
            comp = s * z
            sz = sum(comp)  # comp   = Sz
            Qx = Q * x[:on]
            dFeas = y * A
            dFeas[:on] -= self.c + Qx  # dFeas1 = A1'y - c - Qx
            dFeas[on:] += z  # dFeas2 = A2'y + z

            # Compute duality measure.
            if ns > 0:
                mu = sz / ns
            else:
                mu = 0.0

            # Compute residual norms and scaled residual norms.
            pResid = norm2(pFeas)
            spResid = pResid / (1 + self.normb + self.normA + self.normQ)
            dResid = norm2(dFeas)
            sdResid = dResid / (1 + self.normc + self.normA + self.normQ)
            if ns > 0:
                cResid = norm_infty(comp) / (self.normbc + self.normA +
                                             self.normQ)
            else:
                cResid = 0.0

            # Compute relative duality gap.
            cx = np.dot(c, x[:on])
            xQx = np.dot(x[:on], Qx)
            by = np.dot(b, y)
            rgap = cx + xQx - by
            rgap = abs(rgap) / (1 + abs(cx) + self.normA + self.normQ)
            rgap2 = mu / (1 + abs(cx) + self.normA + self.normQ)

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            # Should probably get rid of q when regpr=0 and of r when regdu=0.
            if iter == 0:
                if regpr > 0:
                    q = dFeas / regpr
                    qNorm = dResid / regpr
                    rho_q = dResid
                else:
                    q = dFeas
                    qNorm = dResid
                    rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas / regdu
                    rNorm = pResid / regdu
                    del_r = pResid
                else:
                    r = -pFeas
                    rNorm = pResid
                    del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu

            else:

                if regdu > 0:
                    regdu = regdu / 10
                    regdu = max(regdu, regdu_min)
                if regpr > 0:
                    regpr = regpr / 10
                    regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    if mu < tolerance/100 * mu0 and \
                            rho_q > 1./tolerance/1.0e+6 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter - 1:
                            if pr_infeas_count > 6:
                                status = 'Problem seems to be (locally) dual'
                                status += ' infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter
                    else:
                        pr_infeas_count = 0

                    if mu < tolerance/100 * mu0 and \
                            del_r > 1./tolerance/1.0e+6 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter - 1:
                            if du_infeas_count > 6:
                                status = 'Problem seems to be (locally) primal'
                                status += ' infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter
                    else:
                        du_infeas_count = 0

            # Display objective and residual data.
            output_line = self.format1 % (iter, cx + 0.5 * xQx, pResid, dResid,
                                          cResid, rgap, qNorm, rNorm)

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status = 'iter'
                finished = True
                continue

            # Record some quantities for display
            if ns > 0:
                mins = np.min(s)
                minz = np.min(z)
                maxs = np.max(s)
            else:
                mins = minz = maxs = 0

            # Compute augmented matrix and factorize it.

            factorized = False
            degenerate = False
            nb_bump = 0
            while not factorized and not degenerate:

                self.update_linear_system(s, z, regpr, regdu)
                self.log.debug('Factorizing')
                self.LBL.factorize(H)
                factorized = True

                # If the augmented matrix does not have full rank, bump up the
                # regularization parameters.
                if not self.LBL.isFullRank:
                    if self.verbose:
                        self.log.info('Primal-Dual Matrix Rank Deficient' + \
                                      '... bumping up reg parameters')

                    if regpr == 0. and regdu == 0.:
                        degenerate = True
                    else:
                        if regpr > 0:
                            regpr *= 100
                        if regdu > 0:
                            regdu *= 100
                        nb_bump += 1
                        degenerate = nb_bump > self.bump_max
                    factorized = False

            # Abandon if regularization is unsuccessful.
            if not self.LBL.isFullRank and degenerate:
                status = 'Unable to regularize sufficiently.'
                short_status = 'degn'
                finished = True
                continue

            if PredictorCorrector:
                # Use Mehrotra predictor-corrector method.
                # Compute affine-scaling step, i.e. with centering = 0.
                self.set_affine_scaling_rhs(rhs, pFeas, dFeas, s, z)

                (step, nres, neig) = self.solveSystem(rhs)

                # Recover dx and dz.
                dx, ds, dy, dz = self.get_affine_scaling_dxsyz(
                    step, x, s, y, z)

                # Compute largest allowed primal and dual stepsizes.
                (alpha_p, ip) = self.maxStepLength(s, ds)
                (alpha_d, ip) = self.maxStepLength(z, dz)

                # Estimate duality gap after affine-scaling step.
                muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz) / ns
                sigma = (muAff / mu)**3

                # Incorporate predictor information for corrector step.
                # Only update rhs[on:n]; the rest of the vector did not change.
                comp += ds * dz
                comp -= sigma * mu
                self.update_corrector_rhs(rhs, s, z, comp)
            else:
                # Use long-step method: Compute centering parameter.
                sigma = min(0.1, 100 * mu)
                comp -= sigma * mu

                # Assemble rhs.
                self.update_long_step_rhs(rhs, pFeas, dFeas, comp, s)

            # Solve augmented system.
            (step, nres, neig) = self.solveSystem(rhs)

            # Recover step.
            dx, ds, dy, dz = self.get_dxsyz(step, x, s, y, z, comp)

            normds = norm2(ds)
            normdy = norm2(dy)
            normdx = norm2(dx)

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0 - mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic.
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz) / ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult * mu_tmp - s[ip] * zip) / (alpha_p *
                                                               ds[ip] * zip)
                    alpha_p *= max(1 - mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult * mu_tmp - z[id] * sid) / (alpha_d *
                                                               dz[id] * sid)
                    alpha_d *= max(1 - mult, gamma_d)

                if ip == id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            output_line += self.format2 % (mu, alpha_p, alpha_d, nres, regpr,
                                           regdu, rho_q, del_r, mins, minz,
                                           maxs)
            self.log.info(output_line)

            # Update iterates and perturbation vectors.
            x += alpha_p * dx  # This also updates slack variables.
            y += alpha_d * dy
            z += alpha_d * dz
            q *= (1 - alpha_p)
            q += alpha_p * dx
            r *= (1 - alpha_d)
            r += alpha_d * dy
            qNorm = norm2(q)
            rNorm = norm2(r)
            if regpr > 0:
                rho_q = regpr * qNorm / (1 + self.normc)
                rho_q_min = min(rho_q_min, rho_q)
            else:
                rho_q = 0.0
            if regdu > 0:
                del_r = regdu * rNorm / (1 + self.normb)
                del_r_min = min(del_r_min, del_r)
            else:
                del_r = 0.0
            iter += 1

        solve_time = cputime() - setup_time

        self.log.info('-' * len(self.header))

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid
        self.cResid = cResid
        self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = self.c0 + cx + 0.5 * xQx

        return
Exemple #31
0
    def __init__(self, lp, **kwargs):
        """
        Solve a linear program of the form::

            minimize c' x   subject to  A1 x + A2 s = b  and  s >= 0,      (LP)

        where the variables x are the original problem variables and s are
        slack variables. Any linear program may be converted to the above form
        by instantiation of the `SlackFramework` class. The conversion to the
        slack formulation is mandatory in this implementation.

        The method is a variant of Mehrotra's predictor-corrector method where
        steps are computed by solving the primal-dual system in augmented form.

        Primal and dual regularization parameters may be specified by the user
        via the opional keyword arguments `regpr` and `regdu`. Both should be
        positive real numbers and should not be "too large". By default they are
        set to 1.0 and updated at each iteration.

        If `scale` is set to `True`, (LP) is scaled automatically prior to
        solution so as to equilibrate the rows and columns of the constraint
        matrix [A1 A2].

        Advantages of this method are that it is not sensitive to dense columns
        in A, no special treatment of the unbounded variables x is required, and
        a sparse symmetric quasi-definite system of equations is solved at each
        iteration. The latter, although indefinite, possesses a Cholesky-like
        factorization. Those properties makes the method typically more robust
        that a standard predictor-corrector implementation and the linear system
        solves are often much faster than in a traditional interior-point method
        in augmented form.

        :keywords:
            :scale: Perform row and column equilibration of the constraint
                    matrix [A1 A2] prior to solution (default: `True`).

            :stabilize: Scale the linear system to be solved at each iteration
                        (default: `True`).

            :regpr: Initial value of primal regularization parameter
                    (default: `1.0`).

            :regdu: Initial value of dual regularization parameter
                    (default: `1.0`).

            :verbose: Turn on verbose mode (default `False`).
        """

        if not isinstance(lp, SlackFramework):
            msg = 'Input problem must be an instance of SlackFramework'
            raise ValueError, msg

        scale = kwargs.get('scale', True)
        self.verbose = kwargs.get('verbose', True)
        self.stabilize = kwargs.get('stabilize', True)

        self.lp = lp
        self.A = lp.A()               # Constraint matrix
        if not isinstance(self.A, PysparseMatrix):
            self.A = PysparseMatrix(matrix=self.A)

        m, n = self.A.shape
        # Record number of slack variables in LP
        self.nSlacks  = lp.n - lp.original_n

        # Constant vectors
        zero = np.zeros(n)
        self.b = -lp.cons(zero)     # Right-hand side
        self.c0 = lp.obj(zero)      # Constant term in objective
        self.c =  lp.grad(zero[:lp.original_n]) #lp.cost()  # Cost vector

        # Apply in-place problem scaling if requested.
        self.prob_scaled = False
        if scale:
            self.t_scale = cputime()
            self.scale()
            self.t_scale = cputime() - self.t_scale

        self.normb  = norm2(self.b)
        self.normc  = norm2(self.c)
        self.normbc = 1 + max(self.normb, self.normc)

        # Initialize augmented matrix
        self.H = PysparseMatrix(size=n+m,
                                sizeHint=n+m+self.A.nnz,
                                symmetric=True)

        # We perform the analyze phase on the augmented system only once.
        # self.LBL will be initialized in set_initial_guess().
        self.LBL = None

        self.regpr = kwargs.get('regpr', 1.0) ; self.regpr_min = 1.0e-8
        self.regdu = kwargs.get('regdu', 1.0) ; self.regdu_min = 1.0e-8

        # Check input parameters.
        if self.regpr < 0.0: self.regpr = 0.0
        if self.regdu < 0.0: self.regdu = 0.0

        # Dual regularization is necessary for stabilization.
        if self.regdu == 0.0:
            sys.stderr.write('Warning: No dual regularization in effect\n')
            sys.stderr.write('         Stabilization has been turned off\n')
            self.stabilize = False

        # Initialize format strings for display
        fmt_hdr = '%-4s  %9s' + '  %-8s'*6 + '  %-7s  %-4s  %-4s' + '  %-8s'*8
        self.header = fmt_hdr % ('Iter', 'Cost', 'pResid', 'dResid', 'cResid',
                                 'rGap', 'qNorm', 'rNorm', 'Mu', 'AlPr', 'AlDu',
                                 'LS Resid', 'RegPr', 'RegDu', 'Rho q', 'Del r',
                                 'Min(s)', 'Min(z)', 'Max(s)')
        self.format1  = '%-4d  %9.2e'
        self.format1 += '  %-8.2e' * 6
        self.format2  = '  %-7.1e  %-4.2f  %-4.2f'
        self.format2 += '  %-8.2e' * 8 + '\n'

        if self.verbose: self.display_stats()

        return
Exemple #32
0
def gltr_explicit(H, g, **kwargs):
    G = pygltr.PyGltrContext(g, **kwargs)
    t = cputime()
    G.explicit_solve(H.to_csr())
    t = cputime() - t
    return (G.m, G.mult, G.snorm, G.niter, G.nc, G.ierr, t)
Exemple #33
0
def gltr_explicit(H, g, **kwargs):
    G = pygltr.PyGltrContext(g, **kwargs)
    t = cputime()
    G.explicit_solve(H.to_csr())
    t = cputime() - t
    return (G.m, G.mult, G.snorm, G.niter, G.nc, G.ierr, t)
Exemple #34
0
    def Solve(self):
        if self.A is not None:
            if self.factorize and not self.factorized: self.Factorize()
            if self.b is not None: self.FindFeasible()

        n = self.n
        m = self.m
        xNorm2 = 0.0  # Squared norm of current iterate x, not counting x_feas

        # Obtain initial projected residual
        self.t_solve = cputime()
        if self.A is not None:
            if self.b is not None:
                self.rhs[:n] = self.c + self.H * self.x_feasible
                self.rhs[n:] = 0.0
            else:
                self.rhs[:n] = self.c
            self.Proj.solve(self.rhs)
            r = g = self.Proj.x[:n]
            self.v = self.Proj.x[n:]

            #self.CheckAccurate()

        else:
            g = self.c
            r = g.copy()

        # Initialize search direction
        p = -g
        pHp = None

        self.residNorm0 = numpy.dot(r, g)
        rg = self.residNorm0
        threshold = max(self.abstol, self.reltol * sqrt(self.residNorm0))
        iter = 0
        onBoundary = False

        if self.debug:
            self._write(self.header)
            self._write('-' * len(self.header) + '\n')
            self._write(self.fmt1 % (iter, rg))

        while sqrt(rg) > threshold and iter < self.maxiter and not onBoundary:

            Hp = self.H * p
            pHp = numpy.dot(p, Hp)

            # Display current iteration info
            if self.debug: self._write(self.fmt % (iter, rg, pHp))

            if self.radius is not None:
                # Compute steplength to the boundary
                sigma = self.to_boundary(self.x, p, self.radius, ss=xNorm2)
            elif pHp <= 0.0:
                self._write('Problem is not second-order sufficient\n')
                status = 'problem not SOS'
                self.infDescent = True
                self.dir = p
                continue

            alpha = rg / pHp

            if self.radius is not None and (pHp <= 0.0 or alpha > sigma):
                # p is a direction of singularity or negative curvature or
                # next iterate will lie past the boundary of the trust region
                # Move to boundary of trust-region
                self.x += sigma * p
                xNorm2 = self.radius * self.radius
                status = 'on boundary (sigma = %g)' % sigma
                self.infDescent = True
                onBoundary = True
                continue

            # Make sure nonnegativity bounds remain enforced, if requested
            if (self.btol is not None) and (self.cur_iter is not None):
                stepBnd = self.ftb(self.x, p)
                if stepBnd < alpha:
                    self.x += stepBnd * p
                    status = 'on boundary'
                    onBoundary = True
                    continue

            # Move on
            self.x += alpha * p
            r += alpha * Hp

            if self.A is not None:
                # Project current residual
                self.rhs[:n] = r
                self.Proj.solve(self.rhs)

                # Perform actual iterative refinement, if necessary
                #self.Proj.refine( self.rhs, nitref=self.max_itref,
                #                  tol=self.itref_tol )

                # Obtain new projected gradient
                g = self.Proj.x[:n]
                if self.precon is not None:
                    # Prepare for iterative semi-refinement
                    self.A.matvec_transp(self.Proj.x[n:], self.v)
            else:
                g = r

            rg_next = numpy.dot(r, g)
            beta = rg_next / rg
            p = -g + beta * p
            if self.precon is not None:
                # Perform iterative semi-refinement
                r = r - self.v
            else:
                r = g
            rg = rg_next

            if self.radius is not None:
                xNorm2 = numpy.dot(self.x, self.x)
            iter += 1

        # Output info about the last iteration
        if self.debug and iter > 0:
            self._write(self.fmt % (iter, rg, pHp))

        # Obtain final solution x
        self.xNorm2 = xNorm2
        self.stepNorm = sqrt(xNorm2)
        if self.x_feasible is not None:
            self.x += self.x_feasible

        if self.A is not None:
            # Find (weighted) least-squares Lagrange multipliers
            self.rhs[:n] = -self.c - self.H * self.x
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.v = self.Proj.x[n:].copy()

        self.t_solve = cputime() - self.t_solve

        self.step = self.x  # Alias for consistency with TruncatedCG.
        self.onBoundary = onBoundary
        self.converged = (iter < self.maxiter)
        if iter < self.maxiter and not onBoundary:
            status = 'residual small'
        elif iter >= self.maxiter:
            status = 'max iter'
        self.iter = iter
        self.nMatvec = iter
        self.residNorm = sqrt(rg)
        self.status = status

        return
Exemple #35
0
Fichier : lp.py Projet : mpf/nlpy
    def __init__(self, lp, **kwargs):
        """
        Solve a linear program of the form::

            minimize c' x   subject to  A1 x + A2 s = b  and  s >= 0,      (LP)

        where the variables x are the original problem variables and s are
        slack variables. Any linear program may be converted to the above form
        by instantiation of the `SlackFramework` class. The conversion to the
        slack formulation is mandatory in this implementation.

        The method is a variant of Mehrotra's predictor-corrector method where
        steps are computed by solving the primal-dual system in augmented form.

        Primal and dual regularization parameters may be specified by the user
        via the opional keyword arguments `regpr` and `regdu`. Both should be
        positive real numbers and should not be "too large". By default they are
        set to 1.0 and updated at each iteration.

        If `scale` is set to `True`, (LP) is scaled automatically prior to
        solution so as to equilibrate the rows and columns of the constraint
        matrix [A1 A2].

        Advantages of this method are that it is not sensitive to dense columns
        in A, no special treatment of the unbounded variables x is required, and
        a sparse symmetric quasi-definite system of equations is solved at each
        iteration. The latter, although indefinite, possesses a Cholesky-like
        factorization. Those properties makes the method typically more robust
        that a standard predictor-corrector implementation and the linear system
        solves are often much faster than in a traditional interior-point method
        in augmented form.

        :keywords:
            :scale: Perform row and column equilibration of the constraint
                    matrix [A1 A2] prior to solution (default: `True`).

            :stabilize: Scale the linear system to be solved at each iteration
                        (default: `True`).

            :regpr: Initial value of primal regularization parameter
                    (default: `1.0`).

            :regdu: Initial value of dual regularization parameter
                    (default: `1.0`).

            :verbose: Turn on verbose mode (default `False`).
        """

        if not isinstance(lp, SlackFramework):
            msg = 'Input problem must be an instance of SlackFramework'
            raise ValueError, msg

        scale = kwargs.get('scale', True)
        self.verbose = kwargs.get('verbose', True)
        self.stabilize = kwargs.get('stabilize', True)

        self.lp = lp
        self.A = lp.A()               # Constraint matrix
        if not isinstance(self.A, PysparseMatrix):
            self.A = PysparseMatrix(matrix=self.A)

        m, n = self.A.shape
        # Record number of slack variables in LP
        self.nSlacks  = lp.n - lp.original_n

        # Constant vectors
        zero = np.zeros(n)
        self.b = -lp.cons(zero)     # Right-hand side
        self.c0 = lp.obj(zero)      # Constant term in objective
        self.c =  lp.grad(zero[:lp.original_n]) #lp.cost()  # Cost vector

        # Apply in-place problem scaling if requested.
        self.prob_scaled = False
        if scale:
            self.t_scale = cputime()
            self.scale()
            self.t_scale = cputime() - self.t_scale
        else:
            # scale() sets self.normA to the Frobenius norm of A as a
            # by-product. Set it manually here if scaling is not enabled.
            self.normA = self.A.matrix.norm('fro')

        self.normb  = norm_infty(self.b) #norm2(self.b)
        self.normc  = norm_infty(self.c) #norm2(self.c)
        self.normbc = 1 + max(self.normb, self.normc)

        # Initialize augmented matrix
        self.H = PysparseMatrix(size=n+m,
                                sizeHint=n+m+self.A.nnz,
                                symmetric=True)

        # We perform the analyze phase on the augmented system only once.
        # self.LBL will be initialized in set_initial_guess().
        self.LBL = None

        self.regpr = kwargs.get('regpr', 1.0) ; self.regpr_min = 1.0e-8
        self.regdu = kwargs.get('regdu', 1.0) ; self.regdu_min = 1.0e-8

        # Check input parameters.
        if self.regpr < 0.0: self.regpr = 0.0
        if self.regdu < 0.0: self.regdu = 0.0

        # Dual regularization is necessary for stabilization.
        if self.regdu == 0.0:
            sys.stderr.write('Warning: No dual regularization in effect\n')
            sys.stderr.write('         Stabilization has been turned off\n')
            self.stabilize = False

        # Initialize format strings for display
        fmt_hdr = '%-4s  %9s' + '  %-8s'*6 + '  %-7s  %-4s  %-4s' + '  %-8s'*8
        self.header = fmt_hdr % ('Iter', 'Cost', 'pResid', 'dResid', 'cResid',
                                 'rGap', 'qNorm', 'rNorm', 'Mu', 'AlPr', 'AlDu',
                                 'LS Resid', 'RegPr', 'RegDu', 'Rho q', 'Del r',
                                 'Min(s)', 'Min(z)', 'Max(s)')
        self.format1  = '%-4d  %9.2e'
        self.format1 += '  %-8.2e' * 6
        self.format2  = '  %-7.1e  %-4.2f  %-4.2f'
        self.format2 += '  %-8.2e' * 8 + '\n'

        if self.verbose: self.display_stats()

        return
Exemple #36
0
    def Solve( self ):

        # Find feasible solution
        if self.A is not None:
            if self.factorize and not self.factorized: self.Factorize()
            if self.b is not None: self.FindFeasible()

        n = self.n
        m = self.m
        nMatvec = 0
        alpha = beta = omega = 0.0

        self.t_solve = cputime()

        # Obtain fixed vector r0 = projected initial residual
        # (initial x = 0 in homogeneous problem.)
        if self.A is not None:
            self.rhs[:n] = self.r
            self.rhs[n:] = 0.0
            self.Proj.solve( self.rhs )
            r0 = self.Proj.x[:n].copy()
            Btv = self.r - r0
        else:
            r0 = self.c

        # Initialize search direction
        self.p = self.r

        # Further initializations
        rr0 = rr00 = numpy.dot(self.r, r0)
        residNorm = self.residNorm0 = sqrt(rr0)
        stopTol = self.abstol + self.reltol * self.residNorm0
        finished = False

        if self.debug:
            self._write( self.header )
            self._write( '-' * len(self.header) + '\n' )

        if self.debug:
            self._write(self.fmt % (nMatvec, residNorm, rr0, alpha, omega))

        while not finished:

            # Project p
            self.rhs[:n] = self.p
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.Pp = self.Proj.x[:n]

            # Compute alpha and s
            if self._matvec_found:
                # Here we must copy Ap to prevent it from being overwritten
                # in the next matvec. We still need Ap when we update p below.
                self.Ap = self.matvec( self.Pp ).copy()
            else:
                self.H.matvec( self.Pp, self.Ap )
            nMatvec += 1

            alpha = rr0/numpy.dot(r0, self.Ap)
            self.s = self.r - alpha * self.Ap

            # Project s
            self.rhs[:n] = self.s - Btv              # Iterative semi-refinement
            self.rhs[n:] = 0.0
            self.Proj.solve(self.rhs)
            self.Ps = self.Proj.x[:n].copy()
            Btv = self.s - self.Ps

            residNorm = sqrt(numpy.dot(self.s, self.Ps))

            # Test for termination in the CGS process
            if residNorm <= stopTol or nMatvec > self.nMatvecMax:
                self.x += alpha * self.Pp
                if nMatvec > self.nMatvecMax:
                    reason = 'matvec'
                else:
                    reason = 's small'
                finished = True

            else:

                # Project  A*Ps
                if self._matvec_found:
                    self.As = self.matvec( self.Ps )
                else:
                    self.H.matvec( self.Ps, self.As )
                nMatvec += 1
                self.rhs[:n] = self.As
                self.rhs[n:] = 0.0
                self.Proj.solve(self.rhs)

                # Compute omega and update x
                sAs = numpy.dot(self.Ps, self.As)
                AsPAs = numpy.dot(self.As, self.Proj.x[:n])
                omega = sAs/AsPAs
                self.x += alpha * self.Pp + omega * self.Ps

                # Check for termination
                if nMatvec > self.nMatvecMax:
                    finished = True
                    reason = 'matvec'

                else:

                    # Update residual
                    self.r = self.s - omega * self.As
                    rr0_next = numpy.dot(self.r, r0)
                    beta = alpha/omega * rr0_next/rr0
                    rr0 = rr0_next
                    self.p -= omega * self.Ap
                    self.p *= beta
                    self.p += self.r

                    # Check for termination in the Bi-CGSTAB process
                    if abs(rr0) < 1.0e-12 * rr00:
                        self.rhs[:n] = self.r
                        self.rhs[n:] = 0.0
                        self.Proj.solve(self.rhs)
                        rPr = numpy.dot(self.r, self.Proj.x[:n])
                        if sqrt(rPr) <= stopTol:
                            finished = True
                            reason = 'r small'

            # Display current iteration info
            if self.debug:
                self._write(self.fmt % (nMatvec, residNorm, rr0, alpha, omega))

        # End while

        # Obtain final solution x
        if self.x_feasible is not None:
            self.x += self.x_feasible

        if self.A is not None:
            # Find (weighted) least-squares Lagrange multipliers from
            #   [ G  B^T ] [w]   [c - Hx]
            #   [ B   0  ] [v] = [  0   ]
            if self._matvec_found:
                self.rhs[:n] = -self.matvec( self.x )
            else:
                self.H.matvec( -self.x, self.rhs[:n] )
            self.rhs[:n] += self.c
            self.rhs[n:] = 0.0
            self.Proj.solve( self.rhs )
            self.v = self.Proj.x[n:].copy()

        self.t_solve = cputime() - self.t_solve
        self.converged = (nMatvec < self.nMatvecMax)
        self.nMatvec = nMatvec
        self.residNorm = residNorm
        self.status = reason

        return
Exemple #37
0
    def solve(self, **kwargs):
        """
        Solve the input problem with the primal-dual-regularized
        interior-point method. Accepted input keyword arguments are

        :keywords:

          :itermax:  The maximum allowed number of iterations (default: 10n)
          :tolerance:  Stopping tolerance (default: 1.0e-6)
          :PredictorCorrector:  Use the predictor-corrector method
                                (default: `True`). If set to `False`, a variant
                                of the long-step method is used. The long-step
                                method is generally slower and less robust.

        :returns:

            :x:            final iterate
            :y:            final value of the Lagrange multipliers associated
                           to `A1 x + A2 s = b`
            :z:            final value of the Lagrange multipliers associated
                           to `s >= 0`
            :obj_value:    final cost
            :iter:         total number of iterations
            :kktResid:     final relative residual
            :solve_time:   time to solve the QP
            :status:       string describing the exit status.
            :short_status: short version of status, used for printing.

        """
        qp = self.qp
        itermax = kwargs.get('itermax', max(100,10*qp.n))
        tolerance = kwargs.get('tolerance', 1.0e-6)
        PredictorCorrector = kwargs.get('PredictorCorrector', True)
        check_infeasible = kwargs.get('check_infeasible', True)

        # Transfer pointers for convenience.
        m, n = self.A.shape ; on = qp.original_n
        A = self.A ; b = self.b ; c = self.c ; Q = self.Q ; diagQ = self.diagQ
        H = self.H

        regpr = self.regpr ; regdu = self.regdu
        regpr_min = self.regpr_min ; regdu_min = self.regdu_min

        # Obtain initial point from Mehrotra's heuristic.
        (x,y,z) = self.set_initial_guess(**kwargs)

        # Slack variables are the trailing variables in x.
        s = x[on:] ; ns = self.nSlacks

        # Initialize steps in dual variables.
        dz = np.zeros(ns)

        # Allocate room for right-hand side of linear systems.
        rhs = self.initialize_rhs()
        finished = False
        iter = 0

        setup_time = cputime()

        # Main loop.
        while not finished:

            # Display initial header every so often.
            if iter % 50 == 0:
                self.log.info(self.header)
                self.log.info('-' * len(self.header))

            # Compute residuals.
            pFeas = A*x - b
            comp = s*z ; sz = sum(comp)                # comp   = Sz
            Qx = Q*x[:on]
            dFeas = y*A ; dFeas[:on] -= self.c + Qx    # dFeas1 = A1'y - c - Qx
            dFeas[on:] += z                            # dFeas2 = A2'y + z

            # Compute duality measure.
            if ns > 0:
                mu = sz/ns
            else:
                mu = 0.0

            # Compute residual norms and scaled residual norms.
            pResid = norm2(pFeas)
            spResid = pResid/(1+self.normb+self.normA+self.normQ)
            dResid = norm2(dFeas)
            sdResid = dResid/(1+self.normc+self.normA+self.normQ)
            if ns > 0:
                cResid = norm_infty(comp)/(self.normbc+self.normA+self.normQ)
            else:
                cResid = 0.0

            # Compute relative duality gap.
            cx = np.dot(c,x[:on])
            xQx = np.dot(x[:on],Qx)
            by = np.dot(b,y)
            rgap  = cx + xQx - by
            rgap  = abs(rgap) / (1 + abs(cx) + self.normA + self.normQ)
            rgap2 = mu / (1 + abs(cx) + self.normA + self.normQ)

            # Compute overall residual for stopping condition.
            kktResid = max(spResid, sdResid, rgap2)

            # At the first iteration, initialize perturbation vectors
            # (q=primal, r=dual).
            # Should probably get rid of q when regpr=0 and of r when regdu=0.
            if iter == 0:
                if regpr > 0:
                    q =  dFeas/regpr ; qNorm = dResid/regpr ; rho_q = dResid
                else:
                    q =  dFeas ; qNorm = dResid ; rho_q = 0.0
                rho_q_min = rho_q
                if regdu > 0:
                    r = -pFeas/regdu ; rNorm = pResid/regdu ; del_r = pResid
                else:
                    r = -pFeas ; rNorm = pResid ; del_r = 0.0
                del_r_min = del_r
                pr_infeas_count = 0  # Used to detect primal infeasibility.
                du_infeas_count = 0  # Used to detect dual infeasibility.
                pr_last_iter = 0
                du_last_iter = 0
                mu0 = mu

            else:

                if regdu > 0:
                    regdu = regdu/10
                    regdu = max(regdu, regdu_min)
                if regpr > 0:
                    regpr = regpr/10
                    regpr = max(regpr, regpr_min)

                # Check for infeasible problem.
                if check_infeasible:
                    if mu < tolerance/100 * mu0 and \
                            rho_q > 1./tolerance/1.0e+6 * rho_q_min:
                        pr_infeas_count += 1
                        if pr_infeas_count > 1 and pr_last_iter == iter-1:
                            if pr_infeas_count > 6:
                                status  = 'Problem seems to be (locally) dual'
                                status += ' infeasible'
                                short_status = 'dInf'
                                finished = True
                                continue
                        pr_last_iter = iter
                    else:
                        pr_infeas_count = 0

                    if mu < tolerance/100 * mu0 and \
                            del_r > 1./tolerance/1.0e+6 * del_r_min:
                        du_infeas_count += 1
                        if du_infeas_count > 1 and du_last_iter == iter-1:
                            if du_infeas_count > 6:
                                status = 'Problem seems to be (locally) primal'
                                status += ' infeasible'
                                short_status = 'pInf'
                                finished = True
                                continue
                        du_last_iter = iter
                    else:
                        du_infeas_count = 0

            # Display objective and residual data.
            output_line = self.format1 % (iter, cx + 0.5 * xQx, pResid,
                                          dResid, cResid, rgap, qNorm,
                                          rNorm)

            if kktResid <= tolerance:
                status = 'Optimal solution found'
                short_status = 'opt'
                finished = True
                continue

            if iter >= itermax:
                status = 'Maximum number of iterations reached'
                short_status = 'iter'
                finished = True
                continue

            # Record some quantities for display
            if ns > 0:
                mins = np.min(s)
                minz = np.min(z)
                maxs = np.max(s)
            else:
                mins = minz = maxs = 0

            # Compute augmented matrix and factorize it.

            factorized = False
            degenerate = False
            nb_bump = 0
            while not factorized and not degenerate:

                self.update_linear_system(s, z, regpr, regdu)
                self.log.debug('Factorizing')
                self.LBL.factorize(H)
                factorized = True

                # If the augmented matrix does not have full rank, bump up the
                # regularization parameters.
                if not self.LBL.isFullRank:
                    if self.verbose:
                        self.log.info('Primal-Dual Matrix Rank Deficient' + \
                                      '... bumping up reg parameters')

                    if regpr == 0. and regdu == 0.:
                        degenerate = True
                    else:
                        if regpr > 0:
                            regpr *= 100
                        if regdu > 0:
                            regdu *= 100
                        nb_bump += 1
                        degenerate = nb_bump > self.bump_max
                    factorized = False

            # Abandon if regularization is unsuccessful.
            if not self.LBL.isFullRank and degenerate:
                status = 'Unable to regularize sufficiently.'
                short_status = 'degn'
                finished = True
                continue

            if PredictorCorrector:
                # Use Mehrotra predictor-corrector method.
                # Compute affine-scaling step, i.e. with centering = 0.
                self.set_affine_scaling_rhs(rhs, pFeas, dFeas, s, z)

                (step, nres, neig) = self.solveSystem(rhs)

                # Recover dx and dz.
                dx, ds, dy, dz = self.get_affine_scaling_dxsyz(step, x, s, y, z)

                # Compute largest allowed primal and dual stepsizes.
                (alpha_p, ip) = self.maxStepLength(s, ds)
                (alpha_d, ip) = self.maxStepLength(z, dz)

                # Estimate duality gap after affine-scaling step.
                muAff = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns
                sigma = (muAff/mu)**3

                # Incorporate predictor information for corrector step.
                # Only update rhs[on:n]; the rest of the vector did not change.
                comp += ds*dz
                comp -= sigma * mu
                self.update_corrector_rhs(rhs, s, z, comp)
            else:
                # Use long-step method: Compute centering parameter.
                sigma = min(0.1, 100*mu)
                comp -= sigma * mu

                # Assemble rhs.
                self.update_long_step_rhs(rhs, pFeas, dFeas, comp, s)

            # Solve augmented system.
            (step, nres, neig) = self.solveSystem(rhs)

            # Recover step.
            dx, ds, dy, dz = self.get_dxsyz(step, x, s, y, z, comp)

            normds = norm2(ds) ; normdy = norm2(dy) ; normdx = norm2(dx)

            # Compute largest allowed primal and dual stepsizes.
            (alpha_p, ip) = self.maxStepLength(s, ds)
            (alpha_d, id) = self.maxStepLength(z, dz)

            # Compute fraction-to-the-boundary factor.
            tau = max(.9995, 1.0-mu)

            if PredictorCorrector:
                # Compute actual stepsize using Mehrotra's heuristic.
                mult = 0.1

                # ip=-1 if ds ≥ 0, and id=-1 if dz ≥ 0
                if (ip != -1 or id != -1) and ip != id:
                    mu_tmp = np.dot(s + alpha_p * ds, z + alpha_d * dz)/ns

                if ip != -1 and ip != id:
                    zip = z[ip] + alpha_d * dz[ip]
                    gamma_p = (mult*mu_tmp - s[ip]*zip)/(alpha_p*ds[ip]*zip)
                    alpha_p *= max(1-mult, gamma_p)

                if id != -1 and ip != id:
                    sid = s[id] + alpha_p * ds[id]
                    gamma_d = (mult*mu_tmp - z[id]*sid)/(alpha_d*dz[id]*sid)
                    alpha_d *= max(1-mult, gamma_d)

                if ip==id and ip != -1:
                    # There is a division by zero in Mehrotra's heuristic
                    # Fall back on classical rule.
                    alpha_p *= tau
                    alpha_d *= tau

            else:
                alpha_p *= tau
                alpha_d *= tau

            # Display data.
            output_line += self.format2 % (mu, alpha_p, alpha_d,
                                           nres, regpr, regdu, rho_q,
                                           del_r, mins, minz, maxs)
            self.log.info(output_line)

            # Update iterates and perturbation vectors.
            x += alpha_p * dx    # This also updates slack variables.
            y += alpha_d * dy
            z += alpha_d * dz
            q *= (1-alpha_p) ; q += alpha_p * dx
            r *= (1-alpha_d) ; r += alpha_d * dy
            qNorm = norm2(q) ; rNorm = norm2(r)
            if regpr > 0:
                rho_q = regpr * qNorm/(1+self.normc)
                rho_q_min = min(rho_q_min, rho_q)
            else:
                rho_q = 0.0
            if regdu > 0:
                del_r = regdu * rNorm/(1+self.normb)
                del_r_min = min(del_r_min, del_r)
            else:
                del_r = 0.0
            iter += 1

        solve_time = cputime() - setup_time

        self.log.info('-' * len(self.header))

        # Transfer final values to class members.
        self.x = x
        self.y = y
        self.z = z
        self.iter = iter
        self.pResid = pResid ; self.cResid = cResid ; self.dResid = dResid
        self.rgap = rgap
        self.kktResid = kktResid
        self.solve_time = solve_time
        self.status = status
        self.short_status = short_status

        # Unscale problem if applicable.
        if self.prob_scaled: self.unscale()

        # Recompute final objective value.
        self.obj_value = self.c0 + cx + 0.5 * xQx

        return
Exemple #38
0
    def solve(self, **kwargs):
        """
        Solve current problem with trust-funnel framework.

        :keywords:
            :ny: Enable Nocedal-Yuan backtracking linesearch.

        :returns:
            This method sets the following members of the instance:
            :f: Final objective value
            :optimal: Flag indicating whether normal stopping conditions were
                      attained
            :pResid: Final primal residual
            :dResid: Final dual residual
            :niter: Total number of iterations
            :tsolve: Solve time.

        """

        ny = kwargs.get("ny", True)
        reg = kwargs.get("reg", 0.0)
        # ny = False

        tsolve = cputime()

        # Set some shortcuts.
        nlp = self.nlp
        n = nlp.n
        m = nlp.m
        x = self.x
        f = self.f
        c = self.cons(x)
        y = nlp.pi0.copy()
        self.it = 0

        # Initialize some constants.
        kappa_n = 1.0e2  # Factor of pNorm in normal step TR radius.
        kappa_b = 0.99  # Fraction of TR to compute tangential step.
        kappa_delta = 0.1  # Progress factor to compute tangential step.

        # Trust-region parameters.
        eta_1 = 1.0e-5
        eta_2 = 0.95
        eta_3 = 0.5
        gamma_1 = 0.25
        gamma_3 = 2.5

        kappa_tx1 = 0.9  # Factor of theta_max in max acceptable infeasibility.
        kappa_tx2 = 0.5  # Convex combination factor of theta and thetaTrial.

        # Compute constraint violation.
        theta = 0.5 * np.dot(c, c)

        # Set initial funnel radius.
        kappa_ca = 1.0e3  # Max initial funnel radius.
        kappa_cr = 2.0  # Infeasibility tolerance factor.
        theta_max = max(kappa_ca, kappa_cr * theta)

        # Evaluate first-order derivatives.
        g = nlp.grad(x)
        J = self.jac(x)
        Jop = PysparseLinearOperator(J)

        # Initial radius for f- and c-iterations.
        Delta_f = max(self.Delta_f, 0.1 * np.linalg.norm(g))
        Delta_c = max(self.Delta_c, 0.1 * sqrt(2 * theta))

        # Reset initial multipliers to least-squares estimates by
        # approximately solving:
        #   [ I   J' ] [ w ]   [ -g ]
        #   [ J   0  ] [ y ] = [  0 ].
        # This is equivalent to solving
        #   minimize |g + J'y|.
        if m > 0:
            y, _, _ = self.lsq(Jop.T, -g, reg=reg)

        pNorm = cNorm = 0
        if m > 0:
            pNorm = np.linalg.norm(c)
            cNorm = np.linalg.norm(c, np.inf)
            grad_lag = g + Jop.T * y
        else:
            grad_lag = g.copy()
        dNorm = np.linalg.norm(grad_lag) / (1 + np.linalg.norm(y))

        # Display current info if requested.
        self.log.info(self.hdr)
        self.log.info(self.linefmt1 % (0, " ", " ", " ", " ", f, pNorm, dNorm, Delta_f, Delta_c, theta_max, 0))

        # Compute primal stopping tolerance.
        stop_p = max(self.atol, self.stop_p * pNorm)
        self.log.debug("pNorm = %8.2e, cNorm = %8.2e, dNorm = %8.e2" % (pNorm, cNorm, dNorm))

        optimal = (pNorm <= stop_p) and (dNorm <= self.stop_d)
        self.log.debug("optimal: %s" % repr(optimal))

        # Start of main iteration.
        while not optimal and (self.it < self.maxit):

            self.it += 1
            Delta = min(Delta_f, Delta_c)
            cgiter = 0

            # 1. Compute normal step as an (approximate) solution to
            #    minimize |c + J n|  subject to  |n| <= min(Delta_c, kN |c|).

            if self.it > 1 and pNorm <= stop_p and dNorm >= 1.0e4 * self.stop_d:

                self.log.debug("Setting nStep=0 b/c need to work on optimality")
                nStep = np.zeros(n)
                nStepNorm = 0.0
                n_end = "0"
                m_xpn = 0  # Model value at x+n.

            else:

                nStep_max = min(Delta_c, kappa_n * pNorm)
                nStep, nStepNorm, lsq_status = self.lsq(Jop, -c, radius=nStep_max, reg=reg)

                if lsq_status == "residual small":
                    n_end = "r"
                elif lsq_status == "trust-region boundary active":
                    n_end = "b"
                else:
                    n_end = "?"

                # Evaluate the model of the obective after the normal step.
                _Hv = self.hprod(x, y, nStep)  # H*nStep
                m_xpn = np.dot(g, nStep) + 0.5 * np.dot(nStep, _Hv)

            self.log.debug("Normal step norm = %8.2e" % nStepNorm)
            self.log.debug("Model value: %9.2e" % m_xpn)

            # 2. Compute tangential step if normal step is not too long.

            if nStepNorm <= kappa_b * Delta:

                # 2.1. Compute Lagrange multiplier estimates and dual residuals
                #      by minimizing |(g + H n) + J'y|

                if nStepNorm == 0.0:
                    gN = g  # Note: this is just a pointer ; g will not be modified below.
                else:
                    gN = g + _Hv

                y_new, y_norm, _ = self.lsq(Jop.T, -gN, reg=reg)
                r = gN + Jop.T * y_new

                # Here Nick does iterative refinement to improve r and y_new...

                # Compute dual optimality measure.
                residNorm = np.linalg.norm(r)
                norm_gN = np.linalg.norm(gN)
                pi = 0.0
                if residNorm > 0:
                    pi = abs(np.dot(gN, r)) / residNorm

                # 2.2. If the dual residuals are large, compute a suitable
                #      tangential step as a solution to:
                #      minimize    g't + 1/2 t' H t
                #      subject to  Jt = 0, |n+t| <= Delta.

                if pi > self.forcing(3, theta):

                    self.log.debug("Computing tStep...")
                    Delta_within = Delta - nStepNorm

                    Hop = SimpleLinearOperator(n, n, lambda v: self.hprod(x, y_new, v), symmetric=True)
                    PPCG = ProjectedCG(gN, Hop, A=J.matrix if m > 0 else None, radius=Delta_within, dreg=reg)
                    PPCG.Solve()
                    tStep = PPCG.step
                    tStepNorm = PPCG.stepNorm
                    cgiter = PPCG.iter

                    self.log.debug("|t| = %8.2e" % tStepNorm)

                    if PPCG.status == "residual small":
                        t_end = "r"
                    elif PPCG.onBoundary and not PPCG.infDescent:
                        t_end = "b"
                    elif PPCG.infDescent:
                        t_end = "-"
                    elif PPCG.status == "max iter":
                        t_end = ">"
                    else:
                        t_end = "?"

                    # Compute total step and model decrease.
                    step = nStep + tStep
                    stepNorm = np.linalg.norm(step)
                    _Hv = self.hprod(x, y, step)  # y or y_new?
                    m_xps = np.dot(g, step) + 0.5 * np.dot(step, _Hv)

                else:

                    self.log.debug("Setting tStep=0 b/c pi is sufficiently small")
                    tStepNorm = 0
                    t_end = "0"
                    step = nStep
                    stepNorm = nStepNorm
                    m_xps = m_xpn

                y = y_new

            else:

                # No need to compute a tangential step.
                self.log.debug("Setting tStep=0 b/c the normal step is too large")
                t_end = "0"
                y = np.zeros(m)
                tStepNorm = 0.0
                step = nStep
                stepNorm = nStepNorm
                m_xps = m_xpn

            self.log.debug("Model decrease = %9.2e" % m_xps)

            # Compute trial point and evaluate local data.
            xTrial = x + step
            fTrial = nlp.obj(xTrial)
            cTrial = self.cons(xTrial)
            thetaTrial = 0.5 * np.dot(cTrial, cTrial)
            delta_f = -m_xps  # Overall improvement in the model.
            delta_ft = m_xpn - m_xps  # Improvement due to tangential step.
            Jspc = c + Jop * step

            # Compute improvement in linearized feasibility.
            delta_feas = theta - 0.5 * np.dot(Jspc, Jspc)

            # Decide whether to consider the current iteration
            # an f- or a c-iteration.

            if (
                tStepNorm > 0
                and (delta_f >= self.forcing(2, theta))
                and delta_f >= kappa_delta * delta_ft
                and thetaTrial <= theta_max
            ):

                # Step 3. Consider that this is an f-iteration.
                it_type = "f"

                # Decide whether trial point is accepted.
                ratio = (f - fTrial) / delta_f

                self.log.debug("f-iter ratio = %9.2e" % ratio)

                if ratio >= eta_1:  # Successful step.
                    suc = "s"
                    x = xTrial
                    f = fTrial
                    c = cTrial
                    self.step = step.copy()
                    theta = thetaTrial

                    # Decide whether to update f-trust-region radius.
                    if ratio >= eta_2:
                        suc = "v"
                        Delta_f = min(max(Delta_f, gamma_3 * stepNorm), 1.0e10)

                    # Decide whether to update c-trust-region radius.
                    if thetaTrial < eta_3 * theta_max:
                        ns = nStepNorm if nStepNorm > 0 else Delta_c
                        Delta_c = min(max(Delta_c, gamma_3 * ns), 1.0e10)

                    self.log.debug("New Delta_f = %8.2e" % Delta_f)
                    self.log.debug("New Delta_c = %8.2e" % Delta_c)

                else:  # Unsuccessful step (ratio < eta_1).

                    attempt_SOC = True
                    suc = "u"

                    if attempt_SOC:

                        self.log.debug("  Attempting second-order correction")

                        # Attempt a second-order correction by solving
                        # minimize |cTrial + J n|  subject to  |n| <= Delta_c.
                        socStep, socStepNorm, socStatus = self.lsq(Jop, -cTrial, radius=Delta_c, reg=reg)

                        if socStatus != "trust-region boundary active":

                            # Consider SOC step as candidate step.
                            xSoc = xTrial + socStep
                            fSoc = nlp.obj(xSoc)
                            cSoc = self.cons(xSoc)
                            thetaSoc = 0.5 * np.dot(cSoc, cSoc)
                            ratio = (f - fSoc) / delta_f

                            # Decide whether to accept SOC step.
                            if ratio >= eta_1 and thetaSoc <= theta_max:
                                suc = "2"
                                x = xSoc
                                f = fSoc
                                c = cSoc
                                theta = thetaSoc
                                self.step = step + socStep
                            else:

                                # Backtracking linesearch a la Nocedal & Yuan.
                                # Abandon SOC step. Backtrack from x+step.
                                if ny:
                                    (x, f, alpha) = self.nyf(x, f, fTrial, g, step)
                                    # g = nlp.grad(x)
                                    c = self.cons(x)
                                    theta = 0.5 * np.dot(c, c)
                                    self.step = step + alpha * socStep
                                    Delta_f = min(alpha, 0.8) * stepNorm
                                    suc = "y"

                                else:
                                    Delta_f = gamma_1 * Delta_f

                        else:  # SOC step lies on boundary of trust region.
                            Delta_f = gamma_1 * Delta_f

                    else:  # SOC step not attempted.

                        # Backtracking linesearch a la Nocedal & Yuan.
                        if ny:
                            (x, f, alpha) = self.nyf(x, f, fTrial, g, step)
                            # g = nlp.grad(x)
                            c = self.cons(x)
                            theta = 0.5 * np.dot(c, c)
                            self.step = alpha * step
                            Delta_f = min(alpha, 0.8) * stepNorm
                            suc = "y"
                        else:
                            Delta_f = gamma_1 * Delta_f

            else:

                # Step 4. Consider that this is a c-iteration.
                it_type = "c"

                # Display information.
                self.log.debug("c-iteration because ")
                if tStepNorm == 0.0:
                    self.log.debug("|t|=0")
                if delta_f < self.forcing(2, theta):
                    self.log.debug("delta_f=%8.2e < forcing=%8.2e" % (delta_f, self.forcing(2, theta)))
                if delta_f < kappa_delta * delta_ft:
                    self.log.debug("delta_f=%8.2e < frac * delta_ft=%8.2e" % (delta_f, delta_ft))
                if thetaTrial > theta_max:
                    self.log.debug("thetaTrial=%8.2e > theta_max=%8.2e" % (thetaTrial, theta_max))

                # Step 4.1. Check trial point for acceptability.
                if delta_feas < 0:
                    self.log.debug(" !!! Warning: delta_feas is negative !!!")

                ratio = (theta - thetaTrial + 1.0e-16) / (delta_feas + 1.0e-16)
                self.log.debug("c-iter ratio = %9.2e" % ratio)

                if ratio >= eta_1:  # Successful step.
                    x = xTrial
                    f = fTrial
                    c = cTrial
                    self.step = step.copy()
                    suc = "s"

                    # Step 4.2. Update Delta_c.
                    if ratio >= eta_2:  # Very successful step.
                        ns = nStepNorm if nStepNorm > 0 else Delta_c
                        Delta_c = min(max(Delta_c, gamma_3 * ns), 1.0e10)
                        suc = "v"

                    # Step 4.3. Update maximum acceptable infeasibility.
                    theta_max = max(kappa_tx1 * theta_max, kappa_tx2 * theta + (1 - kappa_tx2) * thetaTrial)
                    theta = thetaTrial

                else:  # Unsuccessful step.

                    # Backtracking linesearch a la Nocedal & Yuan.
                    ns = nStepNorm if nStepNorm > 0 else Delta_c
                    if ny:
                        (x, c, theta, alpha) = self.nyc(x, theta, thetaTrial, c, Jop.T * c, step)
                        f = nlp.obj(x)
                        # g = nlp.grad(x)
                        self.step = alpha * step
                        Delta_c = min(alpha, 0.8) * ns
                        suc = "y"
                    else:
                        Delta_c = gamma_1 * ns  # Delta_c
                        suc = "u"

                self.log.debug("New Delta_c = %8.2e" % Delta_c)
                self.log.debug("New theta_max = %8.2e" % theta_max)

            # Step 5. Book keeping.
            if ratio >= eta_1 or ny:
                g = nlp.grad(x)
                J = self.jac(x)
                Jop = PysparseLinearOperator(J)
                self.post_iteration()

                pNorm = cNorm = 0
                if m > 0:
                    pNorm = np.linalg.norm(c)
                    cNorm = np.linalg.norm(c, np.inf)
                    grad_lag = g + Jop.T * y
                else:
                    grad_lag = g.copy()
                dNorm = np.linalg.norm(grad_lag) / (1 + np.linalg.norm(y))

            if self.it % 20 == 0:
                self.log.info(self.hdr)
            self.log.info(
                self.linefmt
                % (self.it, it_type, suc, n_end, t_end, f, pNorm, dNorm, Delta_f, Delta_c, theta_max, cgiter)
            )

            optimal = (pNorm <= stop_p) and (dNorm <= self.stop_d)
        # End while.

        self.tsolve = cputime() - tsolve

        if optimal:
            self.status = 0  # Successful solve.
            self.log.info("Found an optimal solution! Yeah!")
        else:
            self.status = 1  # Refine this in the future.

        self.x = x
        self.f = f
        self.optimal = optimal
        self.pResid = pNorm
        self.dResid = dNorm
        self.niter = self.it

        return
Exemple #39
0
    def Solve(self, **kwargs):

        nlp = self.nlp

        # Gather initial information.
        self.f      = self.nlp.obj(self.x)
        self.f0     = self.f
        self.g      = self.nlp.grad(self.x)  # Current  gradient
        self.g_old  = self.g                   # Previous gradient
        self.gnorm  = norms.norm2(self.g)
        self.g0     = self.gnorm

        # Reset initial trust-region radius.
        # self.TR.Delta = 0.1 * self.g0

        if self.inexact:
            cgtol = 1.0
        else:
            cgtol = -1.0
        stoptol = max(self.abstol, self.reltol * self.g0)
        step_status = None
        exitOptimal = exitIter = exitUser = False

        # Initialize non-monotonicity parameters.
        if not self.monotone:
            fMin = fRef = fCan = self.f0
            l = 0
            sigRef = sigCan = 0

        t = cputime()

        # Print out header and initial log.
        if self.iter % 20 == 0 and self.verbose:
            self.log.info(self.hline)
            self.log.info(self.header)
            self.log.info(self.hline)
            self.log.info(self.format0 % (self.iter, self.f,
                                             self.gnorm, '', '',
                                             self.TR.Delta, ''))

        while not (exitUser or exitOptimal or exitIter):

            self.iter += 1
            self.alpha = 1.0

            # Save current gradient
            if self.save_g:
                self.g_old = self.g.copy()

            # Iteratively minimize the quadratic model in the trust region
            # m(s) = <g, s> + 1/2 <s, Hs>
            # Note that m(s) does not include f(x): m(0) = 0.

            if self.inexact:
                cgtol = max(1.0e-6, min(0.5 * cgtol, sqrt(self.gnorm)))

            H = SimpleLinearOperator(nlp.n, nlp.n,
                                     lambda v: self.hprod(v),
                                     symmetric=True)

            self.solver = self.TrSolver(self.g, H)
            self.solver.Solve(prec=self.precon,
                              radius=self.TR.Delta,
                              reltol=cgtol,
                              #debug=True
                              )

            step = self.solver.step
            snorm = self.solver.stepNorm
            cgiter = self.solver.niter

            # Obtain model value at next candidate
            m = self.solver.m
            if m is None:
                m = numpy.dot(self.g, step) + 0.5*numpy.dot(step, H * step)

            self.total_cgiter += cgiter
            x_trial = self.x + step
            f_trial = nlp.obj(x_trial)

            rho  = self.TR.Rho(self.f, f_trial, m)

            if not self.monotone:
                rhoHis = (fRef - f_trial)/(sigRef - m)
                rho = max(rho, rhoHis)

            step_status = 'Rej'
            if rho >= self.TR.eta1:

                # Trust-region step is accepted.

                self.TR.UpdateRadius(rho, snorm)
                self.x = x_trial
                self.f = f_trial
                self.g = nlp.grad(self.x)
                self.gnorm = norms.norm2(self.g)
                step_status = 'Acc'

                # Update non-monotonicity parameters.
                if not self.monotone:
                    sigRef = sigRef - m
                    sigCan = sigCan - m
                    if f_trial < fMin:
                        fCan = f_trial
                        fMin = f_trial
                        sigCan = 0
                        l = 0
                    else:
                        l = l + 1

                    if f_trial > fCan:
                        fCan = f_trial
                        sigCan = 0

                    if l == self.nIterNonMono:
                        fRef = fCan
                        sigRef = sigCan

            else:

                # Trust-region step is rejected.

                if self.ny: # Backtracking linesearch following "Nocedal & Yuan"
                    slope = numpy.dot(self.g, step)
                    bk = 0
                    while bk < self.nbk and \
                            f_trial >= self.f + 1.0e-4 * self.alpha * slope:
                        bk = bk + 1
                        self.alpha /= 1.2
                        x_trial = self.x + self.alpha * step
                        f_trial = nlp.obj(x_trial)
                    self.x = x_trial
                    self.f = f_trial
                    self.g = nlp.grad(self.x)
                    self.gnorm = norms.norm2(self.g)
                    self.TR.Delta = self.alpha * snorm
                    step_status = 'N-Y'
                else:
                    self.TR.UpdateRadius(rho, snorm)

            self.step_status = step_status
            self.radii.append(self.TR.Delta)
            status = ''
            try:
                self.PostIteration()
            except UserExitRequest:
                status = 'usr'

            # Print out header, say, every 20 iterations
            if self.iter % 20 == 0 and self.verbose:
                self.log.info(self.hline)
                self.log.info(self.header)
                self.log.info(self.hline)

            if self.verbose:
                pstatus = step_status if step_status != 'Acc' else ''
                self.log.info(self.format % (self.iter, self.f,
                          self.gnorm, cgiter, rho,
                          self.TR.Delta, pstatus))

            exitOptimal = self.gnorm < stoptol
            exitIter    = self.iter > self.maxiter
            exitUser    = status == 'usr'

        self.tsolve = cputime() - t    # Solve time

        # Set final solver status.
        if status == 'usr':
            pass
        elif self.gnorm <= stoptol:
            status = 'opt'
        else: # self.iter > self.maxiter:
            status = 'itr'
        self.status = status
Exemple #40
0
nA = A.shape[0]
nC = C.shape[0]
K = spmatrix.ll_mat_sym(nA + nC, A.nnz + C.nnz + min(nA, nC))
K[:nA, :nA] = A
K[nA:, nA:] = C
K[nA:, nA:].scale(-1.0)
idx = np.arange(min(nA, nC), dtype=np.int)
K.put(1, nA + idx, idx)

# Create right-hand side rhs=K*e
e = np.ones(nA + nC)
rhs = np.empty(nA + nC)
K.matvec(e, rhs)

# Factorize and solve Kx = rhs, knowing K is sqd
t = cputime()
P = LBLContext(K, sqd=True)
t = cputime() - t
sys.stderr.write('Factorization time with sqd=True : %5.2fs   ' % t)
P.solve(rhs, get_resid=False)
sys.stderr.write('Error: %7.1e\n' % np.linalg.norm(P.x - e, ord=np.Inf))

# Do it all over again, pretending we don't know K is sqd
t = cputime()
P = LBLContext(K)
t = cputime() - t
sys.stderr.write('Factorization time with sqd=False: %5.2fs   ' % t)
P.solve(rhs, get_resid=False)
sys.stderr.write('Error: %7.1e\n' % np.linalg.norm(P.x - e, ord=np.Inf))

try:
Exemple #41
0
if options.n_dim is not None:
    n = options.n_dim

if options.delta_size is not None:
    delta = options.delta_size
# Set printing standards for arrays.
numpy.set_printoptions(precision=3, linewidth=70, threshold=10, edgeitems=2)

multiple_problems = len(args) > 1

#if not options.verbose:
    #log.info(hdr)
    #log.info('-'*len(hdr))
args = ['example']    
for probname in args:
    t_setup = cputime()
    #lsqp = DCT(n,m,delta)#partial_
    #n=2;m= 2;
    #lsqp= exampleliop(n,m)
    lsqp = partial_DCT(n,m,delta)
	
    t_setup = cputime() - t_setup

    # Pass problem to RegQP.
    regqp = Solver(lsqp,
                   verbose=options.verbose,
                   **opts_init)

    regqp.solve(PredictorCorrector=not options.longstep,
                check_infeasible=not options.assume_feasible,
                **opts_solve)
Exemple #42
0
    def Solve(self, **kwargs):

        nlp = self.nlp

        # Gather initial information.
        self.f = self.nlp.obj(self.x)
        self.f0 = self.f
        self.g = self.nlp.grad(self.x)  # Current  gradient
        self.g_old = self.g  # Previous gradient
        self.gnorm = norms.norm2(self.g)
        self.g0 = self.gnorm

        # Reset initial trust-region radius.
        # self.TR.Delta = 0.1 * self.g0

        if self.inexact:
            cgtol = 1.0
        else:
            cgtol = -1.0
        stoptol = max(self.abstol, self.reltol * self.g0)
        step_status = None
        exitOptimal = exitIter = exitUser = False

        # Initialize non-monotonicity parameters.
        if not self.monotone:
            fMin = fRef = fCan = self.f0
            l = 0
            sigRef = sigCan = 0

        t = cputime()

        # Print out header and initial log.
        if self.iter % 20 == 0 and self.verbose:
            self.log.info(self.hline)
            self.log.info(self.header)
            self.log.info(self.hline)
            self.log.info(
                self.format0 %
                (self.iter, self.f, self.gnorm, '', '', self.TR.Delta, ''))

        while not (exitUser or exitOptimal or exitIter):

            self.iter += 1
            self.alpha = 1.0

            # Save current gradient
            if self.save_g:
                self.g_old = self.g.copy()

            # Iteratively minimize the quadratic model in the trust region
            # m(s) = <g, s> + 1/2 <s, Hs>
            # Note that m(s) does not include f(x): m(0) = 0.

            if self.inexact:
                cgtol = max(1.0e-6, min(0.5 * cgtol, sqrt(self.gnorm)))

            H = SimpleLinearOperator(nlp.n,
                                     nlp.n,
                                     lambda v: self.hprod(v),
                                     symmetric=True)

            self.solver = self.TrSolver(self.g, H)
            self.solver.Solve(
                prec=self.precon,
                radius=self.TR.Delta,
                reltol=cgtol,
                #debug=True
            )

            step = self.solver.step
            snorm = self.solver.stepNorm
            cgiter = self.solver.niter

            # Obtain model value at next candidate
            m = self.solver.m
            if m is None:
                m = numpy.dot(self.g, step) + 0.5 * numpy.dot(step, H * step)

            self.total_cgiter += cgiter
            x_trial = self.x + step
            f_trial = nlp.obj(x_trial)

            rho = self.TR.Rho(self.f, f_trial, m)

            if not self.monotone:
                rhoHis = (fRef - f_trial) / (sigRef - m)
                rho = max(rho, rhoHis)

            step_status = 'Rej'
            if rho >= self.TR.eta1:

                # Trust-region step is accepted.

                self.TR.UpdateRadius(rho, snorm)
                self.x = x_trial
                self.f = f_trial
                self.g = nlp.grad(self.x)
                self.gnorm = norms.norm2(self.g)
                step_status = 'Acc'

                # Update non-monotonicity parameters.
                if not self.monotone:
                    sigRef = sigRef - m
                    sigCan = sigCan - m
                    if f_trial < fMin:
                        fCan = f_trial
                        fMin = f_trial
                        sigCan = 0
                        l = 0
                    else:
                        l = l + 1

                    if f_trial > fCan:
                        fCan = f_trial
                        sigCan = 0

                    if l == self.nIterNonMono:
                        fRef = fCan
                        sigRef = sigCan

            else:

                # Trust-region step is rejected.

                if self.ny:  # Backtracking linesearch following "Nocedal & Yuan"
                    slope = numpy.dot(self.g, step)
                    bk = 0
                    while bk < self.nbk and \
                            f_trial >= self.f + 1.0e-4 * self.alpha * slope:
                        bk = bk + 1
                        self.alpha /= 1.2
                        x_trial = self.x + self.alpha * step
                        f_trial = nlp.obj(x_trial)
                    self.x = x_trial
                    self.f = f_trial
                    self.g = nlp.grad(self.x)
                    self.gnorm = norms.norm2(self.g)
                    self.TR.Delta = self.alpha * snorm
                    step_status = 'N-Y'
                else:
                    self.TR.UpdateRadius(rho, snorm)

            self.step_status = step_status
            self.radii.append(self.TR.Delta)
            status = ''
            try:
                self.PostIteration()
            except UserExitRequest:
                status = 'usr'

            # Print out header, say, every 20 iterations
            if self.iter % 20 == 0 and self.verbose:
                self.log.info(self.hline)
                self.log.info(self.header)
                self.log.info(self.hline)

            if self.verbose:
                pstatus = step_status if step_status != 'Acc' else ''
                self.log.info(self.format %
                              (self.iter, self.f, self.gnorm, cgiter, rho,
                               self.TR.Delta, pstatus))

            exitOptimal = self.gnorm < stoptol
            exitIter = self.iter > self.maxiter
            exitUser = status == 'usr'

        self.tsolve = cputime() - t  # Solve time

        # Set final solver status.
        if status == 'usr':
            pass
        elif self.gnorm <= stoptol:
            status = 'opt'
        else:  # self.iter > self.maxiter:
            status = 'itr'
        self.status = status