def solve(self, quiet=False, return_status=False): """ Solves optimization program using current parameter values. """ # Check DCP if not self.is_dcp(): raise ValueError('Program is not DCP') # Create param-value map replace_map = {} for param in self.parameters: if np.isnan(param.value): raise ValueError('Invalid parameter value: NaN') else: replace_map[param] = param.value # Create new program new_p = cvxpy_program(self.action, re_eval(self.objective, replace_map), re_eval(self.constraints, replace_map), [], self.options, '') # Solve new program obj, valid = solve_prog(new_p, quiet) if return_status: return obj, valid else: return obj
def solve(self,quiet=False,return_status=False): """ Solves optimization program using current parameter values. """ # Check DCP if not self.is_dcp(): raise ValueError('Program is not DCP') # Create param-value map replace_map = {} for param in self.parameters: if np.isnan(param.value): raise ValueError('Invalid parameter value: NaN') else: replace_map[param] = param.value # Create new program new_p = cvxpy_program(self.action, re_eval(self.objective,replace_map), re_eval(self.constraints,replace_map), [],self.options,'') # Solve new program obj,valid = solve_prog(new_p,quiet) if return_status: return obj,valid else: 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 _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 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()
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)
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)
def _pm_expand(self,constr): """ Returns constraints that embed the program into the parent program. :param constr: cvxpy_constr of the form self(args), comparison-type, variable. """ # Get arguments args = constr.left.children # Create argument reference list args_ref = self._get_expanded_objects(self.formals) # Check number of arguments if len(args) != len(args_ref): raise TypeError("Invalid number of arguments") # Create map and new program replace_map = {} for k in range(0,len(args),1): replace_map[args_ref[k]] = args[k] new_p = cvxpy_program(self.action, re_eval(self.objective,replace_map), re_eval(self.constraints,replace_map), [],self.options,'') # Embed right = constr.right new_constr = [] if self.action == MINIMIZE: new_constr += [compare(new_p.objective,LESS_EQUALS,right)] else: new_constr += [compare(new_p.objective,GREATER_EQUALS,right)] new_constr += new_p.constraints # Return constraints return new_constr
def is_dcp(self,args=None): """ Determines if program is DCP-compliant. :param args: List of scalar arguments. """ # No arguments: Check body of program if args == None: if (self.action == MINIMIZE and not self.objective.is_convex()): return False elif (self.action == MAXIMIZE and not self.objective.is_concave()): return False else: return self.constraints.is_dcp() # Arguments given: Replace arguments and then check else: # Create reference list of arguments args_ref = self._get_expanded_objects(self.formals) # Check number of arguments if len(args) != len(args_ref): raise TypeError('Invalid number of arguments') # Create map and new program replace_map = {} for k in range(0,len(args),1): replace_map[args_ref[k]] = args[k] new_p = cvxpy_program(self.action, re_eval(self.objective,replace_map), re_eval(self.constraints,replace_map), [],self.options,'') # Check dcp on resulting program return new_p.is_dcp()
def _pm_expand(self, constr): """ Returns constraints that embed the program into the parent program. :param constr: cvxpy_constr of the form self(args), comparison-type, variable. """ # Get arguments args = constr.left.children # Create argument reference list args_ref = self._get_expanded_objects(self.formals) # Check number of arguments if len(args) != len(args_ref): raise TypeError("Invalid number of arguments") # Create map and new program replace_map = {} for k in range(0, len(args), 1): replace_map[args_ref[k]] = args[k] new_p = cvxpy_program(self.action, re_eval(self.objective, replace_map), re_eval(self.constraints, replace_map), [], self.options, '') # Embed right = constr.right new_constr = [] if self.action == MINIMIZE: new_constr += [compare(new_p.objective, LESS_EQUALS, right)] else: new_constr += [compare(new_p.objective, GREATER_EQUALS, right)] new_constr += new_p.constraints # Return constraints return new_constr
def is_dcp(self, args=None): """ Determines if program is DCP-compliant. :param args: List of scalar arguments. """ # No arguments: Check body of program if args == None: if (self.action == MINIMIZE and not self.objective.is_convex()): return False elif (self.action == MAXIMIZE and not self.objective.is_concave()): return False else: return self.constraints.is_dcp() # Arguments given: Replace arguments and then check else: # Create reference list of arguments args_ref = self._get_expanded_objects(self.formals) # Check number of arguments if len(args) != len(args_ref): raise TypeError('Invalid number of arguments') # Create map and new program replace_map = {} for k in range(0, len(args), 1): replace_map[args_ref[k]] = args[k] new_p = cvxpy_program(self.action, re_eval(self.objective, replace_map), re_eval(self.constraints, replace_map), [], self.options, '') # Check dcp on resulting program return new_p.is_dcp()
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)
def __call__(self,*args): """ Invokes program. :param args: List of arguments. """ # Get arguments reference args_ref = self._get_expanded_objects(self.formals) # Internal call if len(args) == 1 and type(args[0]) is list: # Get list of arguments args_list = args[0] # Check number of arguments if len(args_list) != len(args_ref): raise TypeError('Invalid number of arguments') # User call else: # Check number of arguments if len(args) != len(self.formals): raise TypeError('Invalid number of arguments') # Check syntax for i in range(0,len(args),1): if (np.isscalar(args[i]) or type(args[i]).__name__ in SCALAR_OBJS): if self.formals[i].shape != (1,1): raise ValueError('Invalid argument shape') elif (type(args[i]) is cvxpy_matrix or type(args[i]).__name__ in ARRAY_OBJS): if self.formals[i].shape != args[i].shape: raise ValueError('Invalid argument shape') else: raise TypeError('Invalid argument') # Expand arguments args_list = self._get_expanded_objects(args) # Verify values for i in range(0,len(args_ref)): if np.isscalar(args_list[i]) and np.isnan(args_list[i]): raise ValueError('Invalid argument value: NaN') # Upgrade scalars to objects args_upgraded = self._upgrade_scalars(args_list) # Verify replacements for i in range(0,len(args_ref)): if (len(args_upgraded[i].variables) != 0 and type(args_ref[i]) is not cvxpy_scalar_var): raise TypeError('Invalid replacement') # All numeric if all(list(map(lambda x: type(x) is cvxpy_obj,args_upgraded))): # Create map and new program replace_map = {} for i in range(0,len(args_ref),1): replace_map[args_ref[i]] = args_upgraded[i] new_p = cvxpy_program(self.action, re_eval(self.objective,replace_map), re_eval(self.constraints,replace_map), [],self.options,'') # Solve obj,valid = new_p.solve(quiet=True,return_status=True) if not valid: raise ValueError('Unable to compute value') else: return obj # Not all numeric else: # Return expression tree return cvxpy_tree(self,args_upgraded)
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)
def __call__(self, *args): """ Invokes program. :param args: List of arguments. """ # Get arguments reference args_ref = self._get_expanded_objects(self.formals) # Internal call if len(args) == 1 and type(args[0]) is list: # Get list of arguments args_list = args[0] # Check number of arguments if len(args_list) != len(args_ref): raise TypeError('Invalid number of arguments') # User call else: # Check number of arguments if len(args) != len(self.formals): raise TypeError('Invalid number of arguments') # Check syntax for i in range(0, len(args), 1): if (np.isscalar(args[i]) or type(args[i]).__name__ in SCALAR_OBJS): if self.formals[i].shape != (1, 1): raise ValueError('Invalid argument shape') elif (type(args[i]) is cvxpy_matrix or type(args[i]).__name__ in ARRAY_OBJS): if self.formals[i].shape != args[i].shape: raise ValueError('Invalid argument shape') else: raise TypeError('Invalid argument') # Expand arguments args_list = self._get_expanded_objects(args) # Verify values for i in range(0, len(args_ref)): if np.isscalar(args_list[i]) and np.isnan(args_list[i]): raise ValueError('Invalid argument value: NaN') # Upgrade scalars to objects args_upgraded = self._upgrade_scalars(args_list) # Verify replacements for i in range(0, len(args_ref)): if (len(args_upgraded[i].variables) != 0 and type(args_ref[i]) is not cvxpy_scalar_var): raise TypeError('Invalid replacement') # All numeric if all(list(map(lambda x: type(x) is cvxpy_obj, args_upgraded))): # Create map and new program replace_map = {} for i in range(0, len(args_ref), 1): replace_map[args_ref[i]] = args_upgraded[i] new_p = cvxpy_program(self.action, re_eval(self.objective, replace_map), re_eval(self.constraints, replace_map), [], self.options, '') # Solve obj, valid = new_p.solve(quiet=True, return_status=True) if not valid: raise ValueError('Unable to compute value') else: return obj # Not all numeric else: # Return expression tree return cvxpy_tree(self, args_upgraded)