def pm_expand(constr_list): """ Description ----------- Expands functions which are implemented using partial minimization descriptions. constr_list: cvxpy_list of constraints. Arguments --------- constr_list: cvxpy_list of constraints. """ new_list = cvxpy_list([]) for c in constr_list: if (c.left.type == TREE and c.left.item.type == FUNCTION and c.left.item.expansion_type == PM): new_constr = transform(expand(c.left.item._pm_expand(c))) new_constr = pm_expand(new_constr._get_convex()) new_list += new_constr elif (c.left.type == EXPRESSION and c.right.type == SET and c.right.expansion_type == PM): new_constr = transform(expand(c.right._pm_expand(c))) new_constr = pm_expand(new_constr._get_convex()) new_list += new_constr else: new_list += cvxpy_list([c]) # Return new list return new_list
def pm_expand(constr_list): """ Description ----------- Expands functions which are implemented using partial minimization descriptions. constr_list: cvxpy_list of constraints. Arguments --------- constr_list: cvxpy_list of constraints. """ new_list = cvxpy_list([]) for c in constr_list: if(c.left.type == TREE and c.left.item.type == FUNCTION and c.left.item.expansion_type == PM): new_constr = transform(expand(c.left.item._pm_expand(c))) new_constr = pm_expand(new_constr._get_convex()) new_list += new_constr elif(c.left.type == EXPRESSION and c.right.type == SET and c.right.expansion_type == PM): new_constr = transform(expand(c.right._pm_expand(c))) new_constr = pm_expand(new_constr._get_convex()) new_list += new_constr else: new_list += cvxpy_list([c]) # Return new list return new_list
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 _pm_expand(self,constr): # Get objects v = constr.left v = vstack([v[i,0] for i in range(0,v.shape[0])]) n = v.shape[0]-1 x = vstack([v[0:n,0]]) y = v[n,0] z = variable() # One element if n==1: return cvxpy_list([greater_equals(x,0), greater_equals(x,y)]) # Get power of 2 size m = 0 while np.log2(n+m) % 1 != 0: m += 1 # Copy elements of x on a list and restrict them constr_list = [] el_list = [] for i in range(0,n,1): el_list+=[x[i,0]] if (not np.isscalar(x[i,0]) and type(x[i,0]) is not cvxpy_obj): constr_list += [greater_equals(x[i,0],0)] # Construct expansion for i in range(0,m,1): el_list += [z] while len(el_list) > 2: new_list = [] for i in range(0,int(len(el_list)/2)): x1 = el_list[2*i] x2 = el_list[2*i+1] w = variable() constr_list += [belongs(vstack((hstack((x1,w)), hstack((w,x2)))), semidefinite_cone)] new_list += [w] el_list = new_list x1 = el_list[0] x2 = el_list[1] constr_list += [belongs(vstack((hstack((x1,z)), hstack((z,x2)))), semidefinite_cone)] constr_list += [greater_equals(z,0),greater_equals(z,y)] return cvxpy_list(constr_list)
def _pm_expand(self, constr): # Get objects v = constr.left v = vstack([v[i, 0] for i in range(0, v.shape[0])]) n = v.shape[0] - 1 x = vstack([v[0:n, 0]]) y = v[n, 0] z = variable() # One element if n == 1: return cvxpy_list([greater_equals(x, 0), greater_equals(x, y)]) # Get power of 2 size m = 0 while np.log2(n + m) % 1 != 0: m += 1 # Copy elements of x on a list and restrict them constr_list = [] el_list = [] for i in range(0, n, 1): el_list += [x[i, 0]] if (not np.isscalar(x[i, 0]) and type(x[i, 0]) is not cvxpy_obj): constr_list += [greater_equals(x[i, 0], 0)] # Construct expansion for i in range(0, m, 1): el_list += [z] while len(el_list) > 2: new_list = [] for i in range(0, int(len(el_list) / 2)): x1 = el_list[2 * i] x2 = el_list[2 * i + 1] w = variable() constr_list += [ belongs(vstack((hstack((x1, w)), hstack((w, x2)))), semidefinite_cone) ] new_list += [w] el_list = new_list x1 = el_list[0] x2 = el_list[1] constr_list += [ belongs(vstack((hstack((x1, z)), hstack((z, x2)))), semidefinite_cone) ] constr_list += [greater_equals(z, 0), greater_equals(z, y)] return cvxpy_list(constr_list)
def __init__(self, action, obj, constr, params, opt, name): # Function attributes self.name = name self.type = FUNCTION self.atom = False self.expansion_type = PM if (action == MINIMIZE): self.curvature = CONVEX else: self.curvature = CONCAVE # Program attributes self.lagrange_mul_eq = None self.action = action self.obj = obj self.constr = cvxpy_list(constr) if (opt != None): self.options = opt else: self.options = { 'SCP_ALG': SCP_ALG_OPT.copy(), 'SCP_SOL': SCP_SOLVER_OPT.copy(), 'REL_SOL': RELAX_SOLVER_OPT.copy(), 'show steps': False, 'quiet': True } # Store parameters param_list = [] for param in params: if (type(param) is cvxpy_scalar_param): param_list += [param] elif (type(param) is cvxpy_param): (m, n) = param.shape for i in range(0, m, 1): for j in range(0, n, 1): param_list += [param[i, j]] else: raise ValueError('Invalid parameter') self.params = cvxpy_list(param_list) # Verify parameters exp_params = self.get_params() if (len(exp_params) != len(self.params)): raise ValueError('Parameters do not match') for x in exp_params: if (x not in self.params): raise ValueError('Parameters do not match')
def get_params(self): l = cvxpy_list([]) for i in range(0,self.shape[0],1): for j in range(0,self.shape[1],1): if(not np.isscalar(self[i,j])): l += self[i,j].get_params() return l
def __init__(self,object_type,value,name): """ Class constructor. :param object_type: Keyword (See cvxpy.def). :param value: Number. :param name: String. """ self.type = object_type self.value = value self.name = name self.shape = (1,1) self.variables = cvxpy_list() self.parameters = cvxpy_list() self.T = self
def call_solver(p, quiet): """ Calls solver. :param p: Convex cvxpy_program. Assumed to be expanded. :param quiet: Boolean. """ # Set printing format for cvxopt sparse matrices opt.spmatrix_str = opt.printing.spmatrix_str_triplet # Expand objects defined via partial minimization constr_list = cvxpy_list(pm_expand(p.constraints)) # Get variables variables = constr_list.variables variables.sort() # Count variables n = len(variables) # Create variable-index map var_to_index = {} for i in range(0, n, 1): var_to_index[variables[i]] = i # Construct objective vector c = construct_c(p.objective, var_to_index, n, p.action) # Construct Ax == b A, b = construct_Ab(constr_list._get_eq(), var_to_index, n) # Construct Gx <= h G, h, dim_l, dim_q, dim_s = construct_Gh(constr_list._get_ineq_in(), var_to_index, n) # Construct F F = construct_F(constr_list._get_ineq_in(), var_to_index, n) # Call cvxopt solvers.options['maxiters'] = p.options['maxiters'] solvers.options['abstol'] = p.options['abstol'] solvers.options['reltol'] = p.options['reltol'] solvers.options['feastol'] = p.options['feastol'] solvers.options['show_progress'] = not quiet dims = {'l': dim_l, 'q': dim_q, 's': dim_s} if F is None: r = solvers.conelp(c, G, h, dims, A, b) else: r = solvers.cpl(c, F, G, h, dims, A, b) # Store numerical values if r['status'] != PRIMAL_INFEASIBLE: for v in variables: v.value = r['x'][var_to_index[v]] # Return result return r
def call_solver(p,quiet): """ Calls solver. :param p: Convex cvxpy_program. Assumed to be expanded. :param quiet: Boolean. """ # Set printing format for cvxopt sparse matrices opt.spmatrix_str = opt.printing.spmatrix_str_triplet # Expand objects defined via partial minimization constr_list = cvxpy_list(pm_expand(p.constraints)) # Get variables variables = constr_list.variables variables.sort() # Count variables n = len(variables) # Create variable-index map var_to_index = {} for i in range(0,n,1): var_to_index[variables[i]] = i # Construct objective vector c = construct_c(p.objective,var_to_index,n,p.action) # Construct Ax == b A,b = construct_Ab(constr_list._get_eq(),var_to_index,n) # Construct Gx <= h G,h,dim_l,dim_q,dim_s = construct_Gh(constr_list._get_ineq_in(), var_to_index,n) # Construct F F = construct_F(constr_list._get_ineq_in(),var_to_index,n) # Call cvxopt solvers.options['maxiters'] = p.options['maxiters'] solvers.options['abstol'] = p.options['abstol'] solvers.options['reltol'] = p.options['reltol'] solvers.options['feastol'] = p.options['feastol'] solvers.options['show_progress'] = not quiet dims = {'l':dim_l, 'q':dim_q, 's':dim_s} if F is None: r = solvers.conelp(c,G,h,dims,A,b) else: r = solvers.cpl(c,F,G,h,dims,A,b) # Store numerical values if r['status'] != PRIMAL_INFEASIBLE: for v in variables: v.value = r['x'][var_to_index[v]] # Return result return r
def _dom_constr(self, args): """ Description ----------- Dummy method to make the program appear as a function. """ return cvxpy_list([])
def _dom_constr(self,args): """ Description ----------- Dummy method to make the program appear as a function. """ return cvxpy_list([])
def __init__(self,action,obj,constr,params,opt,name): # Function attributes self.name = name self.type = FUNCTION self.atom = False self.expansion_type = PM if(action == MINIMIZE): self.curvature = CONVEX else: self.curvature = CONCAVE # Program attributes self.lagrange_mul_eq = None self.action = action self.obj = obj self.constr = cvxpy_list(constr) if(opt != None): self.options = opt else: self.options = {'SCP_ALG':SCP_ALG_OPT.copy(), 'SCP_SOL':SCP_SOLVER_OPT.copy(), 'REL_SOL':RELAX_SOLVER_OPT.copy(), 'show steps':False,'quiet': True} # Store parameters param_list = [] for param in params: if(type(param) is cvxpy_scalar_param): param_list += [param] elif(type(param) is cvxpy_param): (m,n) = param.shape for i in range(0,m,1): for j in range(0,n,1): param_list += [param[i,j]] else: raise ValueError('Invalid parameter') self.params = cvxpy_list(param_list) # Verify parameters exp_params = self.get_params() if(len(exp_params) != len(self.params)): raise ValueError('Parameters do not match') for x in exp_params: if(x not in self.params): raise ValueError('Parameters do not match')
def __getattribute__(self, name): # Transpose if name == 'T': new_ar = cvxpy_sparray(self.shape[1], self.shape[0]) for i in range(0, self.shape[0], 1): rowi_indeces = self.rows[i] rowi_values = self.data[i] for k in range(0, len(rowi_indeces)): new_ar[rowi_indeces[k], i] = rowi_values[k] return new_ar # Variables elif name == 'variables': l = [] for i in range(0, self.shape[0], 1): for obj in self.data[i]: if not np.isscalar(obj): l += obj.variables return cvxpy_list(set(l)) # Parameters elif name == 'parameters': l = [] for i in range(0, self.shape[0], 1): for obj in self.data[i]: if not np.isscalar(obj): l += obj.parameters return cvxpy_list(set(l)) # Value elif name == 'value': mat = cvxpy_spmatrix((self.shape[0], self.shape[1]), np.float64) for i in range(0, self.shape[0], 1): rowi_indeces = self.rows[i] rowi_values = self.data[i] for k in range(0, len(rowi_indeces)): if np.isscalar(rowi_values[k]): mat[i, rowi_indeces[k]] = rowi_values[k] else: mat[i, rowi_indeces[k]] = rowi_values[k].value return mat # Other attribute else: return object.__getattribute__(self, name)
def __getattribute__(self,name): # Transpose if name == 'T': new_ar = cvxpy_sparray(self.shape[1],self.shape[0]) for i in range(0,self.shape[0],1): rowi_indeces = self.rows[i] rowi_values = self.data[i] for k in range(0,len(rowi_indeces)): new_ar[rowi_indeces[k],i] = rowi_values[k] return new_ar # Variables elif name == 'variables': l = [] for i in range(0,self.shape[0],1): for obj in self.data[i]: if not np.isscalar(obj): l += obj.variables return cvxpy_list(set(l)) # Parameters elif name == 'parameters': l = [] for i in range(0,self.shape[0],1): for obj in self.data[i]: if not np.isscalar(obj): l += obj.parameters return cvxpy_list(set(l)) # Value elif name == 'value': mat = cvxpy_spmatrix((self.shape[0],self.shape[1]),np.float64) for i in range(0,self.shape[0],1): rowi_indeces = self.rows[i] rowi_values = self.data[i] for k in range(0,len(rowi_indeces)): if np.isscalar(rowi_values[k]): mat[i,rowi_indeces[k]] = rowi_values[k] else: mat[i,rowi_indeces[k]] = rowi_values[k].value return mat # Other attribute else: return object.__getattribute__(self,name)
def __getattribute__(self,name): # Parameters if name == 'parameters': return cvxpy_list([self]) # Other else: return object.__getattribute__(self,name)
def get_params(self): """ Description ----------- Construct a set with the parameters present in the program. """ constr_params = self.constr.get_params() obj_params = self.obj.get_params() all_params = constr_params for v in obj_params: l1 = map(lambda x: x is v,all_params) if(len(l1) != 0): if not reduce(lambda x,y: x or y,l1): all_params += cvxpy_list([v]) else: all_params += cvxpy_list([v]) return all_params
def __getattribute__(self,name): # Variables if name == 'variables': return cvxpy_list([self]) # Other else: return object.__getattribute__(self,name)
def get_params(self): """ Description ----------- Construct a set with the parameters present in the program. """ constr_params = self.constr.get_params() obj_params = self.obj.get_params() all_params = constr_params for v in obj_params: l1 = map(lambda x: x is v, all_params) if (len(l1) != 0): if not reduce(lambda x, y: x or y, l1): all_params += cvxpy_list([v]) else: all_params += cvxpy_list([v]) return all_params
def transform(constr_list): """ Description ----------- Transforms nonlinear equality constraints to equivalent form involving two inequalities. Arguments --------- constr_list: cvxpy_list of constraints in expanded form so that all nonlinear constraints are equalities. """ # New list new_list = cvxpy_list([]) # Transform for constr in constr_list: # Function if (constr.left.type == TREE and constr.left.item.type == FUNCTION): # Get function fn = constr.left.item # Get equivalent transformation new_constr = cvxpy_list([ less(constr.left, constr.right), greater(constr.left, constr.right) ]) # Append new_list += cvxpy_list(new_constr) # Not a function else: # Append new_list += cvxpy_list([constr]) # Return transformed list return new_list
def __getattribute__(self, name): # Transpose if name == 'T': new_ar = cvxpy_array(self.shape[1], self.shape[0]) for i in range(0, self.shape[0], 1): for j in range(0, self.shape[1], 1): new_ar.data[j][i] = self.data[i][j] return new_ar # Variables elif name == 'variables': l = [] for i in range(0, self.shape[0], 1): for j in range(0, self.shape[1], 1): if not np.isscalar(self.data[i][j]): l += self.data[i][j].variables return cvxpy_list(set(l)) # Parameters elif name == 'parameters': l = [] for i in range(0, self.shape[0], 1): for j in range(0, self.shape[1], 1): if not np.isscalar(self.data[i][j]): l += self.data[i][j].parameters return cvxpy_list(set(l)) # Value elif name == 'value': mat = cvxpy_matrix(np.zeros((self.shape[0], self.shape[1])), np.float64) for i in range(0, self.shape[0], 1): for j in range(0, self.shape[1], 1): if np.isscalar(self.data[i][j]): mat[i, j] = self.data[i][j] else: mat[i, j] = self.data[i][j].value return mat # Other attribute else: return object.__getattribute__(self, name)
def __getattribute__(self,name): # Transpose if name == 'T': new_ar = cvxpy_array(self.shape[1],self.shape[0]) for i in range(0,self.shape[0],1): for j in range(0,self.shape[1],1): new_ar.data[j][i] = self.data[i][j] return new_ar # Variables elif name == 'variables': l = [] for i in range(0,self.shape[0],1): for j in range(0,self.shape[1],1): if not np.isscalar(self.data[i][j]): l += self.data[i][j].variables return cvxpy_list(set(l)) # Parameters elif name == 'parameters': l = [] for i in range(0,self.shape[0],1): for j in range(0,self.shape[1],1): if not np.isscalar(self.data[i][j]): l += self.data[i][j].parameters return cvxpy_list(set(l)) # Value elif name == 'value': mat = cvxpy_matrix(np.zeros((self.shape[0],self.shape[1])),np.float64) for i in range(0,self.shape[0],1): for j in range(0,self.shape[1],1): if np.isscalar(self.data[i][j]): mat[i,j] = self.data[i][j] else: mat[i,j] = self.data[i][j].value return mat # Other attribute else: return object.__getattribute__(self,name)
def transform(constr_list): """ Description ----------- Transforms nonlinear equality constraints to equivalent form involving two inequalities. Arguments --------- constr_list: cvxpy_list of constraints in expanded form so that all nonlinear constraints are equalities. """ # New list new_list = cvxpy_list([]) # Transform for constr in constr_list: # Function if(constr.left.type == TREE and constr.left.item.type == FUNCTION): # Get function fn = constr.left.item # Get equivalent transformation new_constr = cvxpy_list([less(constr.left,constr.right), greater(constr.left,constr.right)]) # Append new_list += cvxpy_list(new_constr) # Not a function else: # Append new_list += cvxpy_list([constr]) # Return transformed list return new_list
def _pm_expand(self, constr): """ Description: Return the partial minimization expansion of the function. Argument constr: The constraint to be replaced. It is assumed to be in expanded format so the right hand side is a variable. """ arg = constr.left.children[0] right = constr.right return cvxpy_list([less(-right, arg), less(arg, right)])
def _pm_expand(self,constr): """ Description: Return the partial minimization expansion of the function. Argument constr: The constraint to be replaced. It is assumed to be in expanded format so the right hand side is a variable. """ arg = constr.left.children[0] right = constr.right return cvxpy_list([less(-right,arg), less(arg,right)])
def __getattribute__(self,name): # Variables if name == 'variables': l = list(map(lambda x: x.variables,self.children)) return cvxpy_list(set(reduce(lambda x,y:x+y,l,[]))) # Parameters elif name == 'parameters': l = list(map(lambda x: x.parameters,self.children)) return cvxpy_list(set(reduce(lambda x,y:x+y,l,[]))) # Value elif name == 'value': # Summation if (self.item.type == OPERATOR and self.item.name == SUMMATION): return np.sum(list(map(lambda x:x.value,self.children))) # Multiplication elif (self.item.type == OPERATOR and self.item.name == MULTIPLICATION): return self.children[0].value*self.children[1].value # Function elif self.item.type == FUNCTION: return self.item(list(map(lambda x: x.value,self.children))) # Error else: raise TypeError('Invalid tree item') # Other else: return object.__getattribute__(self,name)
def compare(obj1,op,obj2): # Both scalars if((np.isscalar(obj1) or type(obj1).__name__ in SCALAR_OBJS) and (np.isscalar(obj2) or type(obj2).__name__ in SCALAR_OBJS)): # Upgrade scalars to cvxpy_obj if(np.isscalar(obj1)): obj1 = cvxpy_obj(CONSTANT,obj1,str(obj1)) if(np.isscalar(obj2)): obj2 = cvxpy_obj(CONSTANT,obj2,str(obj2)) # Construct and return constraint return cvxpy_constr(obj1,op,obj2) # Upgrate scalars to arrays if((type(obj1) is cvxpy_matrix or type(obj1).__name__ in ARRAY_OBJS) and (np.isscalar(obj2) or type(obj2).__name__ in SCALAR_OBJS)): (m,n) = obj1.shape new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): new_exp[i,j] = obj2 obj2 = new_exp if((type(obj2) is cvxpy_matrix or type(obj2).__name__ in ARRAY_OBJS) and (np.isscalar(obj1) or type(obj1).__name__ in SCALAR_OBJS)): (m,n) = obj2.shape new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): new_exp[i,j] = obj1 obj1 = new_exp # Both arrays if((type(obj1) is cvxpy_matrix or type(obj1).__name__ in ARRAY_OBJS) and (type(obj2) is cvxpy_matrix or type(obj2).__name__ in ARRAY_OBJS)): constr = [] if(obj1.shape != obj2.shape): raise ValueError('Invalid dimensions') (m,n) = obj1.shape for i in range(0,m,1): for j in range(0,n,1): constr += [compare(obj1[i,j],op,obj2[i,j])] return cvxpy_list(constr) # Invalid arguments raise ValueError('Objects not comparable')
def compare(obj1, op, obj2): # Both scalars if ((np.isscalar(obj1) or type(obj1).__name__ in SCALAR_OBJS) and (np.isscalar(obj2) or type(obj2).__name__ in SCALAR_OBJS)): # Upgrade scalars to cvxpy_obj if (np.isscalar(obj1)): obj1 = cvxpy_obj(CONSTANT, obj1, str(obj1)) if (np.isscalar(obj2)): obj2 = cvxpy_obj(CONSTANT, obj2, str(obj2)) # Construct and return constraint return cvxpy_constr(obj1, op, obj2) # Upgrate scalars to arrays if ((type(obj1) is cvxpy_matrix or type(obj1).__name__ in ARRAY_OBJS) and (np.isscalar(obj2) or type(obj2).__name__ in SCALAR_OBJS)): (m, n) = obj1.shape new_exp = cvxpy_expression(m, n) for i in range(0, m, 1): for j in range(0, n, 1): new_exp[i, j] = obj2 obj2 = new_exp if ((type(obj2) is cvxpy_matrix or type(obj2).__name__ in ARRAY_OBJS) and (np.isscalar(obj1) or type(obj1).__name__ in SCALAR_OBJS)): (m, n) = obj2.shape new_exp = cvxpy_expression(m, n) for i in range(0, m, 1): for j in range(0, n, 1): new_exp[i, j] = obj1 obj1 = new_exp # Both arrays if ((type(obj1) is cvxpy_matrix or type(obj1).__name__ in ARRAY_OBJS) and (type(obj2) is cvxpy_matrix or type(obj2).__name__ in ARRAY_OBJS)): constr = [] if (obj1.shape != obj2.shape): raise ValueError('Invalid dimensions') (m, n) = obj1.shape for i in range(0, m, 1): for j in range(0, n, 1): constr += [compare(obj1[i, j], op, obj2[i, j])] return cvxpy_list(constr) # Invalid arguments raise ValueError('Objects not comparable')
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): # Get shape v = constr.left n = v.shape[0]-1 x = v[0:n,0] y = v[n,0] z = var() # Get power of 2 size m = 0 while (np.log2(n+m) % 1 != 0): m = m + 1 # Copy elements of x on a list and restrict them constr_list = [] el_list = [] for i in range(0,n,1): el_list+=[x[i,0]] if(not np.isscalar(x[i,0]) and type(x[i,0]) is not cvxpy_obj): constr_list += [greater(x[i,0],0)] # Construct expansion z = var() for i in range(0,m,1): el_list += [z] while(len(el_list) > 2): new_list = [] for i in range(0,len(el_list)/2): x1 = el_list[2*i] x2 = el_list[2*i+1] w = var() constr_list += [belongs(vstack((hstack((x1,w)), hstack((w,x2)))), sdc(2))] new_list += [w] el_list = new_list x1 = el_list[0] x2 = el_list[1] constr_list += [belongs(vstack((hstack((x1,z)), hstack((z,x2)))), sdc(2))] constr_list += [greater(z,0),greater(z,y)] return cvxpy_list(constr_list)
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 ----------- Return the partial minimization expansion of the function. Argument -------- constr: The constraint to be replaced. It is assumed the constraint was expanded and transformed so that the right hand side is a variable. """ new_list = [] for arg in constr.left.children: if type(arg) is cvxpy_obj: arg = arg.get_value() new_list += [less(arg, constr.right)] return cvxpy_list(new_list)
def _pm_expand(self, constr): """ Description ----------- Return the partial minimization expansion of the function. Argument -------- constr: The constraint to be replaced. It is assumed the constraint was expanded and transformed so that the right hand side is a variable. """ new_list = [] for arg in constr.left.children: if (type(arg) is cvxpy_obj): arg = arg.get_value() new_list += [less(arg, constr.right)] return cvxpy_list(new_list)
def _pm_expand(self, constr): # Get shape v = constr.left n = v.shape[0] - 1 x = v[0:n, 0] y = v[n, 0] z = var() # Get power of 2 size m = 0 while np.log2(n + m) % 1 != 0: m = m + 1 # Copy elements of x on a list and restrict them constr_list = [] el_list = [] for i in range(0, n, 1): el_list += [x[i, 0]] if not np.isscalar(x[i, 0]) and type(x[i, 0]) is not cvxpy_obj: constr_list += [greater(x[i, 0], 0)] # Construct expansion z = var() for i in range(0, m, 1): el_list += [z] while len(el_list) > 2: new_list = [] for i in range(0, len(el_list) / 2): x1 = el_list[2 * i] x2 = el_list[2 * i + 1] w = var() constr_list += [belongs(vstack((hstack((x1, w)), hstack((w, x2)))), sdc(2))] new_list += [w] el_list = new_list x1 = el_list[0] x2 = el_list[1] constr_list += [belongs(vstack((hstack((x1, z)), hstack((z, x2)))), sdc(2))] constr_list += [greater(z, 0), greater(z, y)] return cvxpy_list(constr_list)
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 is_affine(self): """ Determines if tree is an affine expression. """ # Summation if (self.item.type == OPERATOR and self.item.name == SUMMATION): return all(list(map(lambda x: x.is_affine(),self.children))) # Multiplication elif (self.item.type == OPERATOR and self.item.name == MULTIPLICATION): ob2 = self.children[1] return ob2.is_affine() # Function elif self.item.type == FUNCTION: return len(cvxpy_list(self.children).variables) == 0 # Error else: raise TypeError('Invalid tree item')
def get_vars(self): return cvxpy_list([])
def _range_constr(self, v): return cvxpy_list([greater(v, 0)])
def _dom_constr(self, args): return cvxpy_list([])
def _range_constr(self, v): return cvxpy_list([])
def re_eval(arg,param_map): """ Description ----------- Replaces parameters found in arg using the param_map and re-evaluates the resulting object. Arguments --------- arg: Argument to be re-evaluated. param_map: Dictionery that maps the parameters to objects. """ # Number if(np.isscalar(arg)): return arg # Constant object elif(type(arg) is cvxpy_obj): return arg.get_value() # Scalar variable elif(type(arg) is cvxpy_scalar_var): return arg # Scalar param elif(type(arg) is cvxpy_scalar_param): return re_eval(param_map[arg],param_map) # Summation elif(type(arg) is cvxpy_tree and arg.item.name == '+'): new_children = map(lambda x:re_eval(x,param_map),arg.children) return sum(new_children) # Multiplication elif(type(arg) is cvxpy_tree and arg.item.name == '*'): child1 = re_eval(arg.children[0],param_map) child2 = re_eval(arg.children[1],param_map) return child1*child2 # Function elif(type(arg) is cvxpy_tree and arg.item.type == FUNCTION): new_children = map(lambda x:re_eval(x,param_map),arg.children) return arg.item(new_children) # Constraint elif(type(arg) is cvxpy_constr): # Not set membership if(arg.op != 'in'): left = re_eval(arg.left ,param_map) right= re_eval(arg.right,param_map) if(arg.op == '=='): return equal(left,right) elif(arg.op == '<='): return less(left,right) else: return greater(left,right) # Set membership else: left = re_eval(arg.left,param_map) return belongs(left,arg.right) # Array elif(type(arg) is cvxpy_expression or type(arg) is cvxpy_var or type(arg) is cvxpy_param): (m,n) = arg.shape new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): new_exp[i,j] = re_eval(arg[i,j],param_map) return new_exp # List elif(type(arg) is cvxpy_list): new_list = cvxpy_list([]) for c in arg: new_list += cvxpy_list([re_eval(c,param_map)]) return new_list # Invalid else: raise ValueError('Invalid argument')
def _range_constr(self, v): return cvxpy_list([greater(v,0)])
def _dom_constr(self, args): arg = args[0] return cvxpy_list([greater(arg, 0)])
def expand(arg): # Constant if(type(arg) is cvxpy_obj): return arg,cvxpy_list([]) # Scalar variable elif(type(arg) is cvxpy_scalar_var): return arg,cvxpy_list([]) # Summation elif(type(arg) is cvxpy_tree and arg.item.name == '+'): # Get item and children item = arg.item children = arg.children # New var v = var() # Expand children new_children = [] new_constr = cvxpy_list([]) for child in children: # Multiplication if(child.type == TREE and child.item.name == '*'): child_var,child_constr = expand(child.children[1]) new_children += [child.children[0].data*child_var] new_constr += child_constr # Else else: child_var,child_constr = expand(child) new_children += [child_var] new_constr += child_constr # Return (Always right side is the new variable) new_tree = cvxpy_tree(item,new_children) return v,cvxpy_list([equal(new_tree,v)])+new_constr # Multiplication elif(type(arg) is cvxpy_tree and arg.item.name == '*'): # Get item and children item = arg.item children = arg.children # New var v = var() # Apply expand to second operand (first is a constant) child_var,child_constr = expand(children[1]) # Return result (Always right side is the new variable) new_tree = cvxpy_tree(item,[children[0],child_var]) new_eq = cvxpy_list([equal(new_tree,v)]) new_eq += child_constr return v,new_eq # Function elif(type(arg) is cvxpy_tree and arg.item.type == FUNCTION): # Get item and children item = arg.item children = arg.children # New var v = var() # Analyze children new_children = [] new_constr = cvxpy_list([]) for child in children: child_var,child_constr = expand(child) new_children += [child_var] new_constr += child_constr # Return (Always right side is the new variable) new_tree = cvxpy_tree(item,new_children) new_constr += item._range_constr(v) new_constr += item._dom_constr(new_children) return v,cvxpy_list([equal(new_tree,v)])+new_constr # Constraint elif(type(arg) is cvxpy_constr): # Not set membership if(arg.op != 'in'): # Apply expand to left and right side obj1,constr_list1 = expand(arg.left) obj2,constr_list2 = expand(arg.right) # Return new constraints new_constr = cvxpy_constr(obj1,arg.op,obj2) new_list = cvxpy_list([new_constr]) new_list += constr_list1 new_list += constr_list2 return new_list # Set membership else: obj, constr_list = expand(arg.left) new_constr = cvxpy_constr(obj,arg.op,arg.right) return cvxpy_list([new_constr])+constr_list # Array elif(type(arg) is cvxpy_expression or type(arg) is cvxpy_var): (m,n) = arg.shape new_list = cvxpy_list([]) new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): # Number: Upgrade if(np.isscalar(arg[i,j])): new_exp[i,j] = cvxpy_obj(CONSTANT,arg[i,j],str(arg[i,j])) # Not a number else: obj,constr_list = expand(arg[i,j]) new_exp[i,j] = obj new_list += constr_list return new_exp,new_list # List of constraints elif(type(arg) is cvxpy_list): # Empty list if(len(arg) == 0): return cvxpy_list([]) else: new_list = map(expand,arg) return reduce(lambda x,y:x+y,new_list) # Invalid else: raise ValueError('Invalid argument')
def get_params(self): return cvxpy_list([])
def get_params(self): l = map(lambda x: x.get_params(),self.children) if(len(l) != 0): return reduce(lambda x,y:x+y,l) else: return cvxpy_list([])
def expand(arg): # Constant if (type(arg) is cvxpy_obj): return arg, cvxpy_list([]) # Scalar variable elif (type(arg) is cvxpy_scalar_var): return arg, cvxpy_list([]) # Summation elif (type(arg) is cvxpy_tree and arg.item.name == '+'): # Get item and children item = arg.item children = arg.children # New var v = var() # Expand children new_children = [] new_constr = cvxpy_list([]) for child in children: # Multiplication if (child.type == TREE and child.item.name == '*'): child_var, child_constr = expand(child.children[1]) new_children += [child.children[0].data * child_var] new_constr += child_constr # Else else: child_var, child_constr = expand(child) new_children += [child_var] new_constr += child_constr # Return (Always right side is the new variable) new_tree = cvxpy_tree(item, new_children) return v, cvxpy_list([equal(new_tree, v)]) + new_constr # Multiplication elif (type(arg) is cvxpy_tree and arg.item.name == '*'): # Get item and children item = arg.item children = arg.children # New var v = var() # Apply expand to second operand (first is a constant) child_var, child_constr = expand(children[1]) # Return result (Always right side is the new variable) new_tree = cvxpy_tree(item, [children[0], child_var]) new_eq = cvxpy_list([equal(new_tree, v)]) new_eq += child_constr return v, new_eq # Function elif (type(arg) is cvxpy_tree and arg.item.type == FUNCTION): # Get item and children item = arg.item children = arg.children # New var v = var() # Analyze children new_children = [] new_constr = cvxpy_list([]) for child in children: child_var, child_constr = expand(child) new_children += [child_var] new_constr += child_constr # Return (Always right side is the new variable) new_tree = cvxpy_tree(item, new_children) new_constr += item._range_constr(v) new_constr += item._dom_constr(new_children) return v, cvxpy_list([equal(new_tree, v)]) + new_constr # Constraint elif (type(arg) is cvxpy_constr): # Not set membership if (arg.op != 'in'): # Apply expand to left and right side obj1, constr_list1 = expand(arg.left) obj2, constr_list2 = expand(arg.right) # Return new constraints new_constr = cvxpy_constr(obj1, arg.op, obj2) new_list = cvxpy_list([new_constr]) new_list += constr_list1 new_list += constr_list2 return new_list # Set membership else: obj, constr_list = expand(arg.left) new_constr = cvxpy_constr(obj, arg.op, arg.right) return cvxpy_list([new_constr]) + constr_list # Array elif (type(arg) is cvxpy_expression or type(arg) is cvxpy_var): (m, n) = arg.shape new_list = cvxpy_list([]) new_exp = cvxpy_expression(m, n) for i in range(0, m, 1): for j in range(0, n, 1): # Number: Upgrade if (np.isscalar(arg[i, j])): new_exp[i, j] = cvxpy_obj(CONSTANT, arg[i, j], str(arg[i, j])) # Not a number else: obj, constr_list = expand(arg[i, j]) new_exp[i, j] = obj new_list += constr_list return new_exp, new_list # List of constraints elif (type(arg) is cvxpy_list): # Empty list if (len(arg) == 0): return cvxpy_list([]) else: new_list = map(expand, arg) return reduce(lambda x, y: x + y, new_list) # Invalid else: raise ValueError('Invalid argument')