def graph_implementation(self, arg_objs, shape: Tuple[int, ...], data=None): """Reshape 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) """ arg = arg_objs[0] if data[1] == 'F': return (lu.reshape(arg, shape), []) else: # 'C': arg = lu.transpose(arg) if len(shape) <= 1: return (lu.reshape(arg, shape), []) else: result = lu.reshape(arg, (shape[1], shape[0])) return (lu.transpose(result), [])
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 = lu.create_eq(self.A, lu.transpose(self.A)) leq_constr = lu.create_geq(self.A) return ([eq_constr], [leq_constr])
def format_axis(t, X, axis): """Formats all the row/column cones for the solver. Parameters ---------- t: The scalar part of the second-order constraint. X: A matrix whose rows/columns are each a cone. axis: Slice by column 0 or row 1. Returns ------- list A list of LinLeqConstr that represent all the elementwise cones. """ # Reduce to norms of columns. if axis == 1: X = lu.transpose(X) # Create matrices Tmat, Xmat such that Tmat*t + Xmat*X # gives the format for the elementwise cone constraints. num_cones = t.size[0] cone_size = 1 + X.size[0] terms = [] # Make t_mat mat_size = (cone_size, 1) prod_size = (cone_size, t.size[0]) t_mat = sp.coo_matrix(([1.0], ([0], [0])), mat_size).tocsc() t_mat = lu.create_const(t_mat, mat_size, sparse=True) terms += [lu.mul_expr(t_mat, lu.transpose(t), prod_size)] # Make X_mat mat_size = (cone_size, X.size[0]) prod_size = (cone_size, X.size[1]) val_arr = (cone_size - 1)*[1.0] row_arr = range(1, cone_size) col_arr = range(cone_size-1) X_mat = sp.coo_matrix((val_arr, (row_arr, col_arr)), mat_size).tocsc() X_mat = lu.create_const(X_mat, mat_size, sparse=True) terms += [lu.mul_expr(X_mat, X, prod_size)] return [lu.create_geq(lu.sum_expr(terms))]
def format_axis(t, X, axis): """Formats all the row/column cones for the solver. Parameters ---------- t: The scalar part of the second-order constraint. X: A matrix whose rows/columns are each a cone. axis: Slice by column 0 or row 1. Returns ------- list A list of LinLeqConstr that represent all the elementwise cones. """ # Reduce to norms of columns. if axis == 1: X = lu.transpose(X) # Create matrices Tmat, Xmat such that Tmat*t + Xmat*X # gives the format for the elementwise cone constraints. num_cones = t.size[0] cone_size = 1 + X.size[0] terms = [] # Make t_mat mat_size = (cone_size, 1) prod_size = (cone_size, t.size[0]) t_mat = sp.coo_matrix(([1.0], ([0], [0])), mat_size).tocsc() t_mat = lu.create_const(t_mat, mat_size, sparse=True) terms += [lu.mul_expr(t_mat, lu.transpose(t), prod_size)] # Make X_mat mat_size = (cone_size, X.size[0]) prod_size = (cone_size, X.size[1]) val_arr = (cone_size - 1) * [1.0] row_arr = range(1, cone_size) col_arr = range(cone_size - 1) X_mat = sp.coo_matrix((val_arr, (row_arr, col_arr)), mat_size).tocsc() X_mat = lu.create_const(X_mat, mat_size, sparse=True) terms += [lu.mul_expr(X_mat, X, prod_size)] return [lu.create_geq(lu.sum_expr(terms))]
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 half = lu.create_const(0.5, (1, 1)) symm = lu.mul_expr(half, lu.sum_expr([obj, lu.transpose(obj)]), obj.size) dual_holder = SDP(symm, enforce_sym=False, constr_id=self.id) return (None, constraints + [dual_holder])
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 half = lu.create_const(0.5, (1,1)) symm = lu.mul_expr(half, lu.sum_expr([obj, lu.transpose(obj)]), obj.size) dual_holder = SDP(symm, enforce_sym=False, constr_id=self.id) return (None, constraints + [dual_holder])
def format_axis(t, X, axis): """Formats all the row/column cones for the solver. Parameters ---------- t: The scalar part of the second-order constraint. X: A matrix whose rows/columns are each a cone. axis: Slice by column 0 or row 1. Returns ------- list A list of LinLeqConstr that represent all the elementwise cones. """ # Reduce to norms of columns. if axis == 1: X = lu.transpose(X) # Create matrices Tmat, Xmat such that Tmat*t + Xmat*X # gives the format for the elementwise cone constraints. cone_size = 1 + X.shape[0] terms = [] # Make t_mat mat_shape = (cone_size, 1) t_mat = sp.csc_matrix(([1.0], ([0], [0])), mat_shape) t_mat = lu.create_const(t_mat, mat_shape, sparse=True) t_vec = t if not t.shape: # t is scalar t_vec = lu.reshape(t, (1, 1)) else: # t is 1D t_vec = lu.reshape(t, (1, t.shape[0])) mul_shape = (cone_size, t_vec.shape[1]) terms += [lu.mul_expr(t_mat, t_vec, mul_shape)] # Make X_mat if len(X.shape) == 1: X = lu.reshape(X, (X.shape[0], 1)) mat_shape = (cone_size, X.shape[0]) val_arr = (cone_size - 1) * [1.0] row_arr = list(range(1, cone_size)) col_arr = list(range(cone_size - 1)) X_mat = sp.csc_matrix((val_arr, (row_arr, col_arr)), mat_shape) X_mat = lu.create_const(X_mat, mat_shape, sparse=True) mul_shape = (cone_size, X.shape[1]) terms += [lu.mul_expr(X_mat, X, mul_shape)] return [lu.create_geq(lu.sum_expr(terms))]
def graph_implementation(arg_objs, size, data=None): """Create a new variable equal to the argument transposed. 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) """ return (lu.transpose(arg_objs[0]), [])
def graph_implementation(arg_objs, size, data=None): """Create a new variable equal to the argument transposed. 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) """ return lu.transpose(arg_objs[0])
def graph_implementation(self, arg_objs, shape, data=None): """Create a new variable equal to the argument transposed. 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) """ # TODO(akshakya): This will need to be updated when we add support # for >2D ararys. return (lu.transpose(arg_objs[0]), [])
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 _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)) 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)
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)