def apply(self, problem): """Recursively canonicalize the objective and every constraint. """ constraints = [] for constr in problem.constraints: constraints += self._canonicalize_constraint(constr) lazy, real = _get_lazy_and_real_constraints(constraints) feas_problem = problems.problem.Problem(Minimize(0), real) feas_problem._lazy_constraints = lazy objective = problem.objective.expr if objective.is_nonneg(): t = Parameter(nonneg=True) elif objective.is_nonpos(): t = Parameter(nonpos=True) else: t = Parameter() constraints += self._canonicalize_constraint(objective <= t) lazy, real = _get_lazy_and_real_constraints(constraints) param_problem = problems.problem.Problem(Minimize(0), real) param_problem._lazy_constraints = lazy param_problem._bisection_data = BisectionData( feas_problem, t, *tighten.tighten_fns(objective)) return param_problem, InverseData(problem)
def extract_quadratic_coeffs(self, affine_expr, quad_forms): """ Assumes quadratic forms all have variable arguments. Affine expressions can be anything. """ # Extract affine data. affine_problem = cvxpy.Problem(Minimize(affine_expr), []) affine_inverse_data = InverseData(affine_problem) affine_id_map = affine_inverse_data.id_map affine_var_shapes = affine_inverse_data.var_shapes extractor = CoeffExtractor(affine_inverse_data) c, b = extractor.affine(affine_problem.objective.expr) # Combine affine data with quadforms. coeffs = {} for var in affine_problem.variables(): if var.id in quad_forms: var_id = var.id orig_id = quad_forms[var_id][2].args[0].id var_offset = affine_id_map[var_id][0] var_size = affine_id_map[var_id][1] if quad_forms[var_id][2].P.value is not None: c_part = c[0, var_offset:var_offset + var_size].toarray().flatten() P = quad_forms[var_id][2].P.value if sp.issparse(P): P = P.toarray() P = c_part * P else: P = sp.diags(c[0, var_offset:var_offset + var_size].toarray().flatten()) if orig_id in coeffs: coeffs[orig_id]['P'] += P coeffs[orig_id]['q'] += np.zeros(P.shape[0]) else: coeffs[orig_id] = dict() coeffs[orig_id]['P'] = P coeffs[orig_id]['q'] = np.zeros(P.shape[0]) else: var_offset = affine_id_map[var.id][0] var_size = np.prod(affine_var_shapes[var.id], dtype=int) if var.id in coeffs: coeffs[var.id]['P'] += sp.csr_matrix((var_size, var_size)) coeffs[var.id]['q'] += c[0, var_offset:var_offset + var_size].toarray().flatten() else: coeffs[var.id] = dict() coeffs[var.id]['P'] = sp.csr_matrix((var_size, var_size)) coeffs[var.id]['q'] = c[0, var_offset:var_offset + var_size].toarray().flatten() return coeffs, b
def apply(self, problem): """Recursively canonicalize the objective and every constraint.""" inverse_data = InverseData(problem) canon_objective, canon_constraints = self.canonicalize_tree( problem.objective) for constraint in problem.constraints: # canon_constr is the constraint rexpressed in terms of # its canonicalized arguments, and aux_constr are the constraints # generated while canonicalizing the arguments of the original # constraint canon_constr, aux_constr = self.canonicalize_tree(constraint) canon_constraints += aux_constr + [canon_constr] inverse_data.cons_id_map.update({constraint.id: canon_constr.id}) new_problem = problems.problem.Problem(canon_objective, canon_constraints) return new_problem, inverse_data
def format_constr(self, problem, constr, exp_cone_order): """Extract coefficient and offset vector from constraint. Special cases PSD constraints, as SCS expects constraints to be imposed on solely the lower triangular part of the variable matrix. Moreover, it requires the off-diagonal coefficients to be scaled by sqrt(2). """ if isinstance(constr, PSD): expr = constr.expr triangularized_expr = scaled_lower_tri(expr + expr.T) / 2 extractor = CoeffExtractor(InverseData(problem)) A_prime, b_prime = extractor.affine(triangularized_expr) # SCS requests constraints to be formatted as # Ax + s = b, where s is constrained to reside in some # cone. Here, however, we are formatting the constraint # as A"x + b" = s = -Ax + b; hence, A = -A", b = b" return -1 * A_prime, b_prime else: return super(SCS, self).format_constr(problem, constr, exp_cone_order)
def psd_coeff_offset(problem, c): """ Returns an array "G" and vector "h" such that the given constraint is equivalent to "G * z <=_{PSD} h". :param problem: the cvxpy Problem in which "c" arises. :param c: a cvxpy Constraint defining a linear matrix inequality "B + \sum_j A[j] * z[j] >=_{PSD} 0". :return: (G, h) such that "c" holds at "z" iff "G * z <=_{PSD} b" (where the PSD cone is reshaped into a subset of R^N with N = dim ** 2). Note: It is desirable to change this mosek interface so that PSD constraints are represented by a vector in R^N with N = (dim * (dim + 1) / 2). This is possible because arguments to a linear matrix inequality are necessarily symmetric. For now we use N = dim ** 2, because it simplifies implementation and only makes a modest difference in the size of the problem seen by mosek. """ extractor = CoeffExtractor(InverseData(problem)) A_vec, b_vec = extractor.affine(c.expr) G = -A_vec h = b_vec dim = c.expr.shape[0] return G, h, dim
def apply(self, problem): inverse_data = InverseData(problem) real2imag = { var.id: lu.get_id() for var in problem.variables() if var.is_complex() } constr_dict = { cons.id: lu.get_id() for cons in problem.constraints if cons.is_complex() } real2imag.update(constr_dict) inverse_data.real2imag = real2imag leaf_map = {} real_obj, imag_obj = self.canonicalize_tree(problem.objective, inverse_data.real2imag, leaf_map) assert imag_obj is None constrs = [] for constraint in problem.constraints: if type(constraint) == Equality: constraint = lower_equality(constraint) elif type(constraint) == Inequality: constraint = lower_inequality(constraint) # real2imag maps variable id to a potential new variable # created for the imaginary part. real_constrs, imag_constrs = self.canonicalize_tree( constraint, inverse_data.real2imag, leaf_map) if real_constrs is not None: constrs.extend(real_constrs) if imag_constrs is not None: constrs.extend(imag_constrs) new_problem = problems.problem.Problem(real_obj, constrs) return new_problem, inverse_data