Example #1
0
    def solve(self, objective, constraints, cached_data, warm_start, verbose,
              solver_opts):
        """Returns the result of the call to the solver.

        Parameters
        ----------
        objective : CVXPY objective object
            Raw objective passed by CVXPY. Can be convex/concave.
        constraints : list
            The list of raw constraints.

        Returns
        -------
        tuple
            (status, optimal value, primal, equality dual, inequality dual)
        """

        sym_data = self.get_sym_data(objective, constraints)

        id_map = sym_data.var_offsets
        N = sym_data.x_length

        extractor = CoeffExtractor(id_map, N)

        # Extract the coefficients
        (Ps, Q, R) = extractor.get_coeffs(objective.args[0])

        P = Ps[0]
        q = np.asarray(Q.todense()).flatten()
        r = R[0]

        # Forming the KKT system
        if len(constraints) > 0:
            Cs = [extractor.get_coeffs(c._expr)[1:] for c in constraints]
            As = sp.vstack([C[0] for C in Cs])
            bs = np.array([C[1] for C in Cs]).flatten()
            lhs = sp.bmat([[2 * P, As.transpose()], [As, None]], format='csr')
            rhs = np.concatenate([-q, -bs])
        else:  # avoiding calling vstack with empty list
            lhs = 2 * P
            rhs = -q

        warnings.filterwarnings('error')

        # Actually solving the KKT system
        try:
            sol = SLA.spsolve(lhs.tocsr(), rhs)
            x = np.array(sol[:N])
            nu = np.array(sol[N:])
            p_star = np.dot(x.transpose(), P * x + q) + r
        except SLA.MatrixRankWarning:
            x = None
            nu = None
            p_star = None

        warnings.resetwarnings()

        result_dict = {s.PRIMAL: x, s.EQ_DUAL: nu, s.VALUE: p_star}

        return self.format_results(result_dict, None, cached_data)
Example #2
0
    def apply(self, problem):
        """See docstring for MatrixStuffing.apply"""
        inverse_data = InverseData(problem)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        params_to_P, params_to_q, flattened_variable = self.stuffed_objective(
            problem, extractor)
        # Lower equality and inequality to Zero and NonPos.
        cons = []
        for con in problem.constraints:
            if isinstance(con, Equality):
                con = lower_equality(con)
            elif isinstance(con, Inequality):
                con = lower_ineq_to_nonpos(con)
            cons.append(con)

        # Reorder constraints to Zero, NonPos.
        constr_map = group_constraints(cons)
        ordered_cons = constr_map[Zero] + constr_map[NonPos]
        inverse_data.cons_id_map = {con.id: con.id for con in ordered_cons}

        inverse_data.constraints = ordered_cons
        # Batch expressions together, then split apart.
        expr_list = [arg for c in ordered_cons for arg in c.args]
        params_to_Ab = extractor.affine(expr_list)

        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = ParamQuadProg(params_to_P, params_to_q,
                                 flattened_variable, params_to_Ab,
                                 problem.variables(), inverse_data.var_offsets,
                                 ordered_cons, problem.parameters(),
                                 inverse_data.param_id_map)
        return new_prob, inverse_data
Example #3
0
    def stuffed_objective(self, problem, inverse_data):
        extractor = CoeffExtractor(inverse_data)
        # Extract to c.T * x, store r
        C, R = extractor.get_coeffs(problem.objective.expr)

        c = np.asarray(C.todense()).flatten()
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(inverse_data.x_length, boolean=boolean, integer=integer)

        new_obj = c.T * x + 0

        inverse_data.r = R[0]
        return new_obj, x
Example #4
0
    def apply(self, problem):
        inverse_data = InverseData(problem)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        params_to_objective, flattened_variable = self.stuffed_objective(
            problem, extractor)
        # Lower equality and inequality to Zero and NonNeg.
        cons = []
        for con in problem.constraints:
            if isinstance(con, Equality):
                con = lower_equality(con)
            elif isinstance(con, Inequality):
                con = lower_ineq_to_nonneg(con)
            elif isinstance(con, NonPos):
                con = nonpos2nonneg(con)
            elif isinstance(con, SOC) and con.axis == 1:
                con = SOC(con.args[0], con.args[1].T, axis=0,
                          constr_id=con.constr_id)
            elif isinstance(con, PowCone3D) and con.args[0].ndim > 1:
                x, y, z = con.args
                alpha = con.alpha
                con = PowCone3D(x.flatten(), y.flatten(), z.flatten(), alpha.flatten(),
                                constr_id=con.constr_id)
            elif isinstance(con, ExpCone) and con.args[0].ndim > 1:
                x, y, z = con.args
                con = ExpCone(x.flatten(), y.flatten(), z.flatten(),
                              constr_id=con.constr_id)
            cons.append(con)
        # Reorder constraints to Zero, NonNeg, SOC, PSD, EXP, PowCone3D
        constr_map = group_constraints(cons)
        ordered_cons = constr_map[Zero] + constr_map[NonNeg] + \
            constr_map[SOC] + constr_map[PSD] + constr_map[ExpCone] + constr_map[PowCone3D]
        inverse_data.cons_id_map = {con.id: con.id for con in ordered_cons}

        inverse_data.constraints = ordered_cons
        # Batch expressions together, then split apart.
        expr_list = [arg for c in ordered_cons for arg in c.args]
        params_to_problem_data = extractor.affine(expr_list)

        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = ParamConeProg(params_to_objective,
                                 flattened_variable,
                                 params_to_problem_data,
                                 problem.variables(),
                                 inverse_data.var_offsets,
                                 ordered_cons,
                                 problem.parameters(),
                                 inverse_data.param_id_map)
        return new_prob, inverse_data
Example #5
0
    def stuffed_objective(self, problem, inverse_data):
        # We need to copy the problem because we are changing atoms in the
        # expression tree
        problem_copy = problems.problem.Problem(
            Minimize(problem.objective.expr.tree_copy()),
            [con.tree_copy() for con in problem.constraints])
        inverse_data_of_copy = InverseData(problem_copy)
        extractor = CoeffExtractor(inverse_data_of_copy)
        # extract to x.T * P * x + q.T * x, store r
        P, q, r = extractor.quad_form(problem_copy.objective.expr)

        # concatenate all variables in one vector
        boolean, integer = extract_mip_idx(problem.variables())
        x = Variable(inverse_data.x_length, boolean=boolean, integer=integer)
        new_obj = QuadForm(x, P) + q.T * x

        inverse_data.r = r
        return new_obj, x
Example #6
0
    def apply(self, problem):
        inverse_data = InverseData(problem)

        new_obj, new_var = self.stuffed_objective(problem, inverse_data)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        new_cons = []
        for con in problem.constraints:
            arg_list = []
            for arg in con.args:
                A, b = extractor.get_coeffs(arg)
                arg_list.append(reshape(A * new_var + b, arg.shape))
            new_cons.append(con.copy(arg_list))
            inverse_data.cons_id_map[con.id] = new_cons[-1].id

        # Map of old constraint id to new constraint id.
        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = problems.problem.Problem(Minimize(new_obj), new_cons)
        return new_prob, inverse_data
Example #7
0
    def apply(self, problem):
        """See docstring for MatrixStuffing.apply"""
        inverse_data = InverseData(problem)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        new_obj, new_var, r = self.stuffed_objective(problem, extractor)
        inverse_data.r = r
        # Lower equality and inequality to Zero and NonPos.
        cons = []
        for con in problem.constraints:
            if isinstance(con, Equality):
                con = lower_equality(con)
            elif isinstance(con, Inequality):
                con = lower_inequality(con)
            cons.append(con)

        # Batch expressions together, then split apart.
        expr_list = [arg for c in cons for arg in c.args]
        problem_data_tensor = extractor.affine(expr_list)
        Afull, bfull = canon.get_matrix_and_offset_from_unparameterized_tensor(
            problem_data_tensor, new_var.size)
        if 0 not in Afull.shape and 0 not in bfull.shape:
            Afull = cvxtypes.constant()(Afull)
            bfull = cvxtypes.constant()(np.atleast_1d(bfull))

        new_cons = []
        offset = 0
        for orig_con, con in zip(problem.constraints, cons):
            arg_list = []
            for arg in con.args:
                A = Afull[offset:offset + arg.size, :]
                b = bfull[offset:offset + arg.size]
                arg_list.append(reshape(A @ new_var + b, arg.shape))
                offset += arg.size
            new_constraint = con.copy(arg_list)
            new_cons.append(new_constraint)

        inverse_data.constraints = new_cons
        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = problems.problem.Problem(Minimize(new_obj), new_cons)
        return new_prob, inverse_data
Example #8
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)
Example #9
0
    def apply(self, problem):
        inverse_data = InverseData(problem)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        new_obj, new_var, r = self.stuffed_objective(problem, extractor)
        inverse_data.r = r
        # Lower equality and inequality to Zero and NonPos.
        cons = []
        for con in problem.constraints:
            if isinstance(con, Equality):
                con = lower_equality(con)
            elif isinstance(con, Inequality):
                con = lower_inequality(con)
            elif isinstance(con, SOC) and con.axis == 1:
                con = SOC(con.args[0],
                          con.args[1].T,
                          axis=0,
                          constr_id=con.constr_id)
            cons.append(con)

        # Batch expressions together, then split apart.
        expr_list = [arg for con in cons for arg in con.args]
        Afull, bfull = extractor.affine(expr_list)
        new_cons = []
        offset = 0
        for con in cons:
            arg_list = []
            for arg in con.args:
                A = Afull[offset:offset + arg.size, :]
                b = bfull[offset:offset + arg.size]
                arg_list.append(reshape(A * new_var + b, arg.shape))
                offset += arg.size
            new_cons.append(con.copy(arg_list))
            inverse_data.cons_id_map[con.id] = new_cons[-1].id

        # Map of old constraint id to new constraint id.
        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = problems.problem.Problem(Minimize(new_obj), new_cons)
        return new_prob, inverse_data
Example #10
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
Example #11
0
    def apply(self, problem):
        """Returns a stuffed problem.

        The returned problem is a minimization problem in which every
        constraint in the problem has affine arguments that are expressed in
        the form A @ x + b.


        Parameters
        ----------
        problem: The problem to stuff; the arguments of every constraint
            must be affine
        constraints: A list of constraints, whose arguments are affine

        Returns
        -------
        Problem
            The stuffed problem
        InverseData
            Data for solution retrieval
        """
        inverse_data = InverseData(problem)
        # Form the constraints
        extractor = CoeffExtractor(inverse_data)
        new_obj, new_var, r = self.stuffed_objective(problem, extractor)
        inverse_data.r = r
        # Lower equality and inequality to Zero and NonPos.
        cons = []
        for con in problem.constraints:
            if isinstance(con, Equality):
                con = lower_equality(con)
            elif isinstance(con, Inequality):
                con = lower_inequality(con)
            elif isinstance(con, SOC) and con.axis == 1:
                con = SOC(con.args[0], con.args[1].T, axis=0,
                          constr_id=con.constr_id)
            cons.append(con)

        # Batch expressions together, then split apart.
        expr_list = [arg for c in cons for arg in c.args]
        Afull, bfull = extractor.affine(expr_list)
        if 0 not in Afull.shape and 0 not in bfull.shape:
            Afull = cvxtypes.constant()(Afull)
            bfull = cvxtypes.constant()(bfull)

        new_cons = []
        offset = 0
        for con in cons:
            arg_list = []
            for arg in con.args:
                A = Afull[offset:offset+arg.size, :]
                b = bfull[offset:offset+arg.size]
                arg_list.append(reshape(A*new_var + b, arg.shape))
                offset += arg.size
            new_cons.append(con.copy(arg_list))
            # Map old constraint id to new constraint id.
            inverse_data.cons_id_map[con.id] = new_cons[-1].id

        inverse_data.minimize = type(problem.objective) == Minimize
        new_prob = problems.problem.Problem(Minimize(new_obj), new_cons)
        return new_prob, inverse_data