Exemplo n.º 1
0
def norm_inf_canon(expr, args):
    x = args[0]
    axis = expr.axis
    shape = expr.shape
    t = Variable(shape)

    if axis is None:  # shape = (1, 1)
        promoted_t = promote(t, x.shape)
    elif axis == 0:  # shape = (1, n)
        promoted_t = Constant(np.ones(
            (x.shape[0], 1))) * reshape(t, (1, x.shape[1]))
    else:  # shape = (m, 1)
        promoted_t = reshape(t, (x.shape[0], 1)) * Constant(
            np.ones((1, x.shape[1])))

    return t, [x <= promoted_t, x + promoted_t >= 0]
Exemplo n.º 2
0
def linearize(expr):
    """Returns the tangent approximation to the expression.

    Gives an elementwise lower (upper) bound for convex (concave)
    expressions. No guarantees for non-DCP expressions.

    Returns None if cannot be linearized.

    Args:
        expr: An expression.

    Returns:
        An affine expression or None.
    """
    expr = Constant.cast_to_const(expr)
    if expr.is_affine():
        return expr
    else:
        tangent = expr.value
        if tangent is None:
            raise ValueError(
        "Cannot linearize non-affine expression with missing variable values."
            )
        grad_map = expr.grad
        for var in expr.variables():
            if grad_map[var] is None:
                return None
            elif var.is_matrix():
                flattened = Constant(grad_map[var]).T*vec(var - var.value)
                tangent = tangent + reshape(flattened, *expr.size)
            else:
                tangent = tangent + Constant(grad_map[var]).T*(var - var.value)
        return tangent
Exemplo n.º 3
0
def at_least_2D(expr):
    """Upcast 0D and 1D to 2D.
    """
    if expr.ndim < 2:
        return reshape(expr, (expr.size, 1))
    else:
        return expr
Exemplo n.º 4
0
def linearize(expr):
    """Returns the tangent approximation to the expression.

    Gives an elementwise lower (upper) bound for convex (concave)
    expressions. No guarantees for non-DCP expressions.

    Returns None if cannot be linearized.

    Args:
        expr: An expression.

    Returns:
        An affine expression or None.
    """
    expr = Constant.cast_to_const(expr)
    if expr.is_affine():
        return expr
    else:
        tangent = expr.value
        if tangent is None:
            raise ValueError(
                "Cannot linearize non-affine expression with missing variable values."
            )
        grad_map = expr.grad
        for var in expr.variables():
            if grad_map[var] is None:
                return None
            elif var.is_matrix():
                flattened = Constant(grad_map[var]).T * vec(var - var.value)
                tangent = tangent + reshape(flattened, *expr.size)
            else:
                tangent = tangent + Constant(
                    grad_map[var]).T * (var - var.value)
        return tangent
Exemplo n.º 5
0
    def apply(self, problem):
        if not attributes_present(problem.variables(), CONVEX_ATTRIBUTES):
            return problem, ()

        # For each unique variable, add constraints.
        id2new_var = {}
        id2new_obj = {}
        id2old_var = {}
        constr = []
        for var in problem.variables():
            if var.id not in id2new_var:
                id2old_var[var.id] = var
                new_var = False
                new_attr = var.attributes.copy()
                for key in CONVEX_ATTRIBUTES:
                    if new_attr[key]:
                        new_var = True
                        new_attr[key] = False

                if attributes_present([var], SYMMETRIC_ATTRIBUTES):
                    n = var.shape[0]
                    shape = (n*(n+1)//2, 1)
                    upper_tri = Variable(shape, var_id=var.id, **new_attr)
                    upper_tri.set_variable_of_provenance(var)
                    id2new_var[var.id] = upper_tri
                    fill_coeff = Constant(upper_tri_to_full(n))
                    full_mat = fill_coeff @ upper_tri
                    obj = reshape(full_mat, (n, n))
                elif var.attributes['diag']:
                    diag_var = Variable(var.shape[0], var_id=var.id, **new_attr)
                    diag_var.set_variable_of_provenance(var)
                    id2new_var[var.id] = diag_var
                    obj = diag(diag_var)
                elif new_var:
                    obj = Variable(var.shape, var_id=var.id, **new_attr)
                    obj.set_variable_of_provenance(var)
                    id2new_var[var.id] = obj
                else:
                    obj = var
                    id2new_var[var.id] = obj

                id2new_obj[id(var)] = obj
                if var.is_pos() or var.is_nonneg():
                    constr.append(obj >= 0)
                elif var.is_neg() or var.is_nonpos():
                    constr.append(obj <= 0)
                elif var.is_psd():
                    constr.append(obj >> 0)
                elif var.attributes['NSD']:
                    constr.append(obj << 0)

        # Create new problem.
        obj = problem.objective.tree_copy(id_objects=id2new_obj)
        cons_id_map = {}
        for cons in problem.constraints:
            constr.append(cons.tree_copy(id_objects=id2new_obj))
            cons_id_map[cons.id] = constr[-1].id
        inverse_data = (id2new_var, id2old_var, cons_id_map)
        return cvxtypes.problem()(obj, constr), inverse_data
Exemplo n.º 6
0
def max_canon(expr, args):
    x = args[0]
    shape = expr.shape
    axis = expr.axis
    t = Variable(shape)

    if axis is None:  # shape = (1, 1)
        promoted_t = promote(t, x.shape)
    elif axis == 0:  # shape = (1, n)
        promoted_t = Constant(np.ones((x.shape[0], 1))) * reshape(
                                                            t, (1, x.shape[1]))
    else:  # shape = (m, 1)
        promoted_t = reshape(t, (x.shape[0], 1)) * Constant(
                                                      np.ones((1, x.shape[1])))

    constraints = [x <= promoted_t]
    return t, constraints
Exemplo n.º 7
0
def abs_canon(expr, real_args, imag_args, real2imag):
    # Imaginary.
    if real_args[0] is None:
        output = abs(imag_args[0])
    elif imag_args[0] is None:  # Real
        output = abs(real_args[0])
    else:  # Complex.
        real = real_args[0].flatten()
        imag = imag_args[0].flatten()
        norms = pnorm(vstack([real, imag]), p=2, axis=0)
        output = reshape(norms, real_args[0].shape)
    return output, None
Exemplo n.º 8
0
def log_sum_exp_canon(expr, args):
    x = args[0]
    shape = expr.shape
    axis = expr.axis
    t = Variable(shape)

    # log(sum(exp(x))) <= t <=> sum(exp(x-t)) <= 1
    if axis is None:  # shape = (1, 1)
        promoted_t = promote(t, x.shape)
    elif axis == 0:  # shape = (1, n)
        promoted_t = Constant(np.ones(
            (x.shape[0], 1))) * reshape(t, (1, ) + x.shape[1:])
    else:  # shape = (m, 1)
        promoted_t = reshape(t, x.shape[:-1] +
                             (1, )) * Constant(np.ones((1, x.shape[1])))

    exp_expr = exp(x - promoted_t)
    obj, constraints = exp_canon(exp_expr, exp_expr.args)
    obj = sum(obj, axis=axis)
    ones = Constant(np.ones(shape))
    constraints.append(obj <= ones)
    return t, constraints
Exemplo n.º 9
0
def matrix_frac_canon(expr, args):
    X = args[0]  # n by m matrix.
    P = args[1]  # n by n matrix.

    if len(X.shape) == 1:
        X = reshape(X, (X.shape[0], 1))
    n, m = X.shape
    T = Variable((m, m), symmetric=True)
    M = bmat([[P, X], [X.T, T]])
    # ^ a matrix with Schur complement T - X.T*P^-1*X.
    constraints = [PSD(M)]
    if not P.is_symmetric():
        ut = upper_tri(P)
        lt = upper_tri(P.T)
        constraints.append(ut == lt)
    return trace(T), constraints
Exemplo n.º 10
0
def linearize(expr):
    """Returns an affine approximation to the expression computed at the variable/parameter values.

    Gives an elementwise lower (upper) bound for convex (concave)
    expressions that is tight at the current variable/parameter values.
    No guarantees for non-DCP expressions.

    If f and g are convex, the objective f - g can be (heuristically) minimized using
    the implementation below of the convex-concave method:

    .. code :: python

        for iters in range(N):
            Problem(Minimize(f - linearize(g))).solve()

    Returns None if cannot be linearized.

    Args:
        expr: An expression.

    Returns:
        An affine expression or None.
    """
    expr = Constant.cast_to_const(expr)
    if expr.is_affine():
        return expr
    else:
        tangent = expr.value
        if tangent is None:
            raise ValueError(
                "Cannot linearize non-affine expression with missing variable values."
            )
        grad_map = expr.grad
        for var in expr.variables():
            if grad_map[var] is None:
                return None
            elif var.is_matrix():
                flattened = Constant(grad_map[var]).T * vec(var - var.value)
                tangent = tangent + reshape(flattened, expr.shape)
            else:
                tangent = tangent + Constant(
                    grad_map[var]).T * (var - var.value)
        return tangent
Exemplo n.º 11
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
Exemplo n.º 12
0
def matrix_frac_canon(expr, args):
    X = args[0]  # n by m matrix.
    P = args[1]  # n by n matrix.

    if len(X.shape) == 1:
        X = reshape(X, (X.shape[0], 1))
    n, m = X.shape

    # Create a matrix with Schur complement T - X.T*P^-1*X.
    M = Variable((n + m, n + m), PSD=True)
    T = Variable((m, m))
    constraints = []
    # Fix M using the fact that P must be affine by the DCP rules.
    # M[0:n, 0:n] == P.
    constraints.append(M[0:n, 0:n] == P)
    # M[0:n, n:n+m] == X
    constraints.append(M[0:n, n:n + m] == X)
    # M[n:n+m, n:n+m] == T
    constraints.append(M[n:n + m, n:n + m] == T)
    return trace(T), constraints
Exemplo n.º 13
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
Exemplo n.º 14
0
def soc_canon(expr, real_args, imag_args, real2imag):
    # Imaginary.
    if real_args[1] is None:
        output = [SOC(real_args[0], imag_args[1],
                      axis=expr.axis, constr_id=real2imag[expr.id])]
    elif imag_args[1] is None:  # Real
        output = [SOC(real_args[0], real_args[1],
                      axis=expr.axis, constr_id=expr.id)]
    else:  # Complex.
        orig_shape = real_args[1].shape
        real = real_args[1].flatten()
        imag = imag_args[1].flatten()
        flat_X = Variable(real.shape)
        inner_SOC = SOC(flat_X,
                        vstack([real, imag]),
                        axis=0)
        real_X = reshape(flat_X, orig_shape)
        outer_SOC = SOC(real_args[0], real_X,
                        axis=expr.axis, constr_id=expr.id)
        output = [inner_SOC, outer_SOC]
    return output, None
Exemplo n.º 15
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
Exemplo n.º 16
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