Exemple #1
0
    def graph_implementation(self,
                             arg_objs,
                             shape: Tuple[int, ...],
                             data=None) -> Tuple[lo.LinOp, List[Constraint]]:
        """Index/slice into the expression.

        Parameters
        ----------
        arg_objs : list
            LinExpr for each argument.
        shape : tuple
            The shape of the resulting expression.
        data : tuple
            A tuple of slices.

        Returns
        -------
        tuple
            (LinOp, [constraints])
        """
        select_mat = self._select_mat
        final_shape = self._select_mat.shape
        select_vec = np.reshape(select_mat, select_mat.size, order='F')
        # Select the chosen entries from expr.
        arg = arg_objs[0]
        identity = sp.eye(self.args[0].size).tocsc()
        vec_arg = lu.reshape(arg, (self.args[0].size, ))
        mul_mat = identity[select_vec]
        mul_const = lu.create_const(mul_mat, mul_mat.shape, sparse=True)
        mul_expr = lu.mul_expr(mul_const, vec_arg, (mul_mat.shape[0], ))
        obj = lu.reshape(mul_expr, final_shape)
        return (obj, [])
Exemple #2
0
    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), [])
Exemple #3
0
def gm(t, x, y):
    length = t.size[0] * t.size[1]
    return SOC_Axis(
        lu.reshape(lu.sum_expr([x, y]), (length, 1)),
        lu.vstack([
            lu.reshape(lu.sub_expr(x, y), (1, length)),
            lu.reshape(lu.mul_expr(two, t, t.size), (1, length))
        ], (2, length)), 0)
Exemple #4
0
def gm(t, x, y):
    length = t.size[0]*t.size[1]
    return SOC_Axis(lu.reshape(lu.sum_expr([x, y]), (length, 1)),
                    lu.vstack([
                        lu.reshape(lu.sub_expr(x, y), (1, length)),
                        lu.reshape(lu.mul_expr(two, t, t.size), (1, length))
                        ], (2, length)),
                    0)
Exemple #5
0
    def _scaled_lower_tri(self):
        """Returns a LinOp representing the lower triangular entries.

            Scales the strictly lower triangular entries by
            sqrt(2) as required by SCS.
        """
        rows = cols = self.size[0]
        entries = rows*(cols + 1)//2
        val_arr = []
        row_arr = []
        col_arr = []
        count = 0
        for j in range(cols):
            for i in range(rows):
                if j <= i:
                    # Index in the original matrix.
                    col_arr.append(j*rows + i)
                    # Index in the extracted vector.
                    row_arr.append(count)
                    if j == i:
                        val_arr.append(1.0)
                    else:
                        val_arr.append(np.sqrt(2))
                    count += 1

        size = (entries, rows*cols)
        coeff = sp.coo_matrix((val_arr, (row_arr, col_arr)), size).tocsc()
        coeff = lu.create_const(coeff, size, sparse=True)
        vect = lu.reshape(self.A, (rows*cols, 1))
        return lu.mul_expr(coeff, vect, (entries, 1))
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.coo_matrix(([1.0], ([0], [0])), mat_shape).tocsc()
    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.coo_matrix((val_arr, (row_arr, col_arr)), mat_shape).tocsc()
    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))]
Exemple #7
0
 def canonicalize(self):
     """Variable must be semidefinite and symmetric.
     """
     upper_tri = lu.create_var((self.size[0], 1), self.id)
     fill_coeff = upper_tri_to_full(self.n)
     fill_coeff = lu.create_const(fill_coeff, (self.n*self.n, self.size[0]),
                                  sparse=True)
     full_mat = lu.mul_expr(fill_coeff, upper_tri, (self.n*self.n, 1))
     full_mat = lu.reshape(full_mat, (self.n, self.n))
     return (upper_tri, [SDP(full_mat, enforce_sym=False)])
Exemple #8
0
    def graph_implementation(arg_objs, size, data=None):
        """Convolve two vectors.

        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.reshape(arg_objs[0], size), [])
Exemple #9
0
    def graph_implementation(arg_objs, size, data=None):
        """Convolve two vectors.

        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.reshape(arg_objs[0], size), [])
Exemple #10
0
def combine_lin_ops(operators):
    """Combines the LinOps by stacking their output into a vector.

    Parameters
    ----------
    operators : list
        A list of LinOps.

    Returns
    -------
    LinOp
        The combined LinOp.
    """
    # First vectorize all the LinOp outputs.
    vect_lin_ops = []
    total_length = 0
    for lin_op in operators:
        if lin_op.size[1] != 1:
            new_size = (lin_op.size[0] * lin_op.size[1], 1)
            lin_op = lu.reshape(lin_op, new_size)
        vect_lin_ops.append(lin_op)
        total_length += lin_op.size[0]
    # Stack all the outputs into a single vector.
    return lu.vstack(vect_lin_ops, (total_length, 1))
Exemple #11
0
    def graph_implementation(arg_objs, size, data=None):
        r"""Reduces the atom to an affine expression and list of constraints.

        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)

        Notes
        -----

        Implementation notes.

        - For general :math:`p \geq 1`, the inequality :math:`\|x\|_p \leq t`
          is equivalent to the following convex inequalities:

          .. math::

              |x_i| &\leq r_i^{1/p} t^{1 - 1/p}\\
              \sum_i r_i &= t.

          These inequalities happen to also be correct for :math:`p = +\infty`,
          if we interpret :math:`1/\infty` as :math:`0`.

        - For general :math:`0 < p < 1`, the inequality :math:`\|x\|_p \geq t`
          is equivalent to the following convex inequalities:

          .. math::

              r_i &\leq x_i^{p} t^{1 - p}\\
              \sum_i r_i &= t.

        - For general :math:`p < 0`, the inequality :math:`\|x\|_p \geq t`
          is equivalent to the following convex inequalities:

          .. math::

              t &\leq x_i^{-p/(1-p)} r_i^{1/(1 - p)}\\
              \sum_i r_i &= t.




        Although the inequalities above are correct, for a few special cases,
        we can represent the p-norm more efficiently and with fewer variables and inequalities.

        - For :math:`p = 1`, we use the representation

            .. math::

                x_i &\leq r_i\\
                -x_i &\leq r_i\\
                \sum_i r_i &= t

        - For :math:`p = \infty`, we use the representation

            .. math::

                x_i &\leq t\\
                -x_i &\leq t

          Note that we don't need the :math:`r` variable or the sum inequality.

        - For :math:`p = 2`, we use the natural second-order cone representation

            .. math::

                \|x\|_2 \leq t

          Note that we could have used the set of inequalities given above if we wanted
          an alternate decomposition of a large second-order cone into into several
          smaller inequalities.

        """
        p = data[0]
        axis = data[1]
        x = arg_objs[0]
        t = lu.create_var((1, 1))
        constraints = []

        # first, take care of the special cases of p = 2, inf, and 1
        if p == 2:
            if axis is None:
                return t, [SOC(t, [x])]

            else:
                t = lu.create_var(size)
                return t, [
                    SOC_Axis(lu.reshape(t, (t.size[0] * t.size[1], 1)), x,
                             axis)
                ]

        if p == np.inf:
            t_ = lu.promote(t, x.size)
            return t, [
                lu.create_leq(x, t_),
                lu.create_geq(lu.sum_expr([x, t_]))
            ]

        # we need an absolute value constraint for the symmetric convex branches (p >= 1)
        # we alias |x| as x from this point forward to make the code pretty :)
        if p >= 1:
            absx = lu.create_var(x.size)
            constraints += [
                lu.create_leq(x, absx),
                lu.create_geq(lu.sum_expr([x, absx]))
            ]
            x = absx

        if p == 1:
            return lu.sum_entries(x), constraints

        # now, we take care of the remaining convex and concave branches
        # to create the rational powers, we need a new variable, r, and
        # the constraint sum(r) == t
        r = lu.create_var(x.size)
        t_ = lu.promote(t, x.size)
        constraints += [lu.create_eq(lu.sum_entries(r), t)]

        # make p a fraction so that the input weight to gm_constrs
        # is a nice tuple of fractions.
        p = Fraction(p)
        if p < 0:
            constraints += gm_constrs(t_, [x, r], (-p / (1 - p), 1 / (1 - p)))
        if 0 < p < 1:
            constraints += gm_constrs(r, [x, t_], (p, 1 - p))
        if p > 1:
            constraints += gm_constrs(x, [r, t_], (1 / p, 1 - 1 / p))

        return t, constraints
Exemple #12
0
    def graph_implementation(arg_objs, size, data=None):
        r"""Reduces the atom to an affine expression and list of constraints.

        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)

        Notes
        -----

        Implementation notes.

        - For general :math:`p \geq 1`, the inequality :math:`\|x\|_p \leq t`
          is equivalent to the following convex inequalities:

          .. math::

              |x_i| &\leq r_i^{1/p} t^{1 - 1/p}\\
              \sum_i r_i &= t.

          These inequalities happen to also be correct for :math:`p = +\infty`,
          if we interpret :math:`1/\infty` as :math:`0`.

        - For general :math:`0 < p < 1`, the inequality :math:`\|x\|_p \geq t`
          is equivalent to the following convex inequalities:

          .. math::

              r_i &\leq x_i^{p} t^{1 - p}\\
              \sum_i r_i &= t.

        - For general :math:`p < 0`, the inequality :math:`\|x\|_p \geq t`
          is equivalent to the following convex inequalities:

          .. math::

              t &\leq x_i^{-p/(1-p)} r_i^{1/(1 - p)}\\
              \sum_i r_i &= t.




        Although the inequalities above are correct, for a few special cases,
        we can represent the p-norm more efficiently and with fewer variables and inequalities.

        - For :math:`p = 1`, we use the representation

            .. math::

                x_i &\leq r_i\\
                -x_i &\leq r_i\\
                \sum_i r_i &= t

        - For :math:`p = \infty`, we use the representation

            .. math::

                x_i &\leq t\\
                -x_i &\leq t

          Note that we don't need the :math:`r` variable or the sum inequality.

        - For :math:`p = 2`, we use the natural second-order cone representation

            .. math::

                \|x\|_2 \leq t

          Note that we could have used the set of inequalities given above if we wanted
          an alternate decomposition of a large second-order cone into into several
          smaller inequalities.

        """
        p = data[0]
        axis = data[1]
        x = arg_objs[0]
        t = lu.create_var((1, 1))
        constraints = []

        # first, take care of the special cases of p = 2, inf, and 1
        if p == 2:
            if axis is None:
                return t, [SOC(t, [x])]

            else:
                t = lu.create_var(size)
                return t, [SOC_Axis(lu.reshape(t, (t.size[0]*t.size[1], 1)),
                                    x, axis)]

        if p == np.inf:
            t_ = lu.promote(t, x.size)
            return t, [lu.create_leq(x, t_), lu.create_geq(lu.sum_expr([x, t_]))]

        # we need an absolute value constraint for the symmetric convex branches (p >= 1)
        # we alias |x| as x from this point forward to make the code pretty :)
        if p >= 1:
            absx = lu.create_var(x.size)
            constraints += [lu.create_leq(x, absx), lu.create_geq(lu.sum_expr([x, absx]))]
            x = absx

        if p == 1:
            return lu.sum_entries(x), constraints

        # now, we take care of the remaining convex and concave branches
        # to create the rational powers, we need a new variable, r, and
        # the constraint sum(r) == t
        r = lu.create_var(x.size)
        t_ = lu.promote(t, x.size)
        constraints += [lu.create_eq(lu.sum_entries(r), t)]

        # make p a fraction so that the input weight to gm_constrs
        # is a nice tuple of fractions.
        p = Fraction(p)
        if p < 0:
            constraints += gm_constrs(t_, [x, r],  (-p/(1-p), 1/(1-p)))
        if 0 < p < 1:
            constraints += gm_constrs(r,  [x, t_], (p, 1-p))
        if p > 1:
            constraints += gm_constrs(x,  [r, t_], (1/p, 1-1/p))

        return t, constraints