def vstack(t): """ Vertical stack. """ # Verify input type new_list = [] numeric = True for x in t: if (np.isscalar(x)): new_list += [matrix(x)] elif (type(x) is cvxpy_obj): new_list += [matrix(x.data)] elif (type(x).__name__ in SCALAR_OBJS): numeric = False new_x = cvxpy_expression(1, 1) new_x[0, 0] = x new_list += [new_x] elif (type(x).__name__ in ARRAY_OBJS): numeric = False new_list += [x] elif (type(x) is cvxpy_matrix): new_list += [x] else: raise ValueError('Invalid Input') # Input is numeric if (numeric): return np.vstack(new_list) # Verify dimensions n = new_list[0].shape[1] for x in new_list: if (x.shape[1] != n): raise ValueError('Invalid Dimensions') # Allocate new expression m = 0 for x in new_list: m += x.shape[0] new_exp = cvxpy_expression(m, n) # Fill new expression k = 0 for x in new_list: for i in range(0, x.shape[0], 1): for j in range(0, x.shape[1], 1): new_exp[i + k, j] = x[i, j] k = k + x.shape[0] # Return new expression return new_exp
def vstack(t): """ Vertical stack. """ # Verify input type new_list = [] numeric = True for x in t: if(np.isscalar(x)): new_list += [matrix(x)] elif(type(x) is cvxpy_obj): new_list += [matrix(x.data)] elif(type(x).__name__ in SCALAR_OBJS): numeric = False new_x = cvxpy_expression(1,1) new_x[0,0] = x new_list += [new_x] elif(type(x).__name__ in ARRAY_OBJS): numeric = False new_list += [x] elif(type(x) is cvxpy_matrix): new_list += [x] else: raise ValueError('Invalid Input') # Input is numeric if(numeric): return np.vstack(new_list) # Verify dimensions n = new_list[0].shape[1] for x in new_list: if(x.shape[1] != n): raise ValueError('Invalid Dimensions') # Allocate new expression m = 0 for x in new_list: m += x.shape[0] new_exp = cvxpy_expression(m,n) # Fill new expression k = 0 for x in new_list: for i in range(0,x.shape[0],1): for j in range(0,x.shape[1],1): new_exp[i+k,j] = x[i,j] k = k + x.shape[0] # Return new expression return new_exp
def diagflat(arg): """ List, row or column vector to diagonal matrix. """ # Argument is a list if (type(arg) is list): arg = hstack(arg) # Argument is a matrix or object array elif (type(arg) is cvxpy_matrix or type(arg).__name__ in ARRAY_OBJS): (m, n) = arg.shape if (m != 1 and n != 1): raise ValueError('Argument must be one dimensional') elif (n == 1): arg = arg.T # Invalid argument else: raise ValueError('Invalid argument') # Argument is numeric numeric = True for i in range(0, arg.shape[1], 1): if (not np.isscalar(arg[0, i])): numeric = False if (numeric): return matrix(np.diagflat(arg)) # Not numeric (m, n) = arg.shape new_exp = cvxpy_expression(n, n) for i in range(0, n, 1): new_exp[i, i] = arg[0, i] return new_exp
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 __call__(self,*arg): # Check number of arguments if(len(arg) != 1): raise ValueError('Invalid number of arguments') # Extract argument if(type(arg[0]) is list): x = arg[0][0] else: x = arg[0] # Process if(type(x).__name__ in SCALAR_OBJS): return cvxpy_tree(self,[x]) elif(type(x).__name__ in ARRAY_OBJS): (m,n) = x.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] = self(x[i,j]) return new_exp else: return np.square(x)
def diag(arg): """ Extract the diagonal from square array-like object. """ # Check size (m, n) = arg.shape if (m != n): raise ValueError('Invalid dimensions') # cvxpy matrix if (type(arg) is cvxpy_matrix): return matrix(np.diag(arg)).T # Object array elif (type(arg).__name__ in ARRAY_OBJS): new_exp = cvxpy_expression(m, 1) for i in range(0, m, 1): new_exp[i, 0] = arg[i, i] return new_exp # Error else: raise ValueError('Invalid argument type')
def __call__(self, *arg): # Check number of arguments if (len(arg) != 1): raise ValueError('Invalid number of arguments') # Extract argument if (type(arg[0]) is list): x = arg[0][0] else: x = arg[0] # Process if (type(x).__name__ in SCALAR_OBJS): return cvxpy_tree(self, [x]) elif (type(x).__name__ in ARRAY_OBJS): (m, n) = x.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] = self(x[i, j]) return new_exp else: return np.square(x)
def diagflat(arg): """ List, row or column vector to diagonal matrix. """ # Argument is a list if(type(arg) is list): arg = hstack(arg) # Argument is a matrix or object array elif(type(arg) is cvxpy_matrix or type(arg).__name__ in ARRAY_OBJS): (m,n) = arg.shape if(m!=1 and n!=1): raise ValueError('Argument must be one dimensional') elif(n == 1): arg = arg.T # Invalid argument else: raise ValueError('Invalid argument') # Argument is numeric numeric = True for i in range(0,arg.shape[1],1): if(not np.isscalar(arg[0,i])): numeric = False if(numeric): return matrix(np.diagflat(arg)) # Not numeric (m,n) = arg.shape new_exp = cvxpy_expression(n,n) for i in range(0,n,1): new_exp[i,i] = arg[0,i] return new_exp
def diag(arg): """ Extract the diagonal from square array-like object. """ # Check size (m,n) = arg.shape if(m!=n): raise ValueError('Invalid dimensions') # cvxpy matrix if(type(arg) is cvxpy_matrix): return matrix(np.diag(arg)).T # Object array elif(type(arg).__name__ in ARRAY_OBJS): new_exp = cvxpy_expression(m,1) for i in range(0,m,1): new_exp[i,0] = arg[i,i] return new_exp # Error else: raise ValueError('Invalid argument type')
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 __addsub__(self,other,op): """ Description ----------- Handles left add or left subtract. Arguments --------- other: right hand object. op: Character (+ or -) """ # Create operator (I should create another class for this) operator = cvxpy_obj(OPERATOR,None,'+') # Create args to combine if(type(self) is cvxpy_tree and self.item.name == '+'): args = self.children else: args = [self] # Number if(np.isscalar(other)): if(other == 0.0): return self if(op == '+'): constant = cvxpy_obj(CONSTANT,other,str(other)) else: constant = cvxpy_obj(CONSTANT,-other,str(-other)) return cvxpy_tree(operator,args+[constant]) # Scalar variable or scalar param elif(type(other) is cvxpy_scalar_var or type(other) is cvxpy_scalar_param): if(op == '+'): return cvxpy_tree(operator,args+[other]) else: return cvxpy_tree(operator,args+[-other]) # Tree elif(type(other) is cvxpy_tree): if(other.item.name == '+'): if(op == '+'): return cvxpy_tree(operator, args+other.children) else: return cvxpy_tree(operator, args+[-x for x in other.children]) else: if(op == '+'): return cvxpy_tree(operator,args+[other]) else: return cvxpy_tree(operator,args+[-other]) # Matrix elif(type(other) is cvxpy_matrix): m = other.shape[0] n = other.shape[1] new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): if(op == '+'): new_exp[i,j] = self+other[i,j] else: new_exp[i,j] = self-other[i,j] return new_exp # Not implemented else: return NotImplemented
def __mulhandle__(self,other): """ Description ----------- Handles multiply. Note: If other is numeric, the multiplication tree formed has the constant object as first child. Argument -------- other: Other object. """ # Create operator operator = cvxpy_obj(OPERATOR,None,'*') # Number if(np.isscalar(other)): # Multiplication by 0 if(other == 0.0): return 0.0 # Multiplication by 1 elif(other == 1.0): return self # Distribution over addition elif(type(self) is cvxpy_tree and self.item.name == '+'): new_children = [] for child in self.children: new_children += [other*child] return cvxpy_tree(self.item,new_children) # Associativity elif(type(self) is cvxpy_tree and self.item.name == '*' and self.children[0].type == CONSTANT): new_object = other*self.children[0] if(new_object.data == 1.0): return self.children[1] else: return cvxpy_tree(self.item,[new_object, self.children[1]]) # Constant times constant elif(type(self) is cvxpy_obj): new_const = cvxpy_obj(CONSTANT,self.data*other, str(self.data*other)) return new_const # Else else: constant = cvxpy_obj(CONSTANT,other,str(other)) return cvxpy_tree(operator,[constant,self]) # Matrix elif(type(other) is cvxpy_matrix): m = other.shape[0] n = other.shape[1] new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): new_exp[i,j] = other[i,j]*self return new_exp # Scalar param elif(type(other) is cvxpy_scalar_param): return cvxpy_tree(operator,[other,self]) # Tree elif(type(other) is cvxpy_tree): if(len(self.get_vars()) == 0 ): return cvxpy_tree(operator,[self,other]) elif(len(other.get_vars()) == 0): return cvxpy_tree(operator,[other,self]) else: return NotImplemented # Not implemented else: return NotImplemented
def __raddsub__(self,other,op): """ Description ----------- Handles right add or right subtract. Arguments --------- other: left hand object. op: Character (+ or -) """ # Create operator operator = cvxpy_obj(OPERATOR,None,'+') # Create args to combine if(type(self) is cvxpy_tree and self.item.name == '+'): if(op == '+'): args = self.children else: args = [-x for x in self.children] else: if(op == '+'): args = [self] else: args = [-self] # Number if(np.isscalar(other)): if(other == 0.0): if(op == '+'): return self else: return -self constant = cvxpy_obj(CONSTANT,other,str(other)) return cvxpy_tree(operator,[constant]+args) # Scalar variable or scalar param elif(type(other) is cvxpy_scalar_var or type(other) is cvxpy_scalar_param): return cvxpy_tree(operator,[other]+args) # Tree elif(type(other) is cvxpy_tree): if(other.item.name == '+'): return cvxpy_tree(operator,other.children+args) else: return cvxpy_tree(operator,[other]+args) # Matrix elif(type(other) is cvxpy_matrix): m = other.shape[0] n = other.shape[1] new_exp = cvxpy_expression(m,n) for i in range(0,m,1): for j in range(0,n,1): if(op == '+'): new_exp[i,j] = other[i,j]+self else: new_exp[i,j] = other[i,j]-self return new_exp # Not implemented else: return NotImplemented
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 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')