def constr_func(aff_obj): # Combine the constraints from both sides and add an equality constraint. lh_obj, lh_constr = lh_set.canonical_form rh_obj, rh_constr = rh_set.canonical_form constraints = [lu.create_eq(aff_obj, lh_obj), lu.create_eq(aff_obj, rh_obj), ] return constraints + lh_constr + rh_constr
def constraints(self): obj, constraints = super(BoolVar, self).canonicalize() one = lu.create_const(1, (1, 1)) constraints += [lu.create_geq(obj), lu.create_leq(obj, one)] for i in range(self.size[0]): row_sum = lu.sum_expr([self[i, j] for j in range(self.size[0])]) col_sum = lu.sum_expr([self[j, i] for j in range(self.size[0])]) constraints += [lu.create_eq(row_sum, one), lu.create_eq(col_sum, one)] return constraints
def _grad(self, values): """Gives the (sub/super)gradient of the atom w.r.t. each argument. Matrix expressions are vectorized, so the gradient is a matrix. Args: values: A list of numeric values for the arguments. Returns: A list of SciPy CSC sparse matrices or None. """ # TODO should be a simple function in CVXcanon for this. # Make a fake lin op tree for the function. fake_args = [] var_offsets = {} offset = 0 for idx, arg in enumerate(self.args): fake_args += [lu.create_var(arg.size, idx)] var_offsets[idx] = offset offset += arg.size[0] * arg.size[1] fake_expr, _ = self.graph_implementation(fake_args, self.size, self.get_data()) # Get the matrix representation of the function. V, I, J, _ = canonInterface.get_problem_matrix( [lu.create_eq(fake_expr)], var_offsets, None) shape = (offset, self.size[0] * self.size[1]) stacked_grad = sp.coo_matrix((V, (J, I)), shape=shape).tocsc() # Break up into per argument matrices. grad_list = [] start = 0 for idx, arg in enumerate(self.args): stop = start + arg.size[0] * arg.size[1] grad_list += [stacked_grad[start:stop, :]] start = stop return grad_list
def _get_obj(self, objective, var_offsets, x_length, matrix_intf, vec_intf): """Wraps _constr_matrix so it can be called for the objective. """ dummy_constr = lu.create_eq(objective) return self._constr_matrix([dummy_constr], var_offsets, x_length, matrix_intf, vec_intf)
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ A = arg_objs[0] n, _ = A.size # Requires that A is symmetric. # A == A.T obj, constraints = transpose.graph_implementation([A], (n, n)) constraints.append(lu.create_eq(A, obj)) # SDP constraint. t = lu.create_var((1, 1)) prom_t = lu.promote(t, (n, 1)) # I*t - A expr = lu.sub_expr(lu.diag_vec(prom_t), A) return (t, [SDP(expr)] + constraints)
def graph_implementation(self, arg_objs, shape: Tuple[int, ...], data=None) -> Tuple[lo.LinOp, List[Constraint]]: """Cumulative sum via difference matrix. Parameters ---------- arg_objs : list LinExpr for each argument. shape : tuple The shape of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ # Implicit O(n) definition: # X = Y[1:,:] - Y[:-1, :] Y = lu.create_var(shape) axis = data[0] dim = shape[axis] diff_mat = get_diff_mat(dim, axis) diff_mat = lu.create_const(diff_mat, (dim, dim), sparse=True) if axis == 0: diff = lu.mul_expr(diff_mat, Y) else: diff = lu.rmul_expr(Y, diff_mat) return (Y, [lu.create_eq(arg_objs[0], diff)])
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ A = arg_objs[0] n, _ = A.size # Requires that A is symmetric. obj, constraints = transpose.graph_implementation([A], (n, n)) # A == A.T constraints.append(lu.create_eq(A, obj)) # SDP constraint. t = lu.create_var((1, 1)) prom_t = lu.promote(t, (n, 1)) # I*t - A expr = lu.sub_expr(A, lu.diag_vec(prom_t)) return (t, [SDP(expr)] + constraints)
def block_eq(matrix, block, constraints, row_start, row_end, col_start, col_end): """Adds an equality setting a section of the matrix equal to block. Assumes block does not need to be promoted. Parameters ---------- matrix : LinOp The matrix in the block equality. block : LinOp The block in the block equality. constraints : list A list of constraints to append to. row_start : int The first row of the matrix section. row_end : int The last row + 1 of the matrix section. col_start : int The first column of the matrix section. col_end : int The last column + 1 of the matrix section. """ key = (slice(row_start, row_end, None), slice(col_start, col_end, None)) rows = row_end - row_start cols = col_end - col_start assert block.size == (rows, cols) slc, idx_constr = index.graph_implementation([matrix], (rows, cols), [key]) constraints += [lu.create_eq(slc, block)] + idx_constr
def format(self, eq_constr, leq_constr, dims, solver): """Formats EXP constraints for the solver. Parameters ---------- eq_constr : list A list of the equality constraints in the canonical problem. leq_constr : list A list of the inequality constraints in the canonical problem. dims : dict A dict with the dimensions of the conic constraints. solver : str The solver being called. """ # Need x, y, z to be lone Variables. if solver == s.CVXOPT: constraints = [] for i, var in enumerate(self.vars_): if not var.type is VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var eq_constr += constraints # Converts to an inequality constraint. elif solver == s.SCS: leq_constr += format_elemwise([self.x, self.y, self.z]) else: raise ValueError("Solver does not support exponential cone.") # Update dims. dims[s.EXP_DIM] += self.size[0]*self.size[1]
def constr_func(aff_obj): theta = [lu.create_var((1, 1)) for i in range(len(values))] convex_objs = [] for val, theta_var in zip(values, theta): val_aff = val.canonical_form[0] convex_objs.append( lu.mul_expr(val_aff, theta_var, val_aff.size)) convex_combo = lu.sum_expr(convex_objs) one = lu.create_const(1, (1, 1)) constraints = [ lu.create_eq(aff_obj, convex_combo), lu.create_eq(lu.sum_expr(theta), one) ] for theta_var in theta: constraints.append(lu.create_geq(theta_var)) return constraints
def format(self, eq_constr, leq_constr, dims, solver): """Formats EXP constraints for the solver. Parameters ---------- eq_constr : list A list of the equality constraints in the canonical problem. leq_constr : list A list of the inequality constraints in the canonical problem. dims : dict A dict with the dimensions of the conic constraints. solver : str The solver being called. """ # Need x, y, z to be lone Variables. if solver == s.CVXOPT: constraints = [] for i, var in enumerate(self.vars_): if not var.type is VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var eq_constr += constraints # Converts to an inequality constraint. elif solver == s.SCS: leq_constr += format_elemwise([self.x, self.y, self.z]) else: raise ValueError("Solver does not support exponential cone.") # Update dims. dims[s.EXP_DIM] += self.size[0] * self.size[1]
def __format(self, solver): """Internal version of format with cached results. Returns ------- tuple (equality constraints, inequality constraints) """ eq_constr = [] leq_constr = [] # Need x, y, z to be lone Variables. if solver == s.CVXOPT: constraints = [] for i, var in enumerate(self.vars_): if not var.type is VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var eq_constr += constraints # Converts to an inequality constraint. elif solver == s.SCS: leq_constr += format_elemwise([self.x, self.y, self.z]) else: raise SolverError("Solver does not support exponential cone.") return (eq_constr, leq_constr)
def graph_implementation(arg_objs, size, data=None): """Cumulative sum via difference matrix. Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ # Implicit O(n) definition: # X = Y[:1,:] - Y[1:,:] Y = lu.create_var(size) axis = data[0] dim = size[axis] diff_mat = get_diff_mat(dim, axis) diff_mat = lu.create_const(diff_mat, (dim, dim), sparse=True) if axis == 0: diff = lu.mul_expr(diff_mat, Y, size) else: diff = lu.rmul_expr(Y, diff_mat, size) return (Y, [lu.create_eq(arg_objs[0], diff)])
def _coeffs_affine(self, expr): sz = expr.size[0]*expr.size[1] s, _ = expr.canonical_form V, I, J, R = canonInterface.get_problem_matrix([lu.create_eq(s)], self.id_map) Q = sp.csr_matrix((V, (I, J)), shape=(sz, self.N)) Ps = [sp.csr_matrix((self.N, self.N)) for i in range(sz)] return (Ps, Q, R.flatten())
def __CVXOPT_format(self): constraints = [] for i, var in enumerate(self.vars_): if not var.type is VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var return (constraints, [])
def _coeffs_affine(self, expr): sz = expr.size[0] * expr.size[1] s, _ = expr.canonical_form V, I, J, R = canonInterface.get_problem_matrix([lu.create_eq(s)], self.id_map) Q = sp.csr_matrix((V, (I, J)), shape=(sz, self.N)) Ps = [sp.csr_matrix((self.N, self.N)) for i in range(sz)] return (Ps, Q, R.flatten())
def constr_func(aff_obj): theta = [lu.create_var((1, 1)) for i in xrange(len(values))] convex_objs = [] for val, theta_var in zip(values, theta): val_aff = val.canonical_form[0] convex_objs.append( lu.mul_expr(val_aff, theta_var, val_aff.size) ) convex_combo = lu.sum_expr(convex_objs) one = lu.create_const(1, (1, 1)) constraints = [lu.create_eq(aff_obj, convex_combo), lu.create_eq(lu.sum_expr(theta), one)] for theta_var in theta: constraints.append(lu.create_geq(theta_var)) return constraints
def __CVXOPT_format(self): constraints = [] for i, var in enumerate(self.vars_): if var.type is not VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var return (constraints, [])
def __format(self): """Internal version of format with cached results. Returns ------- tuple (equality constraints, inequality constraints) """ eq_constr = lu.create_eq(self.A, lu.transpose(self.A)) leq_constr = lu.create_geq(self.A) return ([eq_constr], [leq_constr])
def canonicalize(self): """Returns the graph implementation of the object. Marks the top level constraint as the dual_holder, so the dual value will be saved to the EqConstraint. Returns: A tuple of (affine expression, [constraints]). """ obj, constraints = self._expr.canonical_form dual_holder = lu.create_eq(obj, constr_id=self.id) return (None, constraints + [dual_holder])
def constr_func(aff_obj): G_aff = G.canonical_form[0] h_aff = h.canonical_form[0] Gx = lu.mul_expr(G_aff, aff_obj, h_aff.size) constraints = [lu.create_leq(Gx, h_aff)] if A is not None: A_const, b_const = map(self.cast_to_const, [A, b]) A_aff = A_const.canonical_form[0] b_aff = b_const.canonical_form[0] Ax = lu.mul_expr(A_aff, aff_obj, b_aff.size) constraints += [lu.create_eq(Ax, b_aff)] return constraints
def __format(self): """Internal version of format with cached results. Returns ------- tuple (equality constraints, inequality constraints) """ eq_constr = [] # If a bool var was created, add an equality constraint. if self.noncvx_var != self.lin_op: eq_constr += lu.create_eq(self.lin_op, noncvx_var) return (eq_constr, [])
def canonicalize(self): obj, constraints = super(Assign, self).canonicalize() shape = (self.size[1], 1) one_row_vec = lu.create_const(np.ones(shape), shape) shape = (1, self.size[0]) one_col_vec = lu.create_const(np.ones(shape), shape) # Row sum <= 1 row_sum = lu.rmul_expr(obj, one_row_vec, (self.size[0], 1)) constraints += [lu.create_leq(row_sum, lu.transpose(one_col_vec))] # Col sum == 1. col_sum = lu.mul_expr(one_col_vec, obj, (1, self.size[1])) constraints += [lu.create_eq(col_sum, lu.transpose(one_row_vec))] return (obj, constraints)
def __format(self): """Internal version of format with cached results. Returns ------- tuple (equality constraints, inequality constraints) """ eq_constr = [] # If a noncvx var was created, add an equality constraint. if self.noncvx_var != self.lin_op: eq_constr += lu.create_eq(self.lin_op, self.noncvx_var) return (eq_constr, [])
def test_eq_constr(self): """Test creating an equality constraint. """ shape = (5, 5) x = create_var(shape) y = create_var(shape) lh_expr = sum_expr([x, y]) value = np.ones(shape) rh_expr = create_const(value, shape) constr = create_eq(lh_expr, rh_expr) self.assertEqual(constr.shape, shape) vars_ = get_expr_vars(constr.expr) ref = [(x.data, shape), (y.data, shape)] self.assertCountEqual(vars_, ref)
def graph_implementation(arg_objs, size, data=None): # min 1-sqrt(2z-z^2) # s.t. x>=0, z<=1, z = x+s, s<=0 x = arg_objs[0] z = lu.create_var(size) s = lu.create_var(size) zeros = lu.create_const(np.mat(np.zeros(size)),size) ones = lu.create_const(np.mat(np.ones(size)),size) z2, constr_square = power.graph_implementation([z],size, (2, (Fraction(1,2), Fraction(1,2)))) two_z = lu.sum_expr([z,z]) sub = lu.sub_expr(two_z, z2) sq, constr_sqrt = power.graph_implementation([sub],size, (Fraction(1,2), (Fraction(1,2), Fraction(1,2)))) obj = lu.sub_expr(ones, sq) constr = [lu.create_eq(z, lu.sum_expr([x,s]))]+[lu.create_leq(zeros,x)]+[lu.create_leq(z, ones)]+[lu.create_leq(s,zeros)]+constr_square+constr_sqrt return (obj, constr)
def _grad(self, values): """Gives the (sub/super)gradient of the atom w.r.t. each argument. Matrix expressions are vectorized, so the gradient is a matrix. Args: values: A list of numeric values for the arguments. Returns: A list of SciPy CSC sparse matrices or None. """ # TODO should be a simple function in CVXcanon for this. # Make a fake lin op tree for the function. fake_args = [] var_offsets = {} offset = 0 for idx, arg in enumerate(self.args): if arg.is_constant(): fake_args += [lu.create_const(arg.value, arg.size)] else: fake_args += [lu.create_var(arg.size, idx)] var_offsets[idx] = offset offset += arg.size[0]*arg.size[1] fake_expr, _ = self.graph_implementation(fake_args, self.size, self.get_data()) # Get the matrix representation of the function. V, I, J, _ = canonInterface.get_problem_matrix( [lu.create_eq(fake_expr)], var_offsets, None ) shape = (offset, self.size[0]*self.size[1]) stacked_grad = sp.coo_matrix((V, (J, I)), shape=shape).tocsc() # Break up into per argument matrices. grad_list = [] start = 0 for arg in self.args: if arg.is_constant(): grad_shape = (arg.size[0]*arg.size[1], shape[1]) if grad_shape == (1, 1): grad_list += [0] else: grad_list += [sp.coo_matrix(grad_shape, dtype='float64')] else: stop = start + arg.size[0]*arg.size[1] grad_list += [stacked_grad[start:stop, :]] start = stop return grad_list
def graph_implementation(arg_objs, shape, data=None): """Reduces the atom to an affine expression and list of constraints. minimize n^2 + 2M|s| subject to s + n = x Parameters ---------- arg_objs : list LinExpr for each argument. shape : tuple The shape of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ M = data[0] x = arg_objs[0] n = lu.create_var(shape) s = lu.create_var(shape) two = lu.create_const(2, (1, 1)) if isinstance(M, Parameter): M = lu.create_param(M, (1, 1)) else: # M is constant. M = lu.create_const(M.value, (1, 1)) # n**2 + 2*M*|s| n2, constr_sq = power.graph_implementation( [n], shape, (2, (Fraction(1, 2), Fraction(1, 2))) ) abs_s, constr_abs = abs.graph_implementation([s], shape) M_abs_s = lu.mul_expr(M, abs_s) obj = lu.sum_expr([n2, lu.mul_expr(two, M_abs_s)]) # x == s + n constraints = constr_sq + constr_abs constraints.append(lu.create_eq(x, lu.sum_expr([n, s]))) return (obj, constraints)
def graph_implementation(arg_objs, size, data=None): # min 1-sqrt(2z-z^2) # s.t. x>=0, z<=1, z = x+s, s<=0 x = arg_objs[0] z = lu.create_var(size) s = lu.create_var(size) zeros = lu.create_const(np.mat(np.zeros(size)), size) ones = lu.create_const(np.mat(np.ones(size)), size) z2, constr_square = power.graph_implementation( [z], size, (2, (Fraction(1, 2), Fraction(1, 2)))) two_z = lu.sum_expr([z, z]) sub = lu.sub_expr(two_z, z2) sq, constr_sqrt = power.graph_implementation( [sub], size, (Fraction(1, 2), (Fraction(1, 2), Fraction(1, 2)))) obj = lu.sub_expr(ones, sq) constr = [lu.create_eq(z, lu.sum_expr([x, s]))] + [ lu.create_leq(zeros, x) ] + [lu.create_leq(z, ones)] + [lu.create_leq(s, zeros) ] + constr_square + constr_sqrt return (obj, constr)
def graph_implementation(arg_objs,size,data=None): x = arg_objs[0] beta,x0 = data[0],data[1] beta_val,x0_val = beta.value,x0.value if isinstance(beta,Parameter): beta = lu.create_param(beta,(1,1)) else: beta = lu.create_const(beta.value,(1,1)) if isinstance(x0,Parameter): x0 = lu.create_param(x0,(1,1)) else: x0 = lu.create_const(x0.value,(1,1)) xi,psi = lu.create_var(size),lu.create_var(size) one = lu.create_const(1,(1,1)) one_over_beta = lu.create_const(1/beta_val,(1,1)) k = np.exp(-beta_val*x0_val) k = lu.create_const(k,(1,1)) # 1/beta * (1 - exp(-beta*(xi+x0))) xi_plus_x0 = lu.sum_expr([xi,x0]) minus_beta_times_xi_plus_x0 = lu.neg_expr(lu.mul_expr(beta,xi_plus_x0,size)) exp_xi,constr_exp = exp.graph_implementation([minus_beta_times_xi_plus_x0],size) minus_exp_minus_etc = lu.neg_expr(exp_xi) left_branch = lu.mul_expr(one_over_beta, lu.sum_expr([one,minus_exp_minus_etc]),size) # psi*exp(-beta*r0) right_branch = lu.mul_expr(k,psi,size) obj = lu.sum_expr([left_branch,right_branch]) #x-x0 == xi + psi, xi >= 0, psi <= 0 zero = lu.create_const(0,size) constraints = constr_exp prom_x0 = lu.promote(x0, size) constraints.append(lu.create_eq(x,lu.sum_expr([prom_x0,xi,psi]))) constraints.append(lu.create_geq(xi,zero)) constraints.append(lu.create_leq(psi,zero)) return (obj, constraints)
def format(self, eq_constr, leq_constr, dims, solver): """Formats SDP constraints as inequalities for the solver. Parameters ---------- eq_constr : list A list of the equality constraints in the canonical problem. leq_constr : list A list of the inequality constraints in the canonical problem. dims : dict A dict with the dimensions of the conic constraints. solver : str The solver being called. """ # A == A.T eq_constr.append(lu.create_eq(self.A, lu.transpose(self.A))) # 0 <= A leq_constr.append(lu.create_geq(self.A)) # Update dims. dims[s.EQ_DIM] += self.size[0]*self.size[1] dims[s.SDP_DIM].append(self.size[0])
def format(self, solver): """Formats EXP constraints for the solver. Parameters ---------- solver : str The solver targetted. """ # Need x, y, z to be lone Variables. if solver == s.CVXOPT: constraints = [] for i, var in enumerate(self.vars_): if not var.type is VARIABLE: lone_var = lu.create_var(var.size) constraints.append(lu.create_eq(lone_var, var)) self.vars_[i] = lone_var return constraints # Converts to an inequality constraint. elif solver == s.SCS: return format_elemwise([self.x, self.y, self.z]) else: raise TypeError("Solver does not support exponential cone.")
def affine(self, expr): """Extract A, b from an expression that is reducable to A*x + b. Parameters ---------- expr : Expression The expression to process. Returns ------- SciPy CSR matrix The coefficient matrix A of shape (np.prod(expr.shape), self.N). NumPy.ndarray The offset vector b of shape (np.prod(expr.shape,)). """ if not expr.is_affine(): raise ValueError("Expression is not affine") s, _ = expr.canonical_form V, I, J, b = canonInterface.get_problem_matrix([lu.create_eq(s)], self.id_map) A = sp.csr_matrix((V, (I, J)), shape=(expr.size, self.N)) return A, b.flatten()
def _coeffs_affine_atom(self, expr): sz = expr.size[0] * expr.size[1] Ps = [sp.lil_matrix((self.N, self.N)) for i in range(sz)] Q = sp.lil_matrix((sz, self.N)) Parg = None Qarg = None Rarg = None fake_args = [] offsets = {} offset = 0 for idx, arg in enumerate(expr.args): if arg.is_constant(): fake_args += [lu.create_const(arg.value, arg.size)] else: if Parg is None: (Parg, Qarg, Rarg) = self.get_coeffs(arg) else: (p, q, r) = self.get_coeffs(arg) Parg += p Qarg = sp.vstack([Qarg, q]) Rarg = np.concatenate([Rarg, r]) fake_args += [lu.create_var(arg.size, idx)] offsets[idx] = offset offset += arg.size[0] * arg.size[1] fake_expr, _ = expr.graph_implementation(fake_args, expr.size, expr.get_data()) # Get the matrix representation of the function. V, I, J, R = canonInterface.get_problem_matrix( [lu.create_eq(fake_expr)], offsets) R = R.flatten() # return "AX+b" for (v, i, j) in zip(V, I.astype(int), J.astype(int)): Ps[i] += v * Parg[j] Q[i, :] += v * Qarg[j, :] R[i] += v * Rarg[j] Ps = [P.tocsr() for P in Ps] return (Ps, Q.tocsr(), R)
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. minimize n^2 + 2M|s| subject to s + n = x Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ M = data x = arg_objs[0] n = lu.create_var(size) s = lu.create_var(size) two = lu.create_const(2, (1, 1)) if isinstance(M, Parameter): M = lu.create_param(M, (1, 1)) else: # M is constant. M = lu.create_const(M.value, (1, 1)) # n**2 + 2*M*|s| n2, constr_sq = square.graph_implementation([n], size) abs_s, constr_abs = abs.graph_implementation([s], size) M_abs_s = lu.mul_expr(M, abs_s, size) obj = lu.sum_expr([n2, lu.mul_expr(two, M_abs_s, size)]) # x == s + n constraints = constr_sq + constr_abs constraints.append(lu.create_eq(x, lu.sum_expr([n, s]))) return (obj, constraints)
def _coeffs_affine_atom(self, expr): sz = expr.size[0]*expr.size[1] Ps = [sp.lil_matrix((self.N, self.N)) for i in range(sz)] Q = sp.lil_matrix((sz, self.N)) Parg = None Qarg = None Rarg = None fake_args = [] offsets = {} offset = 0 for idx, arg in enumerate(expr.args): if arg.is_constant(): fake_args += [lu.create_const(arg.value, arg.size)] else: if Parg is None: (Parg, Qarg, Rarg) = self.get_coeffs(arg) else: (p, q, r) = self.get_coeffs(arg) Parg += p Qarg = sp.vstack([Qarg, q]) Rarg = np.vstack([Rarg, r]) fake_args += [lu.create_var(arg.size, idx)] offsets[idx] = offset offset += arg.size[0]*arg.size[1] fake_expr, _ = expr.graph_implementation(fake_args, expr.size, expr.get_data()) # Get the matrix representation of the function. V, I, J, R = canonInterface.get_problem_matrix([lu.create_eq(fake_expr)], offsets) R = R.flatten() # return "AX+b" for (v, i, j) in zip(V, I.astype(int), J.astype(int)): Ps[i] += v*Parg[j] Q[i, :] += v*Qarg[j, :] R[i] += v*Rarg[j] Ps = [P.tocsr() for P in Ps] return (Ps, Q.tocsr(), R)
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. Creates the equivalent problem:: maximize sum(log(D[i, i])) subject to: D diagonal diag(D) = diag(Z) Z is upper triangular. [D Z; Z.T A] is positive semidefinite The problem computes the LDL factorization: .. math:: A = (Z^TD^{-1})D(D^{-1}Z) This follows from the inequality: .. math:: \det(A) >= \det(D) + \det([D, Z; Z^T, A])/\det(D) >= \det(D) because (Z^TD^{-1})D(D^{-1}Z) is a feasible D, Z that achieves det(A) = det(D) and the objective maximizes det(D). Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ A = arg_objs[0] # n by n matrix. n, _ = A.size X = lu.create_var((2 * n, 2 * n)) Z = lu.create_var((n, n)) D = lu.create_var((n, n)) # Require that X is symmetric (which implies # A is symmetric). # X == X.T obj, constraints = transpose.graph_implementation([X], (n, n)) constraints.append(lu.create_eq(X, obj)) # Require that X and A are PSD. constraints += [SDP(X), SDP(A)] # Fix Z as upper triangular, D as diagonal, # and diag(D) as diag(Z). for i in xrange(n): for j in xrange(n): if i == j: # D[i, j] == Z[i, j] Dij = index.get_index(D, constraints, i, j) Zij = index.get_index(Z, constraints, i, j) constraints.append(lu.create_eq(Dij, Zij)) if i != j: # D[i, j] == 0 Dij = index.get_index(D, constraints, i, j) constraints.append(lu.create_eq(Dij)) if i > j: # Z[i, j] == 0 Zij = index.get_index(Z, constraints, i, j) constraints.append(lu.create_eq(Zij)) # Fix X using the fact that A must be affine by the DCP rules. # X[0:n, 0:n] == D index.block_eq(X, D, constraints, 0, n, 0, n) # X[0:n, n:2*n] == Z, index.block_eq(X, Z, constraints, 0, n, n, 2 * n) # X[n:2*n, n:2*n] == A index.block_eq(X, A, constraints, n, 2 * n, n, 2 * n) # Add the objective sum(log(D[i, i]) log_diag = [] for i in xrange(n): Dii = index.get_index(D, constraints, i, i) obj, constr = log.graph_implementation([Dii], (1, 1)) constraints += constr log_diag.append(obj) obj = lu.sum_expr(log_diag) return (obj, constraints)
def _dummy_constr(self): """Returns a dummy constraint for the objective. """ return [lu.create_eq(self.sym_data.objective)]
def _get_eq_constr(self): """Returns the equality constraints for the SDP constraint. """ upper_tri = lu.upper_tri(self.A) lower_tri = lu.upper_tri(lu.transpose(self.A)) return lu.create_eq(upper_tri, lower_tri)
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. Creates the equivalent problem:: maximize sum(log(D[i, i])) subject to: D diagonal diag(D) = diag(Z) Z is upper triangular. [D Z; Z.T A] is positive semidefinite The problem computes the LDL factorization: .. math:: A = (Z^TD^{-1})D(D^{-1}Z) This follows from the inequality: .. math:: \det(A) >= \det(D) + \det([D, Z; Z^T, A])/\det(D) >= \det(D) because (Z^TD^{-1})D(D^{-1}Z) is a feasible D, Z that achieves det(A) = det(D) and the objective maximizes det(D). Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ A = arg_objs[0] # n by n matrix. n, _ = A.size X = lu.create_var((2 * n, 2 * n)) X, constraints = Semidef(2 * n).canonical_form Z = lu.create_var((n, n)) D = lu.create_var((n, 1)) # Require that X and A are PSD. constraints += [SDP(A)] # Fix Z as upper triangular, D as diagonal, # and diag(D) as diag(Z). Z_lower_tri = lu.upper_tri(lu.transpose(Z)) constraints.append(lu.create_eq(Z_lower_tri)) # D[i, i] = Z[i, i] constraints.append(lu.create_eq(D, lu.diag_mat(Z))) # Fix X using the fact that A must be affine by the DCP rules. # X[0:n, 0:n] == D index.block_eq(X, lu.diag_vec(D), constraints, 0, n, 0, n) # X[0:n, n:2*n] == Z, index.block_eq(X, Z, constraints, 0, n, n, 2 * n) # X[n:2*n, n:2*n] == A index.block_eq(X, A, constraints, n, 2 * n, n, 2 * n) # Add the objective sum(log(D[i, i]) obj, constr = log.graph_implementation([D], (n, 1)) return (lu.sum_entries(obj), constraints + constr)
def graph_implementation(arg_objs, size, data=None): r"""Reduces the atom to an affine expression and list of constraints. Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) Notes ----- Implementation notes. - For general :math:`p \geq 1`, the inequality :math:`\|x\|_p \leq t` is equivalent to the following convex inequalities: .. math:: |x_i| &\leq r_i^{1/p} t^{1 - 1/p}\\ \sum_i r_i &= t. These inequalities happen to also be correct for :math:`p = +\infty`, if we interpret :math:`1/\infty` as :math:`0`. - For general :math:`0 < p < 1`, the inequality :math:`\|x\|_p \geq t` is equivalent to the following convex inequalities: .. math:: r_i &\leq x_i^{p} t^{1 - p}\\ \sum_i r_i &= t. - For general :math:`p < 0`, the inequality :math:`\|x\|_p \geq t` is equivalent to the following convex inequalities: .. math:: t &\leq x_i^{-p/(1-p)} r_i^{1/(1 - p)}\\ \sum_i r_i &= t. Although the inequalities above are correct, for a few special cases, we can represent the p-norm more efficiently and with fewer variables and inequalities. - For :math:`p = 1`, we use the representation .. math:: x_i &\leq r_i\\ -x_i &\leq r_i\\ \sum_i r_i &= t - For :math:`p = \infty`, we use the representation .. math:: x_i &\leq t\\ -x_i &\leq t Note that we don't need the :math:`r` variable or the sum inequality. - For :math:`p = 2`, we use the natural second-order cone representation .. math:: \|x\|_2 \leq t Note that we could have used the set of inequalities given above if we wanted an alternate decomposition of a large second-order cone into into several smaller inequalities. """ p = data[0] x = arg_objs[0] t = lu.create_var((1, 1)) constraints = [] # first, take care of the special cases of p = 2, inf, and 1 if p == 2: return t, [SOC(t, [x])] if p == np.inf: t_ = lu.promote(t, x.size) return t, [lu.create_leq(x, t_), lu.create_geq(lu.sum_expr([x, t_]))] # we need an absolute value constraint for the symmetric convex branches (p >= 1) # we alias |x| as x from this point forward to make the code pretty :) if p >= 1: absx = lu.create_var(x.size) constraints += [lu.create_leq(x, absx), lu.create_geq(lu.sum_expr([x, absx]))] x = absx if p == 1: return lu.sum_entries(x), constraints # now, we take care of the remaining convex and concave branches # to create the rational powers, we need a new variable, r, and # the constraint sum(r) == t r = lu.create_var(x.size) t_ = lu.promote(t, x.size) constraints += [lu.create_eq(lu.sum_entries(r), t)] # make p a fraction so that the input weight to gm_constrs # is a nice tuple of fractions. p = Fraction(p) if p < 0: constraints += gm_constrs(t_, [x, r], (-p / (1 - p), 1 / (1 - p))) if 0 < p < 1: constraints += gm_constrs(r, [x, t_], (p, 1 - p)) if p > 1: constraints += gm_constrs(x, [r, t_], (1 / p, 1 - 1 / p)) return t, constraints
def graph_implementation(arg_objs, size, data=None): """Reduces the atom to an affine expression and list of constraints. Creates the equivalent problem:: maximize sum(log(D[i, i])) subject to: D diagonal diag(D) = diag(Z) Z is upper triangular. [D Z; Z.T A] is positive semidefinite The problem computes the LDL factorization: .. math:: A = (Z^TD^{-1})D(D^{-1}Z) This follows from the inequality: .. math:: \det(A) >= \det(D) + \det([D, Z; Z^T, A])/\det(D) >= \det(D) because (Z^TD^{-1})D(D^{-1}Z) is a feasible D, Z that achieves det(A) = det(D) and the objective maximizes det(D). Parameters ---------- arg_objs : list LinExpr for each argument. size : tuple The size of the resulting expression. data : Additional data required by the atom. Returns ------- tuple (LinOp for objective, list of constraints) """ A = arg_objs[0] # n by n matrix. n, _ = A.size X = lu.create_var((2*n, 2*n)) Z = lu.create_var((n, n)) D = lu.create_var((n, 1)) # Require that X and A are PSD. constraints = [SDP(X), SDP(A)] # Fix Z as upper triangular, D as diagonal, # and diag(D) as diag(Z). Z_lower_tri = lu.upper_tri(lu.transpose(Z)) constraints.append(lu.create_eq(Z_lower_tri)) # D[i, i] = Z[i, i] constraints.append(lu.create_eq(D, lu.diag_mat(Z))) # Fix X using the fact that A must be affine by the DCP rules. # X[0:n, 0:n] == D index.block_eq(X, lu.diag_vec(D), constraints, 0, n, 0, n) # X[0:n, n:2*n] == Z, index.block_eq(X, Z, constraints, 0, n, n, 2*n) # X[n:2*n, n:2*n] == A index.block_eq(X, A, constraints, n, 2*n, n, 2*n) # Add the objective sum(log(D[i, i]) obj, constr = log.graph_implementation([D], (n, 1)) return (lu.sum_entries(obj), constraints + constr)