예제 #1
0
    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)
예제 #2
0
    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
예제 #3
0
    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
    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
예제 #5
0
    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)
예제 #6
0
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
예제 #7
0
    def extract_quadratic_coeffs(self, affine_expr, quad_forms):
        """ Assumes quadratic forms all have variable arguments.
            Affine expressions can be anything.
        """
        assert affine_expr.is_dpp()
        # Here we take the problem objective, replace all the SymbolicQuadForm
        # atoms with variables of the same dimensions.
        # We then apply the canonInterface to reduce the "affine head"
        # of the expression tree to a coefficient vector c and constant offset d.
        # Because the expression is parameterized, we extend that to a matrix
        # [c1 c2 ...]
        # [d1 d2 ...]
        # where ci,di are the vector and constant for the ith parameter.
        affine_id_map, affine_offsets, x_length, affine_var_shapes = \
            InverseData.get_var_offsets(affine_expr.variables())
        op_list = [affine_expr.canonical_form[0]]
        param_coeffs = canonInterface.get_problem_matrix(
            op_list, x_length, affine_offsets, self.param_to_size,
            self.param_id_map, affine_expr.size)

        # Iterates over every entry of the parameters vector,
        # and obtains the Pi and qi for that entry i.
        # These are then combined into matrices [P1.flatten(), P2.flatten(), ...]
        # and [q1, q2, ...]
        constant = param_coeffs[-1, :]
        c = param_coeffs[:-1, :].A

        # coeffs stores the P and q for each quad_form,
        # as well as for true variable nodes in the objective.
        coeffs = {}
        # The goal of this loop is to appropriately multiply
        # the matrix P of each quadratic term by the coefficients
        # in param_coeffs. Later we combine all the quadratic terms
        # to form a single matrix P.
        for var in affine_expr.variables():
            # quad_forms maps the ids of the SymbolicQuadForm atoms
            # in the objective to (modified parent node of quad form,
            #                      argument index of quad form,
            #                      quad form atom)
            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]
                c_part = c[var_offset:var_offset + var_size, :]
                if quad_forms[var_id][2].P.value is not None:
                    # Convert to sparse matrix.
                    P = quad_forms[var_id][2].P.value
                    if sp.issparse(P) and not isinstance(P, sp.coo_matrix):
                        P = P.tocoo()
                    else:
                        P = sp.coo_matrix(P)
                    if var_size == 1:
                        c_part = np.ones((P.shape[0], 1)) * c_part
                else:
                    P = sp.eye(var_size, format='coo')
                # We multiply the columns of P, by c_part
                # by operating directly on the data.
                data = P.data[:, None] * c_part[P.col]
                P_tup = (data, (P.row, P.col), P.shape)
                # Conceptually similar to
                # P = P[:, :, None] * c_part[None, :, :]
                if orig_id in coeffs:
                    if 'P' in coeffs[orig_id]:
                        # Concatenation becomes addition when constructing
                        # COO matrix because repeated indices are summed.
                        # Conceptually equivalent to
                        # coeffs[orig_id]['P'] += P_tup
                        acc_data, (acc_row, acc_col), _ = coeffs[orig_id]['P']
                        acc_data = np.concatenate([acc_data, data], axis=0)
                        acc_row = np.concatenate([acc_row, P.row], axis=0)
                        acc_col = np.concatenate([acc_col, P.col], axis=0)
                        P_tup = (acc_data, (acc_row, acc_col), P.shape)
                        coeffs[orig_id]['P'] = P_tup
                    else:
                        coeffs[orig_id]['P'] = P_tup
                else:
                    coeffs[orig_id] = dict()
                    coeffs[orig_id]['P'] = P_tup
                    coeffs[orig_id]['q'] = np.zeros((P.shape[0], c.shape[1]))
            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]['q'] += c[var_offset:var_offset +
                                             var_size, :]
                else:
                    coeffs[var.id] = dict()
                    coeffs[var.id]['q'] = c[var_offset:var_offset +
                                            var_size, :]
        return coeffs, constant
예제 #8
0
    def extract_quadratic_coeffs(self, affine_expr, quad_forms):
        """ Assumes quadratic forms all have variable arguments.
            Affine expressions can be anything.
        """
        assert affine_expr.is_dpp()
        # Here we take the problem objective, replace all the SymbolicQuadForm
        # atoms with variables of the same dimensions.
        # We then apply the canonInterface to reduce the "affine head"
        # of the expression tree to a coefficient vector c and constant offset d.
        # Because the expression is parameterized, we extend that to a matrix
        # [c1 c2 ...]
        # [d1 d2 ...]
        # where ci,di are the vector and constant for the ith parameter.
        affine_id_map, affine_offsets, x_length, affine_var_shapes = \
            InverseData.get_var_offsets(affine_expr.variables())
        op_list = [affine_expr.canonical_form[0]]
        param_coeffs = canonInterface.get_problem_matrix(
            op_list, x_length, affine_offsets, self.param_to_size,
            self.param_id_map, affine_expr.size)

        # TODO vectorize this code.
        # Iterates over every entry of the parameters vector,
        # and obtains the Pi and qi for that entry i.
        # These are then combined into matrices [P1.flatten(), P2.flatten(), ...]
        # and [q1, q2, ...]
        coeff_list = []
        constant = param_coeffs[-1, :]
        for p in range(param_coeffs.shape[1]):
            c = param_coeffs[:-1, p].A.flatten()

            # coeffs stores the P and q for each quad_form,
            # as well as for true variable nodes in the objective.
            coeffs = {}
            # The goal of this loop is to appropriately multiply
            # the matrix P of each quadratic term by the coefficients
            # in param_coeffs. Later we combine all the quadratic terms
            # to form a single matrix P.
            for var in affine_expr.variables():
                # quad_forms maps the ids of the SymbolicQuadForm atoms
                # in the objective to (modified parent node of quad form,
                #                      argument index of quad form,
                #                      quad form atom)
                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]
                    c_part = c[var_offset:var_offset + var_size]
                    if quad_forms[var_id][2].P.value is not None:
                        P = quad_forms[var_id][2].P.value
                        if c_part.size == 1:
                            P = c_part[0] * P
                        else:
                            P = P @ sp.diags(c_part)
                    else:
                        P = sp.diags(c_part)
                    if orig_id in coeffs:
                        coeffs[orig_id]['P'] += P
                    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[var_offset:var_offset +
                                                 var_size]
                    else:
                        coeffs[var.id] = dict()
                        coeffs[var.id]['P'] = sp.csr_matrix(
                            (var_size, var_size))
                        coeffs[var.id]['q'] = c[var_offset:var_offset +
                                                var_size]
            coeff_list.append(coeffs)
        return coeff_list, constant