Beispiel #1
0
    def _solve_isolating(self,*args):
        """
        Description
        -----------
        Isolate parameters and then solve program.
        Used by linearize method to obtain subgradient.
        Arguments must be numbers. A very important 
        point is that solve_isolating introduces
        equality constraints and places them at the
        beginning of the constraint list. This is 
        later used to construct the subgradient.
        """

        # Process input
        if(len(args) != 0 and type(args[0]) is list):
            args = args[0]
        
        # Create argument list
        arg_list = []
        for arg in args:
            if(np.isscalar(arg)):
                arg_list += [arg]
            elif(type(arg) is cvxpy_matrix):
                (m,n) = arg.shape
                for i in range(0,m,1):
                    for j in range(0,n,1):
                        arg_list += [arg[i,j]]
            else:
                raise ValueError('Arguments must be numeric')

        # Check number of arguments
        if(len(arg_list) != len(self.params)):
            raise ValueError('Invalid number of arguments')

        # Isolate parameters
        p1_map = {}
        new_constr = cvxpy_list([])
        for p in self.params:
            v = var('v_'+p.name)
            p1_map[p] = v
            new_constr += cvxpy_list([equal(v,p)])
        new_p1 = prog((self.action,re_eval(self.obj,p1_map)),
                      new_constr+re_eval(self.constr,p1_map),
                      self.params, self.options,self.name)

        # Substitute parameters with arguments
        p2_map = {}
        for k in range(0,len(arg_list),1):
            p2_map[new_p1.params[k]] = arg_list[k]
        new_p2 = prog((new_p1.action,re_eval(new_p1.obj,p2_map)),
                      re_eval(new_p1.constr,p2_map),
                      [],new_p1.options,new_p1.name)

        # Solve program
        obj,lagrange_mul_eq = solve_prog(new_p2)
        self.lagrange_mul_eq = lagrange_mul_eq
        return obj
    def _solve_isolating(self, *args):
        """
        Description
        -----------
        Isolate parameters and then solve program.
        Used by linearize method to obtain subgradient.
        Arguments must be numbers. A very important 
        point is that solve_isolating introduces
        equality constraints and places them at the
        beginning of the constraint list. This is 
        later used to construct the subgradient.
        """

        # Process input
        if (len(args) != 0 and type(args[0]) is list):
            args = args[0]

        # Create argument list
        arg_list = []
        for arg in args:
            if (np.isscalar(arg)):
                arg_list += [arg]
            elif (type(arg) is cvxpy_matrix):
                (m, n) = arg.shape
                for i in range(0, m, 1):
                    for j in range(0, n, 1):
                        arg_list += [arg[i, j]]
            else:
                raise ValueError('Arguments must be numeric')

        # Check number of arguments
        if (len(arg_list) != len(self.params)):
            raise ValueError('Invalid number of arguments')

        # Isolate parameters
        p1_map = {}
        new_constr = cvxpy_list([])
        for p in self.params:
            v = var('v_' + p.name)
            p1_map[p] = v
            new_constr += cvxpy_list([equal(v, p)])
        new_p1 = prog((self.action, re_eval(self.obj, p1_map)),
                      new_constr + re_eval(self.constr, p1_map), self.params,
                      self.options, self.name)

        # Substitute parameters with arguments
        p2_map = {}
        for k in range(0, len(arg_list), 1):
            p2_map[new_p1.params[k]] = arg_list[k]
        new_p2 = prog((new_p1.action, re_eval(new_p1.obj, p2_map)),
                      re_eval(new_p1.constr, p2_map), [], new_p1.options,
                      new_p1.name)

        # Solve program
        obj, lagrange_mul_eq = solve_prog(new_p2)
        self.lagrange_mul_eq = lagrange_mul_eq
        return obj
 def _get_equivalent(self):
     """
     Description
     -----------
     Construct a new program by applying the
     transform algorithm to the constraints.
     None: It is assumed that the program 
     has been previously expanded.
     """
     return prog((self.action, self.obj), transform(self.constr), [],
                 self.options)
Beispiel #4
0
 def _get_equivalent(self):
     """
     Description
     -----------
     Construct a new program by applying the
     transform algorithm to the constraints.
     None: It is assumed that the program 
     has been previously expanded.
     """
     return prog((self.action,self.obj),
                 transform(self.constr),[],self.options)
Beispiel #5
0
 def _get_expanded(self):
     """
     Description
     -----------
     Construct a new program by applying the
     expansion algorithm to the objective
     and constraints.
     """
     new_obj,new_eq = expand(self.obj)
     more_constr = expand(self.constr)
     return prog((self.action,new_obj),
                 more_constr+new_eq,[],self.options)
 def _get_cvx_relaxation(self):
     """
     Description
     -----------
     Construct a new program by choosing
     the convex constraints and ignoring
     the other ones.
     Note: It is assumed that the program
     has been expanded and transformed
     previously.
     """
     return prog((self.action, self.obj), self.constr._get_convex(), [],
                 self.options)
Beispiel #7
0
 def _get_cvx_relaxation(self):
     """
     Description
     -----------
     Construct a new program by choosing
     the convex constraints and ignoring
     the other ones.
     Note: It is assumed that the program
     has been expanded and transformed
     previously.
     """
     return prog((self.action,self.obj),
                 self.constr._get_convex(),[],self.options)
Beispiel #8
0
    def is_dcp(self,args=None):
        """
        Description
        -----------
        Checks if the program follows DCP rules.
        If args is None, the check is done on the
        body of the program. If args is a list of
        arguments, the parameters are replaced
        with the arguments and the check is done
        on the resulting program. This function
        is called with a list of arguments when 
        cvxpy_tree.is_dcp is executed.
        
        Arguments
        ---------
        args: List of arguments
        """

        # No arguments: Check body of program
        if(args == None):
            if(self.action == MINIMIZE and
               not self.obj.is_convex()):
                return False
            elif(self.action == MAXIMIZE and
                 not self.obj.is_concave()):
                return False
            else:
                return self.constr.is_dcp()

        # Arguments given: Replace parameters and then check
        else:

            # Check if some argument has parameters
            if(len(cvxpy_list(args).get_params()) != 0):
                return False

            # Create a param-arg map
            p_map = {}
            for k in range(0,len(args),1):
                p_map[self.params[k]] = args[k]

            # Re-evaluate
            new_p = prog((self.action,re_eval(self.obj,p_map)),
                         re_eval(self.constr,p_map))

            # Check dcp on resulting program
            return new_p.is_dcp()
    def is_dcp(self, args=None):
        """
        Description
        -----------
        Checks if the program follows DCP rules.
        If args is None, the check is done on the
        body of the program. If args is a list of
        arguments, the parameters are replaced
        with the arguments and the check is done
        on the resulting program. This function
        is called with a list of arguments when 
        cvxpy_tree.is_dcp is executed.
        
        Arguments
        ---------
        args: List of arguments
        """

        # No arguments: Check body of program
        if (args == None):
            if (self.action == MINIMIZE and not self.obj.is_convex()):
                return False
            elif (self.action == MAXIMIZE and not self.obj.is_concave()):
                return False
            else:
                return self.constr.is_dcp()

        # Arguments given: Replace parameters and then check
        else:

            # Check if some argument has parameters
            if (len(cvxpy_list(args).get_params()) != 0):
                return False

            # Create a param-arg map
            p_map = {}
            for k in range(0, len(args), 1):
                p_map[self.params[k]] = args[k]

            # Re-evaluate
            new_p = prog((self.action, re_eval(self.obj, p_map)),
                         re_eval(self.constr, p_map))

            # Check dcp on resulting program
            return new_p.is_dcp()
Beispiel #10
0
    def _pm_expand(self,constr):
        """
        Description
        -----------
        Given the constraint, which must be in the form
        self(args) operator variable, the parameters
        are replaced with arguments and then the 
        partial minimization description of the program
        is merged with the constraint.

        Argument
        --------
        constr: cvxpy_constr of the form self(args) 
        operator variable.
        """

        # Get arguments
        args = constr.left.children

        # Create arg-param map by position
        p_map = {}
        for k in range(0,len(args),1):
            p_map[self.params[k]] = args[k]

        # Create new program
        new_p = prog((self.action,re_eval(self.obj,p_map)),
                     re_eval(self.constr,p_map),[],
                     self.options,self.name)

        # Expand partial minimization
        right = constr.right
        new_constr = []
        if(self.curvature == CONVEX):
            new_constr += [less(new_p.obj,right)]
        else:
            new_constr += [greater(new_p.obj,right)]
        new_constr += new_p.constr
        
        # Return constraints
        return cvxpy_list(new_constr)
Beispiel #11
0
    def __call__(self, *args):
        """
        Description
        -----------
        Call program with specified arguments.
        Parameters are substituted with arguments.
        If all arguments are numeric, the resulting
        optimization program is solved. If some
        arguments are object, a tree is returned.
        
        Arguments
        ---------
        args: List of arguments.
        (Can be numbers or objects)
        """

        # Process input
        if (len(args) != 0 and type(args[0]) is list):
            args = args[0]

        # Create argument list
        arg_list = []
        for arg in args:
            if (np.isscalar(arg) or type(arg).__name__ in SCALAR_OBJS):
                arg_list += [arg]
            elif (type(arg) is cvxpy_matrix
                  or type(arg).__name__ in ARRAY_OBJS):
                (m, n) = arg.shape
                for i in range(0, m, 1):
                    for j in range(0, n, 1):
                        arg_list += [arg[i, j]]
            else:
                raise ValueError('Invalid argument type')

        # Check number of arguments
        if (len(arg_list) != len(self.params)):
            raise ValueError('Invalid argument syntax')

        # Solve if numeric
        if (len(arg_list) == 0
                or reduce(lambda x, y: x and y,
                          map(lambda x: np.isscalar(x), arg_list))):

            # Substitute parameters with arguments
            p1_map = {}
            for k in range(0, len(arg_list), 1):
                p1_map[self.params[k]] = arg_list[k]
            new_p = prog((self.action, re_eval(self.obj, p1_map)),
                         re_eval(self.constr, p1_map), [], self.options,
                         self.name)

            # Solve program
            obj, lagrange_mul_eq = solve_prog(new_p)
            return obj

        # Upgrade numbers to objects
        for i in range(0, len(arg_list), 1):
            if (np.isscalar(arg_list[i])):
                arg_list[i] = cvxpy_obj(CONSTANT, arg_list[i],
                                        str(arg_list[i]))

        # Return tree
        return cvxpy_tree(self, arg_list)
Beispiel #12
0
def scp(p,bad_constr,sol):
    """
    Description: Sequential convex programming
    algorithm. Solve program p and try to 
    enforce equality on the bad constraints.
    Argument p: cvxpy_program is assumed to be in
    expanded and equivalent format.
    Argument bad_constr: List of nonconvex
    constraints, also in expanded and equivalent
    format.
    Argument sol: Solution of relaxation.
    """

    # Quit if program is linear
    if(len(bad_constr) == 0):
        if(not p.options['quiet']):
            print 'Tightening not needed'
        return True

    # Get parameters
    tight_tol = p.options['SCP_ALG']['tight tol']
    starting_lambda = p.options['SCP_ALG']['starting lambda'] 
    max_scp_iter = p.options['SCP_ALG']['max scp iter']
    lambda_multiplier = p.options['SCP_ALG']['lambda multiplier']
    max_lambda = p.options['SCP_ALG']['max lambda']
    top_residual = p.options['SCP_ALG']['top residual']

    # Construct slacks
    slacks = [abs(c.left-c.right) for c in bad_constr]

    # Print header
    if(not p.options['quiet']):
        print 'Iter\t:',
        print 'Max Slack\t:',
        print 'Objective\t:',
        print 'Solver Status\t:',
        print 'Pres\t\t:',
        print 'Dres\t\t:',
        print 'Lambda Max/Min'

    # SCP Loop
    lam = starting_lambda*np.ones(len(slacks))
    for i in range(0,max_scp_iter,1):

        # Calculate max slack
        max_slack = max(map(lambda x:x.get_value(), slacks))

        # Quit if status is primal infeasible
        if(sol['status'] == 'primal infeasible'):
            if(not p.options['quiet']):
                print 'Unable to tighten: Problem became infeasible'
            return False

        # Check if dual infeasible
        if(sol['status'] == 'dual infeasible'):
            sol['status'] = 'dual inf'
            sol['primal infeasibility'] = np.NaN
            sol['dual infeasibility'] = np.NaN

        # Print values
        if(not p.options['quiet']):
            print '%d\t:' %i,
            print '%.3e\t:' %max_slack,
            print '%.3e\t:' %p.obj.get_value(),
            print '   '+sol['status']+'\t:',
            if(sol['primal infeasibility'] is not np.NaN):
                print '%.3e\t:' %sol['primal infeasibility'],
            else:
                print '%.3e\t\t:' %sol['primal infeasibility'],
            if(sol['dual infeasibility'] is not np.NaN):
                print '%.3e\t:' %sol['dual infeasibility'],
            else:
                print '%.3e\t\t:' %sol['dual infeasibility'],
            print '(%.1e,%.1e)' %(np.max(lam),np.min(lam))

        # Quit if max slack is small
        if(max_slack < tight_tol and
           sol['status'] == 'optimal'):
            if(not p.options['quiet']):
                print 'Tightening successful'
            return True

        # Quit if residual is too large
        if(sol['primal infeasibility'] >= top_residual or
           sol['dual infeasibility'] >= top_residual):
            if(not p.options['quiet']):
                print 'Unable to tighten: Residuals are too large'
            return False

        # Linearize slacks
        linear_slacks = []
        for c in bad_constr:
            fn = c.left.item
            args = c.left.children
            right = c.right
            line = fn._linearize(args,right)
            linear_slacks += [line]

        # Add linearized slacks to objective
        sum_lin_slacks = 0.0
        for j in range(0,len(slacks),1):
            sum_lin_slacks += lam[j]*linear_slacks[j]
        if(p.action == MINIMIZE):
            new_obj = p.obj + sum_lin_slacks
        else:
            new_obj = p.obj - sum_lin_slacks
        new_t0, obj_constr = expand(new_obj)    
        new_p = prog((p.action,new_t0), obj_constr+p.constr,[],p.options)

        # Solve new problem
        sol = solve_convex(new_p,'scp')

        # Update lambdas
        for j in range(0,len(slacks),1):
            if(slacks[j].get_value() >= tight_tol):
                if(lam[j] < max_lambda):
                    lam[j] = lam[j]*lambda_multiplier

    # Maxiters reached
    if(not p.options['quiet']):
        print 'Unable to tighten: Maximum iterations reached'
    if(sol['status'] == 'optimal'):
        return True
    else:
        return False
Beispiel #13
0
    def __call__(self,*args):
        """
        Description
        -----------
        Call program with specified arguments.
        Parameters are substituted with arguments.
        If all arguments are numeric, the resulting
        optimization program is solved. If some
        arguments are object, a tree is returned.
        
        Arguments
        ---------
        args: List of arguments.
        (Can be numbers or objects)
        """

        # Process input
        if(len(args) != 0 and type(args[0]) is list):
            args = args[0]
        
        # Create argument list
        arg_list = []
        for arg in args:
            if(np.isscalar(arg) or 
               type(arg).__name__ in SCALAR_OBJS):
                arg_list += [arg]
            elif(type(arg) is cvxpy_matrix or 
                 type(arg).__name__ in ARRAY_OBJS):
                (m,n) = arg.shape
                for i in range(0,m,1):
                    for j in range(0,n,1):
                        arg_list += [arg[i,j]]
            else:
                raise ValueError('Invalid argument type')

        # Check number of arguments
        if(len(arg_list) != len(self.params)):
            raise ValueError('Invalid argument syntax')
    
        # Solve if numeric
        if(len(arg_list) == 0 or
           reduce(lambda x,y: x and y,
                  map(lambda x: np.isscalar(x),arg_list))):

            # Substitute parameters with arguments
            p1_map = {}
            for k in range(0,len(arg_list),1):
                p1_map[self.params[k]] = arg_list[k]
            new_p = prog((self.action,re_eval(self.obj,p1_map)),
                         re_eval(self.constr,p1_map),[],
                         self.options,self.name)
            
            # Solve program
            obj,lagrange_mul_eq = solve_prog(new_p)
            return obj        

        # Upgrade numbers to objects
        for i in range(0,len(arg_list),1):
            if(np.isscalar(arg_list[i])):
                arg_list[i] = cvxpy_obj(CONSTANT,
                                        arg_list[i],
                                        str(arg_list[i]))

        # Return tree
        return cvxpy_tree(self,arg_list)