Beispiel #1
0
    def test_assign_var_value(self):
        """Test assigning a value to a variable.
        """
        # Scalar variable.
        a = Variable()
        a.value = 1
        self.assertEqual(a.value, 1)
        with self.assertRaises(Exception) as cm:
            a.value = [2, 1]
        self.assertEqual(str(cm.exception),
                         "Invalid dimensions (2,) for Variable value.")

        # Test assigning None.
        a.value = 1
        a.value = None
        assert a.value is None

        # Vector variable.
        x = Variable(2)
        x.value = [2, 1]
        self.assertItemsAlmostEqual(x.value, [2, 1])
        # Matrix variable.
        A = Variable((3, 2))
        A.value = np.ones((3, 2))
        self.assertItemsAlmostEqual(A.value, np.ones((3, 2)))

        # Test assigning negative val to nonnegative variable.
        x = Variable(nonneg=True)
        with self.assertRaises(Exception) as cm:
            x.value = -2
        self.assertEqual(str(cm.exception),
                         "Variable value must be nonnegative.")
Beispiel #2
0
def maximum_canon(expr, args):
    shape = expr.shape
    t = Variable(shape)
    constraints = [t >= elem for elem in args]
    return t, constraints
Beispiel #3
0
    def setUp(self):
        self.a = Variable(name='a')
        self.b = Variable(name='b')
        self.c = Variable(name='c')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')
        self.w = Variable(5, name='w')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')

        self.slope = Variable(1, name='slope')
        self.offset = Variable(1, name='offset')
        self.quadratic_coeff = Variable(1, name='quadratic_coeff')

        T = 100
        self.position = Variable((2, T), name='position')
        self.velocity = Variable((2, T), name='velocity')
        self.force = Variable((2, T - 1), name='force')

        self.xs = Variable(80, name='xs')
        self.xsr = Variable(200, name='xsr')
        self.xef = Variable(80, name='xef')

        # Check for all installed QP solvers
        self.solvers = [x for x in QP_SOLVERS if x in INSTALLED_SOLVERS]
        if 'MOSEK' in INSTALLED_SOLVERS:
            self.solvers.append('MOSEK')
Beispiel #4
0
class TestConstraints(BaseTest):
    """ Unit tests for the expression/expression module. """
    def setUp(self):
        self.a = Variable(name='a')
        self.b = Variable(name='b')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')

    # def test_constr_str(self):
    #     """Test string representations of the constraints.
    #     """
    #     constr = self.x <= self.x
    #     self.assertEqual(repr(constr), "NonPos(%s, %s)" % (repr(self.x), repr(self.x)))
    #     constr = self.x <= 2*self.x
    #     self.assertEqual(repr(constr), "NonPos(%s, %s)" % (repr(self.x), repr(2*self.x)))
    #     constr = 2*self.x >= self.x
    #     self.assertEqual(repr(constr), "NonPos(%s, %s)" % (repr(self.x), repr(2*self.x)))

    def test_equality(self):
        """Test the Equality class.
        """
        constr = self.x == self.z
        self.assertEqual(constr.name(), "x == z")
        self.assertEqual(constr.shape, (2, ))
        # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id])
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.x.save_value(2)
        self.z.save_value(2)
        assert constr.value()
        self.x.save_value(3)
        assert not constr.value()

        self.x.value = np.array([2, 1])
        self.z.value = np.array([2, 2])
        assert not constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 1])
        self.assertItemsAlmostEqual(constr.residual, [0, 1])

        self.z.value = np.array([2, 1])
        assert constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 0])
        self.assertItemsAlmostEqual(constr.residual, [0, 0])

        # Incompatible dimensions
        with self.assertRaises(ValueError):
            (self.x == self.y)

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.A])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.A)

    def test_inequality(self):
        """Test the Inequality class.
        """
        constr = self.x <= self.z
        self.assertEqual(constr.name(), "x <= z")
        self.assertEqual(constr.shape, (2, ))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.x.save_value(1)
        self.z.save_value(2)
        assert constr.value()
        self.x.save_value(3)
        assert not constr.value()
        # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id])

        self.x.value = np.array([2, 1])
        self.z.value = np.array([2, 0])
        assert not constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 1])
        self.assertItemsAlmostEqual(constr.residual, [0, 1])

        self.z.value = np.array([2, 2])
        assert constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 0])
        self.assertItemsAlmostEqual(constr.residual, [0, 0])

        # Incompatible dimensions
        with self.assertRaises(Exception):
            (self.x <= self.y)

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.A])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.A)

    def test_psd_constraint(self):
        """Test the PSD constraint <<.
        """
        constr = self.A >> self.B
        self.assertEqual(constr.name(), "A + -B >> 0")
        self.assertEqual(constr.shape, (2, 2))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.A.save_value(np.array([[2, -1], [1, 2]]))
        self.B.save_value(np.array([[1, 0], [0, 1]]))
        assert constr.value()
        self.assertAlmostEqual(constr.violation(), 0)
        self.assertAlmostEqual(constr.residual, 0)

        self.B.save_value(np.array([[3, 0], [0, 3]]))
        assert not constr.value()
        self.assertAlmostEqual(constr.violation(), 1)
        self.assertAlmostEqual(constr.residual, 1)

        with self.assertRaises(Exception) as cm:
            (self.x >> 0)
        self.assertEqual(str(cm.exception),
                         "Non-square matrix in positive definite constraint.")

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.B])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.B)

    def test_nsd_constraint(self):
        """Test the PSD constraint <<.
        """
        constr = self.A << self.B
        self.assertEqual(constr.name(), "B + -A >> 0")
        self.assertEqual(constr.shape, (2, 2))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.B.save_value(np.array([[2, -1], [1, 2]]))
        self.A.save_value(np.array([[1, 0], [0, 1]]))
        assert constr.value()
        self.A.save_value(np.array([[3, 0], [0, 3]]))
        assert not constr.value()

        with self.assertRaises(Exception) as cm:
            self.x << 0
        self.assertEqual(str(cm.exception),
                         "Non-square matrix in positive definite constraint.")

    def test_geq(self):
        """Test the >= operator.
        """
        constr = self.z >= self.x
        self.assertEqual(constr.name(), "x <= z")
        self.assertEqual(constr.shape, (2, ))

        # Incompatible dimensions
        with self.assertRaises(ValueError):
            (self.y >= self.x)

    # Test the SOC class.
    def test_soc_constraint(self):
        exp = self.x + self.z
        scalar_exp = self.a + self.b
        constr = SOC(scalar_exp, exp)
        self.assertEqual(constr.size, 3)

    def test_chained_constraints(self):
        """Tests that chaining constraints raises an error.
        """
        error_str = ("Cannot evaluate the truth value of a constraint or "
                     "chain constraints, e.g., 1 >= x >= 0.")
        with self.assertRaises(Exception) as cm:
            (self.z <= self.x <= 1)
        self.assertEqual(str(cm.exception), error_str)

        with self.assertRaises(Exception) as cm:
            (self.x == self.z == 1)
        self.assertEqual(str(cm.exception), error_str)

        if PY2:
            with self.assertRaises(Exception) as cm:
                (self.z <= self.x).__nonzero__()
            self.assertEqual(str(cm.exception), error_str)
        else:
            with self.assertRaises(Exception) as cm:
                (self.z <= self.x).__bool__()
            self.assertEqual(str(cm.exception), error_str)
Beispiel #5
0
    def setUp(self):
        self.a = Variable(name='a')
        self.b = Variable(name='b')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')
Beispiel #6
0
    def test_round_attr(self):
        """Test rounding for attributes.
        """
        # Nonpos
        v = Variable(1, nonpos=True)
        self.assertAlmostEqual(v.project(1), 0)
        v = Variable(2, nonpos=True)
        self.assertItemsAlmostEqual(v.project(np.array([1, -1])), [0, -1])

        # Nonneg
        v = Variable(1, nonneg=True)
        self.assertAlmostEqual(v.project(-1), 0)
        v = Variable(2, nonneg=True)
        self.assertItemsAlmostEqual(v.project(np.array([1, -1])), [1, 0])

        # Boolean
        v = Variable((2, 2), boolean=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, 0]]).T),
                                    [1, 0, 1, 0])

        # Integer
        v = Variable((2, 2), integer=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1.6], [1, 0]]).T),
                                    [1, -2, 1, 0])

        # Symmetric
        v = Variable((2, 2), symmetric=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, 0]])),
                                    [1, 0, 0, 0])

        # PSD
        v = Variable((2, 2), PSD=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, -1]])),
                                    [1, 0, 0, 0])

        # NSD
        v = Variable((2, 2), NSD=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, -1]])),
                                    [0, 0, 0, -1])

        # diag
        v = Variable((2, 2), diag=True)
        self.assertItemsAlmostEqual(
            v.project(np.array([[1, -1], [1, 0]])).todense(), [1, 0, 0, 0])

        # Hermitian
        v = Variable((2, 2), hermitian=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1j], [1, 0]])),
                                    [1, 0.5 + 0.5j, 0.5 - 0.5j, 0])

        A = Constant(np.array([[1.0]]))
        self.assertEqual(A.is_psd(), True)
        self.assertEqual(A.is_nsd(), False)
        A = Constant(np.array([[-1.0]]))
        self.assertEqual(A.is_psd(), False)
        self.assertEqual(A.is_nsd(), True)
        A = Constant(np.array([[0.0]]))
        self.assertEqual(A.is_psd(), True)
        self.assertEqual(A.is_nsd(), True)
Beispiel #7
0
class TestMatrices(unittest.TestCase):
    """ Unit tests for testing different forms of matrices as constants. """
    def assertExpression(self, expr, shape):
        """Asserts that expr is an Expression with dimension shape.
        """
        assert isinstance(expr, Expression) or isinstance(expr, Constraint)
        self.assertEqual(expr.shape, shape)

    def setUp(self):
        self.a = Variable(name='a')
        self.b = Variable(name='b')
        self.c = Variable(name='c')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')

    # Test numpy arrays
    def test_numpy_arrays(self):
        # Vector
        v = numpy.arange(2)
        self.assertExpression(self.x + v, (2, ))
        self.assertExpression(v + self.x, (2, ))
        self.assertExpression(self.x - v, (2, ))
        self.assertExpression(v - self.x, (2, ))
        self.assertExpression(self.x <= v, (2, ))
        self.assertExpression(v <= self.x, (2, ))
        self.assertExpression(self.x == v, (2, ))
        self.assertExpression(v == self.x, (2, ))
        # Matrix
        A = numpy.arange(8).reshape((4, 2))
        self.assertExpression(A * self.x, (4, ))
        if PY35:
            self.assertExpression(self.x.__rmatmul__(A), (4, ))
        # PSD inequalities.
        A = numpy.ones((2, 2))
        self.assertExpression(A << self.A, (2, 2))
        self.assertExpression(A >> self.A, (2, 2))

    # Test numpy matrices
    def test_numpy_matrices(self):
        # Vector
        v = numpy.arange(2)
        self.assertExpression(self.x + v, (2, ))
        self.assertExpression(v + v + self.x, (2, ))
        self.assertExpression(self.x - v, (2, ))
        self.assertExpression(v - v - self.x, (2, ))
        self.assertExpression(self.x <= v, (2, ))
        self.assertExpression(v <= self.x, (2, ))
        self.assertExpression(self.x == v, (2, ))
        self.assertExpression(v == self.x, (2, ))
        # Matrix
        A = numpy.arange(8).reshape((4, 2))
        self.assertExpression(A * self.x, (4, ))
        self.assertExpression((A.T.dot(A)) * self.x, (2, ))
        if PY35:
            self.assertExpression(self.x.__rmatmul__(A), (4, ))
        # PSD inequalities.
        A = numpy.ones((2, 2))
        self.assertExpression(A << self.A, (2, 2))
        self.assertExpression(A >> self.A, (2, 2))

    def test_numpy_scalars(self):
        """Test numpy scalars."""
        v = numpy.float64(2.0)
        self.assertExpression(self.x + v, (2, ))
        self.assertExpression(v + self.x, (2, ))
        self.assertExpression(v * self.x, (2, ))
        self.assertExpression(self.x - v, (2, ))
        self.assertExpression(v - v - self.x, (2, ))
        self.assertExpression(self.x <= v, (2, ))
        self.assertExpression(v <= self.x, (2, ))
        self.assertExpression(self.x == v, (2, ))
        self.assertExpression(v == self.x, (2, ))
        # PSD inequalities.
        self.assertExpression(v << self.A, (2, 2))
        self.assertExpression(v >> self.A, (2, 2))

    # def test_cvxopt_matrices(self):
    #     """Test cvxopt dense matrices.
    #     """
    #     # Vector
    #     v = cvxopt.matrix( numpy.arange(2).reshape((2,1)) )
    #     self.assertExpression(self.x + v, (2,1))
    #     self.assertExpression(v + v + self.x, (2,1))
    #     self.assertExpression(self.x - v, (2,1))
    #     self.assertExpression(v - v - self.x, (2,1))
    #     self.assertExpression(self.x <= v, (2,1))
    #     self.assertExpression(v <= self.x, (2,1))
    #     self.assertExpression(self.x == v, (2,1))
    #     self.assertExpression(v == self.x, (2,1))
    #     # Matrix
    #     A = cvxopt.matrix( numpy.arange(8).reshape((4,2)) )
    #     self.assertExpression(A*self.x, (4,1))
    #     self.assertExpression( (A.T*A) * self.x, (2,1))

    # # Test cvxopt sparse matrices.
    # def test_cvxopt_sparse(self):
    #     m = 100
    #     n = 20

    #     mu = cvxopt.exp(cvxopt.normal(m))
    #     F = cvxopt.normal(m, n)
    #     D = cvxopt.spdiag(cvxopt.uniform(m))
    #     x = Variable(m)
    #     exp = square(norm2(D*x))

    def test_scipy_sparse(self):
        """Test scipy sparse matrices."""
        # Constants.
        A = numpy.arange(8).reshape((4, 2))
        A = sp.csc_matrix(A)
        A = sp.eye(2).tocsc()
        key = (slice(0, 1, None), slice(None, None, None))
        Aidx = intf.index(A, (slice(0, 2, None), slice(None, None, None)))
        Aidx = intf.index(Aidx, key)
        self.assertEqual(Aidx.shape, (1, 2))
        self.assertEqual(Aidx[0, 0], 1)
        self.assertEqual(Aidx[0, 1], 0)

        # Linear ops.
        var = Variable((4, 2))
        A = numpy.arange(8).reshape((4, 2))
        A = sp.csc_matrix(A)
        B = sp.hstack([A, A])
        self.assertExpression(var + A, (4, 2))
        self.assertExpression(A + var, (4, 2))
        self.assertExpression(B * var, (4, 2))
        self.assertExpression(var - A, (4, 2))
        self.assertExpression(A - A - var, (4, 2))
        if PY35:
            self.assertExpression(var.__rmatmul__(B), (4, 2))
def partial_optimize(prob, opt_vars=None, dont_opt_vars=None, solver=None):
    """Partially optimizes the given problem over the specified variables.

    Either opt_vars or dont_opt_vars must be given.
    If both are given, they must contain all the variables in the problem.

    Partial optimize is useful for two-stage optimization and graph implementations.
    For example, we can write

    .. code :: python

        x = Variable(n)
        t = Variable(n)
        abs_x = partial_optimize(Problem(Minimize(sum(t)),
                  [-t <= x, x <= t]), opt_vars=[t])

    to define the entrywise absolute value of x.

    Parameters
    ----------
    prob : Problem
        The problem to partially optimize.
    opt_vars : list, optional
        The variables to optimize over.
    dont_opt_vars : list, optional
        The variables to not optimize over.
    solver : str, optional
        The default solver to use for value and grad.

    Returns
    -------
    Expression
        An expression representing the partial optimization.
        Convex for minimization objectives and concave for maximization objectives.
    """
    # One of the two arguments must be specified.
    if opt_vars is None and dont_opt_vars is None:
        raise ValueError(
            "partial_optimize called with neither opt_vars nor dont_opt_vars."
        )
    # If opt_vars is not specified, it's the complement of dont_opt_vars.
    elif opt_vars is None:
        ids = [id(var) for var in dont_opt_vars]
        opt_vars = [var for var in prob.variables() if not id(var) in ids]
    # If dont_opt_vars is not specified, it's the complement of opt_vars.
    elif dont_opt_vars is None:
        ids = [id(var) for var in opt_vars]
        dont_opt_vars = [var for var in prob.variables() if not id(var) in ids]
    elif opt_vars is not None and dont_opt_vars is not None:
        ids = [id(var) for var in opt_vars + dont_opt_vars]
        for var in prob.variables():
            if id(var) not in ids:
                raise ValueError(
                    ("If opt_vars and new_opt_vars are both specified, "
                     "they must contain all variables in the problem.")
                )

    # Replace the opt_vars in prob with new variables.
    id_to_new_var = {id(var): Variable(var.shape,
                                       **var.attributes) for var in opt_vars}
    new_obj = prob.objective.tree_copy(id_to_new_var)
    new_constrs = [con.tree_copy(id_to_new_var)
                   for con in prob.constraints]
    new_var_prob = Problem(new_obj, new_constrs)
    return PartialProblem(new_var_prob, opt_vars, dont_opt_vars, solver)
Beispiel #9
0
class TestConstraints(BaseTest):
    """ Unit tests for the expression/expression module. """

    def setUp(self) -> None:
        self.a = Variable(name='a')
        self.b = Variable(name='b')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')

    def test_equality(self) -> None:
        """Test the Equality class.
        """
        constr = self.x == self.z
        self.assertEqual(constr.name(), "x == z")
        self.assertEqual(constr.shape, (2,))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.x.save_value(2)
        self.z.save_value(2)
        assert constr.value()
        self.x.save_value(3)
        assert not constr.value()

        self.x.value = np.array([2, 1])
        self.z.value = np.array([2, 2])
        assert not constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 1])
        self.assertItemsAlmostEqual(constr.residual, [0, 1])

        self.z.value = np.array([2, 1])
        assert constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 0])
        self.assertItemsAlmostEqual(constr.residual, [0, 0])

        # Incompatible dimensions
        with self.assertRaises(ValueError):
            (self.x == self.y)

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.A])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.A)

    def test_inequality(self) -> None:
        """Test the Inequality class.
        """
        constr = self.x <= self.z
        self.assertEqual(constr.name(), "x <= z")
        self.assertEqual(constr.shape, (2,))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.x.save_value(1)
        self.z.save_value(2)
        assert constr.value()
        self.x.save_value(3)
        assert not constr.value()
        # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id])

        self.x.value = np.array([2, 1])
        self.z.value = np.array([2, 0])
        assert not constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 1])
        self.assertItemsAlmostEqual(constr.residual, [0, 1])

        self.z.value = np.array([2, 2])
        assert constr.value()
        self.assertItemsAlmostEqual(constr.violation(), [0, 0])
        self.assertItemsAlmostEqual(constr.residual, [0, 0])

        # Incompatible dimensions
        with self.assertRaises(Exception):
            (self.x <= self.y)

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.A])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.A)

    def test_psd_constraint(self) -> None:
        """Test the PSD constraint <<.
        """
        constr = self.A >> self.B
        self.assertEqual(constr.name(), "A + -B >> 0")
        self.assertEqual(constr.shape, (2, 2))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.A.save_value(np.array([[2, -1], [1, 2]]))
        self.B.save_value(np.array([[1, 0], [0, 1]]))
        assert constr.value()
        self.assertAlmostEqual(constr.violation(), 0)
        self.assertAlmostEqual(constr.residual, 0)

        self.B.save_value(np.array([[3, 0], [0, 3]]))
        assert not constr.value()
        self.assertAlmostEqual(constr.violation(), 1)
        self.assertAlmostEqual(constr.residual, 1)

        with self.assertRaises(Exception) as cm:
            (self.x >> 0)
        self.assertEqual(str(cm.exception), "Non-square matrix in positive definite constraint.")

        # Test copy with args=None
        copy = constr.copy()
        self.assertTrue(type(copy) is type(constr))
        # A new object is constructed, so copy.args == constr.args but copy.args
        # is not constr.args.
        self.assertEqual(copy.args, constr.args)
        self.assertFalse(copy.args is constr.args)
        # Test copy with new args
        copy = constr.copy(args=[self.B])
        self.assertTrue(type(copy) is type(constr))
        self.assertTrue(copy.args[0] is self.B)

    def test_nsd_constraint(self) -> None:
        """Test the PSD constraint <<.
        """
        constr = self.A << self.B
        self.assertEqual(constr.name(), "B + -A >> 0")
        self.assertEqual(constr.shape, (2, 2))
        # Test value and dual_value.
        assert constr.dual_value is None
        with self.assertRaises(ValueError):
            constr.value()
        self.B.save_value(np.array([[2, -1], [1, 2]]))
        self.A.save_value(np.array([[1, 0], [0, 1]]))
        assert constr.value()
        self.A.save_value(np.array([[3, 0], [0, 3]]))
        assert not constr.value()

        with self.assertRaises(Exception) as cm:
            self.x << 0
        self.assertEqual(str(cm.exception),
                         "Non-square matrix in positive definite constraint.")

    def test_geq(self) -> None:
        """Test the >= operator.
        """
        constr = self.z >= self.x
        self.assertEqual(constr.name(), "x <= z")
        self.assertEqual(constr.shape, (2,))

        # Incompatible dimensions
        with self.assertRaises(ValueError):
            (self.y >= self.x)

    # Test the SOC class.
    def test_soc_constraint(self) -> None:
        exp = self.x + self.z
        scalar_exp = self.a + self.b
        constr = SOC(scalar_exp, exp)
        self.assertEqual(constr.size, 3)

        # Test invalid dimensions.
        error_str = ("Argument dimensions (1,) and (1, 4), with axis=0, "
                     "are incompatible.")
        with self.assertRaises(Exception) as cm:
            SOC(Variable(1), Variable((1, 4)))
        self.assertEqual(str(cm.exception), error_str)

    def test_pow3d_constraint(self) -> None:
        n = 3
        np.random.seed(0)
        alpha = 0.275
        x, y, z = Variable(n), Variable(n), Variable(n)
        con = PowCone3D(x, y, z, alpha)
        # check violation against feasible values
        x0, y0 = 0.1 + np.random.rand(n), 0.1 + np.random.rand(n)
        z0 = x0**alpha * y0**(1-alpha)
        z0[1] *= -1
        x.value, y.value, z.value = x0, y0, z0
        viol = con.residual()
        self.assertLessEqual(viol, 1e-7)
        # check violation against infeasible values
        x1 = x0.copy()
        x1[0] *= -0.9
        x.value = x1
        viol = con.residual()
        self.assertGreaterEqual(viol, 0.99*abs(x1[0]))
        # check invalid constraint data
        with self.assertRaises(ValueError):
            con = PowCone3D(x, y, z, 1.001)
        with self.assertRaises(ValueError):
            con = PowCone3D(x, y, z, -0.00001)

    def test_pownd_constraint(self) -> None:
        n = 4
        W, z = Variable(n), Variable()
        np.random.seed(0)
        alpha = 0.5 + np.random.rand(n)
        alpha /= np.sum(alpha)
        with self.assertRaises(ValueError):
            # entries don't sum to one
            con = PowConeND(W, z, alpha+0.01)
        with self.assertRaises(ValueError):
            # shapes don't match exactly
            con = PowConeND(W, z, alpha.reshape((n, 1)))
        with self.assertRaises(ValueError):
            # wrong axis
            con = PowConeND(reshape_atom(W, (n, 1)), z,
                            alpha.reshape((n, 1)),
                            axis=1)
        # Compute a violation
        con = PowConeND(W, z, alpha)
        W0 = 0.1 + np.random.rand(n)
        z0 = np.prod(np.power(W0, alpha))+0.05
        W.value, z.value = W0, z0
        viol = con.violation()
        self.assertGreaterEqual(viol, 0.01)
        self.assertLessEqual(viol, 0.06)

    def test_chained_constraints(self) -> None:
        """Tests that chaining constraints raises an error.
        """
        error_str = ("Cannot evaluate the truth value of a constraint or "
                     "chain constraints, e.g., 1 >= x >= 0.")
        with self.assertRaises(Exception) as cm:
            (self.z <= self.x <= 1)
        self.assertEqual(str(cm.exception), error_str)

        with self.assertRaises(Exception) as cm:
            (self.x == self.z == 1)
        self.assertEqual(str(cm.exception), error_str)

        with self.assertRaises(Exception) as cm:
            (self.z <= self.x).__bool__()
        self.assertEqual(str(cm.exception), error_str)

    def test_nonpos(self) -> None:
        """Tests the NonPos constraint for correctness.
        """
        n = 3
        x = cp.Variable(n)
        c = np.arange(n)
        prob = cp.Problem(cp.Maximize(cp.sum(x)),
                          [cp.NonPos(x - c)])
        # Solve through cone program path.
        prob.solve(solver=cp.ECOS)
        self.assertItemsAlmostEqual(x.value, c)

        # Solve through QP path.
        prob.solve(solver=cp.OSQP)
        self.assertItemsAlmostEqual(x.value, c)

    def test_nonpos_dual(self) -> None:
        """Test dual variables work for NonPos.
        """
        n = 3
        x = cp.Variable(n)
        c = np.arange(n)
        prob = cp.Problem(cp.Maximize(cp.sum(x)),
                          [(x - c) <= 0])
        prob.solve(solver=cp.ECOS)
        dual = prob.constraints[0].dual_value
        prob = cp.Problem(cp.Maximize(cp.sum(x)),
                          [cp.NonPos(x - c)])
        # Solve through cone program path.
        prob.solve(solver=cp.ECOS)
        self.assertItemsAlmostEqual(prob.constraints[0].dual_value, dual)

        # Solve through QP path.
        prob.solve(solver=cp.OSQP)
        self.assertItemsAlmostEqual(prob.constraints[0].dual_value, dual)
Beispiel #10
0
    def test_symmetric(self) -> None:
        """Test symmetric variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), symmetric=True)
        self.assertEqual(str(cm.exception), "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), symmetric=True)
        assert v.is_symmetric()
        v = Variable((2, 2), PSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), NSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_symmetric()
        assert self.a.is_symmetric()
        assert not self.A.is_symmetric()

        v = Variable((2, 2), symmetric=True)
        expr = v + v
        assert expr.is_symmetric()
        expr = -v
        assert expr.is_symmetric()
        expr = v.T
        assert expr.is_symmetric()
        expr = cp.real(v)
        assert expr.is_symmetric()
        expr = cp.imag(v)
        assert expr.is_symmetric()
        expr = cp.conj(v)
        assert expr.is_symmetric()
        expr = cp.promote(Variable(), (2, 2))
        assert expr.is_symmetric()
Beispiel #11
0
    def test_matmul_expression(self) -> None:
        """Test matmul function, corresponding to .__matmul__( operator.
        """
        # Vectors
        c = Constant([[2], [2]])
        exp = c.__matmul__(self.x)
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        # self.assertEqual(exp.name(), c.name() + " .__matmul__( " + self.x.name())
        self.assertEqual(exp.shape, (1,))

        with self.assertRaises(Exception) as cm:
            self.x.__matmul__(2)
        self.assertEqual(str(cm.exception),
                         "Scalar operands are not allowed, use '*' instead")

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.x.__matmul__(np.array([2, 2, 3])))

        # Incompatible dimensions
        with self.assertRaises(Exception) as cm:
            Constant([[2, 1], [2, 2]]) .__matmul__(self.C)

        # Affine times affine is okay
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            q = self.A .__matmul__(self.B)
            self.assertTrue(q.is_quadratic())

        # # Nonaffine times nonconstant raises error
        # with warnings.catch_warnings():
        #     warnings.simplefilter("ignore")
        #     with self.assertRaises(Exception) as cm:
        #         (self.A.__matmul__(self.B).__matmul__(self.A))
        #     self.assertEqual(str(cm.exception), "Cannot multiply UNKNOWN and AFFINE.")

        # Constant expressions
        T = Constant([[1, 2, 3], [3, 5, 5]])
        exp = (T + T) .__matmul__(self.B)
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (3, 2))

        # Expression that would break sign multiplication without promotion.
        c = Constant([[2], [2], [-2]])
        exp = [[1], [2]] + c.__matmul__(self.C)
        self.assertEqual(exp.sign, s.UNKNOWN)

        # Testing shape.
        a = Parameter((1,))
        x = Variable(shape=(1,))
        expr = a.__matmul__(x)
        self.assertEqual(expr.shape, ())

        # Testing shape.
        a = Parameter((1,))
        x = Variable(shape=(1,))
        expr = a.__matmul__(x)
        self.assertEqual(expr.shape, ())

        A = Parameter((4, 4))
        z = Variable((4, 1))
        expr = A.__matmul__(z)
        self.assertEqual(expr.shape, (4, 1))

        v = Variable((1, 1))
        col_scalar = Parameter((1, 1))
        assert v.shape == col_scalar.shape == col_scalar.T.shape
Beispiel #12
0
 def setUp(self) -> None:
     self.X = Variable((2, 2), PSD=True)
     self.Y = Variable((2, 2))
     self.F = np.array([[1, 0], [0, -1]])
 def setUp(self):
     self.X = Variable((2, 2), PSD=True)
     self.Y = Variable((2, 2))
     self.F = np.matrix([[1, 0], [0, -1]])
Beispiel #14
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
Beispiel #15
0
class TestExpressions(BaseTest):
    """ Unit tests for the expression/expression module. """
    def setUp(self):
        self.a = Variable(name='a')

        self.x = Variable(2, name='x')
        self.y = Variable(3, name='y')
        self.z = Variable(2, name='z')

        self.A = Variable((2, 2), name='A')
        self.B = Variable((2, 2), name='B')
        self.C = Variable((3, 2), name='C')
        self.intf = intf.DEFAULT_INTF

    # Test the Variable class.
    def test_variable(self):
        x = Variable(2)
        y = Variable(2)
        assert y.name() != x.name()

        x = Variable(2, name='x')
        y = Variable()
        self.assertEqual(x.name(), 'x')
        self.assertEqual(x.shape, (2, ))
        self.assertEqual(y.shape, tuple())
        self.assertEqual(x.curvature, s.AFFINE)
        # self.assertEqual(x.canonical_form[0].shape, (2, 1))
        # self.assertEqual(x.canonical_form[1], [])

        self.assertEqual(repr(self.x), "Variable((2,))")
        self.assertEqual(repr(self.A), "Variable((2, 2))")

        # # Scalar variable
        # coeff = self.a.coefficients()
        # self.assertEqual(coeff[self.a.id], [1])

        # # Vector variable.
        # coeffs = x.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [x.id])
        # vec = coeffs[x.id][0]
        # self.assertEqual(vec.shape, (2,2))
        # self.assertEqual(vec[0,0], 1)

        # # Matrix variable.
        # coeffs = self.A.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [self.A.id])
        # self.assertEqual(len(coeffs[self.A.id]), 2) or 0 in self.shape
        # mat = coeffs[self.A.id][1]
        # self.assertEqual(mat.shape, (2,4))
        # self.assertEqual(mat[0,2], 1)

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 2), diag=True, symmetric=True)
        self.assertEqual(
            str(cm.exception),
            "Cannot set more than one special attribute in Variable.")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 0))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0).")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, .5))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0.5).")

    def test_assign_var_value(self):
        """Test assigning a value to a variable.
        """
        # Scalar variable.
        a = Variable()
        a.value = 1
        self.assertEqual(a.value, 1)
        with self.assertRaises(Exception) as cm:
            a.value = [2, 1]
        self.assertEqual(str(cm.exception),
                         "Invalid dimensions (2,) for Variable value.")

        # Test assigning None.
        a.value = 1
        a.value = None
        assert a.value is None

        # Vector variable.
        x = Variable(2)
        x.value = [2, 1]
        self.assertItemsAlmostEqual(x.value, [2, 1])
        # Matrix variable.
        A = Variable((3, 2))
        A.value = np.ones((3, 2))
        self.assertItemsAlmostEqual(A.value, np.ones((3, 2)))

        # Test assigning negative val to nonnegative variable.
        x = Variable(nonneg=True)
        with self.assertRaises(Exception) as cm:
            x.value = -2
        self.assertEqual(str(cm.exception),
                         "Variable value must be nonnegative.")

    # Test tranposing variables.
    def test_transpose_variable(self):
        var = self.a.T
        self.assertEqual(var.name(), "a")
        self.assertEqual(var.shape, tuple())

        self.a.save_value(2)
        self.assertEqual(var.value, 2)

        var = self.x
        self.assertEqual(var.name(), "x")
        self.assertEqual(var.shape, (2, ))

        x = Variable((2, 1), name='x')
        var = x.T
        self.assertEqual(var.name(), "x.T")
        self.assertEqual(var.shape, (1, 2))

        x.save_value(np.matrix([1, 2]).T)
        self.assertEqual(var.value[0, 0], 1)
        self.assertEqual(var.value[0, 1], 2)

        var = self.C.T
        self.assertEqual(var.name(), "C.T")
        self.assertEqual(var.shape, (2, 3))

        # coeffs = var.canonical_form[0].coefficients()
        # mat = coeffs.values()[0][0]
        # self.assertEqual(mat.shape, (2,6))
        # self.assertEqual(mat[1,3], 1)

        index = var[1, 0]
        self.assertEqual(index.name(), "C.T[1, 0]")
        self.assertEqual(index.shape, tuple())

        var = x.T.T
        self.assertEqual(var.name(), "x.T.T")
        self.assertEqual(var.shape, (2, 1))

    # Test the Constant class.
    def test_constants(self):
        c = Constant(2.0)
        self.assertEqual(c.name(), str(2.0))

        c = Constant(2)
        self.assertEqual(c.value, 2)
        self.assertEqual(c.shape, tuple())
        self.assertEqual(c.curvature, s.CONSTANT)
        self.assertEqual(c.sign, s.NONNEG)
        self.assertEqual(Constant(-2).sign, s.NONPOS)
        self.assertEqual(Constant(0).sign, s.ZERO)
        # self.assertEqual(c.canonical_form[0].shape, (1, 1))
        # self.assertEqual(c.canonical_form[1], [])

        # coeffs = c.coefficients()
        # self.assertEqual(coeffs.keys(), [s.CONSTANT])
        # self.assertEqual(coeffs[s.CONSTANT], [2])

        # Test the sign.
        c = Constant([[2], [2]])
        self.assertEqual(c.shape, (1, 2))
        self.assertEqual(c.sign, s.NONNEG)
        self.assertEqual((-c).sign, s.NONPOS)
        self.assertEqual((0 * c).sign, s.ZERO)
        c = Constant([[2], [-2]])
        self.assertEqual(c.sign, s.UNKNOWN)

        c = Constant(np.zeros((2, 1)))
        self.assertEqual(c.shape, (2, 1))

        # Test sign of a complex expression.
        c = Constant([1, 2])
        self.assertEqual(c.shape, (2, ))
        A = Constant([[1, 1], [1, 1]])
        exp = c.T * A * c
        self.assertEqual(exp.sign, s.NONNEG)
        self.assertEqual((c.T * c).sign, s.NONNEG)
        exp = c.T.T
        self.assertEqual(exp.sign, s.NONNEG)
        exp = c.T * self.A
        self.assertEqual(exp.sign, s.UNKNOWN)

        # Test repr.
        self.assertEqual(repr(c), "Constant(CONSTANT, NONNEGATIVE, (2,))")

    def test_1D_array(self):
        """Test NumPy 1D arrays as constants.
        """
        c = np.array([1, 2])
        p = Parameter(2)
        p.value = [1, 1]
        self.assertEqual((c * p).value, 3)
        self.assertEqual((c * self.x).shape, tuple())

    # Test Parameter class on good inputs.
    def test_parameters_successes(self):
        # Parameter names and dimensions
        p = Parameter(name='p')
        self.assertEqual(p.name(), "p")
        self.assertEqual(p.shape, tuple())

        # Entry-wise constraints on parameter values.
        val = -np.ones((4, 3))
        val[0, 0] = 2
        p = Parameter((4, 3))
        p.value = val

        # Initialize a parameter with a value; later, set it to None.
        p = Parameter(value=10)
        self.assertEqual(p.value, 10)
        p.value = 10
        p.value = None
        self.assertEqual(p.value, None)

        # Test parameter representation.
        p = Parameter((4, 3), nonpos=True)
        self.assertEqual(repr(p), 'Parameter((4, 3), nonpos=True)')

        # Test valid diagonal parameter.
        p = Parameter((2, 2), diag=True)
        p.value = sp.csc_matrix(np.eye(2))
        self.assertItemsAlmostEqual(p.value.todense(), np.eye(2), places=10)

    def test_psd_nsd_parameters(self):
        # Test valid rank-deficeint PSD parameter.
        np.random.seed(42)
        a = np.random.normal(size=(100, 95))
        a2 = a.dot(a.T)  # This must be a PSD matrix.
        p = Parameter((100, 100), PSD=True)
        p.value = a2
        self.assertItemsAlmostEqual(p.value, a2, places=10)

        # Test positive definite matrix with non-distinct eigenvalues
        m, n = 10, 5
        A = np.random.randn(
            m, n) + 1j * np.random.randn(m, n)  # a random complex matrix
        A = np.dot(A.T.conj(),
                   A)  # a random Hermitian positive definite matrix
        A = np.bmat([[np.real(A), -np.imag(A)], [np.imag(A), np.real(A)]])
        p = Parameter(shape=(2 * n, 2 * n), PSD=True)
        p.value = A
        self.assertItemsAlmostEqual(p.value, A)

        # Test invalid PSD parameter
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), PSD=True, value=[[1, 0], [0, -1]])
        self.assertEqual(str(cm.exception),
                         "Parameter value must be positive semidefinite.")

        # Test invalid NSD parameter
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), NSD=True, value=[[1, 0], [0, -1]])
        self.assertEqual(str(cm.exception),
                         "Parameter value must be negative semidefinite.")

    # Test the Parameter class on bad inputs.
    def test_parameters_failures(self):
        p = Parameter(name='p')
        self.assertEqual(p.name(), "p")
        self.assertEqual(p.shape, tuple())

        p = Parameter((4, 3), nonneg=True)
        with self.assertRaises(Exception) as cm:
            p.value = 1
        self.assertEqual(str(cm.exception),
                         "Invalid dimensions () for Parameter value.")

        val = -np.ones((4, 3))
        val[0, 0] = 2

        p = Parameter((4, 3), nonneg=True)
        with self.assertRaises(Exception) as cm:
            p.value = val
        self.assertEqual(str(cm.exception),
                         "Parameter value must be nonnegative.")

        p = Parameter((4, 3), nonpos=True)
        with self.assertRaises(Exception) as cm:
            p.value = val
        self.assertEqual(str(cm.exception),
                         "Parameter value must be nonpositive.")

        with self.assertRaises(Exception) as cm:
            p = Parameter(2, 1, nonpos=True, value=[2, 1])
        self.assertEqual(str(cm.exception),
                         "Parameter value must be nonpositive.")

        with self.assertRaises(Exception) as cm:
            p = Parameter((4, 3), nonneg=True, value=[1, 2])
        self.assertEqual(str(cm.exception),
                         "Invalid dimensions (2,) for Parameter value.")

        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), diag=True, symmetric=True)
        self.assertEqual(
            str(cm.exception),
            "Cannot set more than one special attribute in Parameter.")

        # Boolean
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), boolean=True, value=[[1, 1], [1, -1]])
        self.assertEqual(str(cm.exception), "Parameter value must be boolean.")

        # Integer
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), integer=True, value=[[1, 1.5], [1, -1]])
        self.assertEqual(str(cm.exception), "Parameter value must be integer.")

        # Diag.
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), diag=True, value=[[1, 1], [1, -1]])
        self.assertEqual(str(cm.exception),
                         "Parameter value must be diagonal.")

        # Symmetric.
        with self.assertRaises(Exception) as cm:
            p = Parameter((2, 2), symmetric=True, value=[[1, 1], [-1, -1]])
        self.assertEqual(str(cm.exception),
                         "Parameter value must be symmetric.")

    def test_symmetric(self):
        """Test symmetric variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), symmetric=True)
        self.assertEqual(
            str(cm.exception),
            "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), symmetric=True)
        assert v.is_symmetric()
        v = Variable((2, 2), PSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), NSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_symmetric()
        assert self.a.is_symmetric()
        assert not self.A.is_symmetric()

        v = Variable((2, 2), symmetric=True)
        expr = v + v
        assert expr.is_symmetric()
        expr = -v
        assert expr.is_symmetric()
        expr = v.T
        assert expr.is_symmetric()
        expr = real(v)
        assert expr.is_symmetric()
        expr = imag(v)
        assert expr.is_symmetric()
        expr = conj(v)
        assert expr.is_symmetric()
        expr = promote(Variable(), (2, 2))
        assert expr.is_symmetric()

    def test_hermitian(self):
        """Test Hermitian variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), hermitian=True)
        self.assertEqual(
            str(cm.exception),
            "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), hermitian=True)
        assert v.is_hermitian()
        # v = Variable((2,2), PSD=True)
        # assert v.is_symmetric()
        # v = Variable((2,2), NSD=True)
        # assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_hermitian()

        v = Variable((2, 2), hermitian=True)
        expr = v + v
        assert expr.is_hermitian()
        expr = -v
        assert expr.is_hermitian()
        expr = v.T
        assert expr.is_hermitian()
        expr = real(v)
        assert expr.is_hermitian()
        expr = imag(v)
        assert expr.is_hermitian()
        expr = conj(v)
        assert expr.is_hermitian()
        expr = promote(Variable(), (2, 2))
        assert expr.is_hermitian()

    def test_round_attr(self):
        """Test rounding for attributes.
        """
        # Nonpos
        v = Variable(1, nonpos=True)
        self.assertAlmostEqual(v.project(1), 0)
        v = Variable(2, nonpos=True)
        self.assertItemsAlmostEqual(v.project(np.array([1, -1])), [0, -1])

        # Nonneg
        v = Variable(1, nonneg=True)
        self.assertAlmostEqual(v.project(-1), 0)
        v = Variable(2, nonneg=True)
        self.assertItemsAlmostEqual(v.project(np.array([1, -1])), [1, 0])

        # Boolean
        v = Variable((2, 2), boolean=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, 0]]).T),
                                    [1, 0, 1, 0])

        # Integer
        v = Variable((2, 2), integer=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1.6], [1, 0]]).T),
                                    [1, -2, 1, 0])

        # Symmetric
        v = Variable((2, 2), symmetric=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, 0]])),
                                    [1, 0, 0, 0])

        # PSD
        v = Variable((2, 2), PSD=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, -1]])),
                                    [1, 0, 0, 0])

        # NSD
        v = Variable((2, 2), NSD=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1], [1, -1]])),
                                    [0, 0, 0, -1])

        # diag
        v = Variable((2, 2), diag=True)
        self.assertItemsAlmostEqual(
            v.project(np.array([[1, -1], [1, 0]])).todense(), [1, 0, 0, 0])

        # Hermitian
        v = Variable((2, 2), hermitian=True)
        self.assertItemsAlmostEqual(v.project(np.array([[1, -1j], [1, 0]])),
                                    [1, 0.5 + 0.5j, 0.5 - 0.5j, 0])

        A = Constant(np.array([[1.0]]))
        self.assertEqual(A.is_psd(), True)
        self.assertEqual(A.is_nsd(), False)
        A = Constant(np.array([[-1.0]]))
        self.assertEqual(A.is_psd(), False)
        self.assertEqual(A.is_nsd(), True)
        A = Constant(np.array([[0.0]]))
        self.assertEqual(A.is_psd(), True)
        self.assertEqual(A.is_nsd(), True)

    # Test the AddExpresion class.
    def test_add_expression(self):
        # Vectors
        c = Constant([2, 2])
        exp = self.x + c
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        # self.assertEqual(exp.canonical_form[0].shape, (2, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), self.x.name() + " + " + c.name())
        self.assertEqual(exp.shape, (2, ))

        z = Variable(2, name='z')
        exp = exp + z + self.x

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.x + self.y)

        # Matrices
        exp = self.A + self.B
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (2, 2))

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.A + self.C)

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            AddExpression([self.A, self.C])

        # Test that sum is flattened.
        exp = self.x + c + self.x
        self.assertEqual(len(exp.args), 3)

        # Test repr.
        self.assertEqual(repr(exp), "Expression(AFFINE, UNKNOWN, (2,))")

    # Test the SubExpresion class.
    def test_sub_expression(self):
        # Vectors
        c = Constant([2, 2])
        exp = self.x - c
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        # self.assertEqual(exp.canonical_form[0].shape, (2, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name())
        self.assertEqual(exp.shape, (2, ))

        z = Variable(2, name='z')
        exp = exp - z - self.x

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.x - self.y)

        # Matrices
        exp = self.A - self.B
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (2, 2))

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.A - self.C)

        # Test repr.
        self.assertEqual(repr(self.x - c), "Expression(AFFINE, UNKNOWN, (2,))")

    # Test the MulExpresion class.
    def test_mul_expression(self):
        # Vectors
        c = Constant([[2], [2]])
        exp = c * self.x
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual((c[0] * self.x).sign, s.UNKNOWN)
        # self.assertEqual(exp.canonical_form[0].shape, (1, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), c.name() + " * " + self.x.name())
        self.assertEqual(exp.shape, (1, ))

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            ([2, 2, 3] * self.x)

        # Matrices: Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            Constant([[2, 1], [2, 2]]) * self.C

        # Affine times affine is okay
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            q = self.A * self.B
            self.assertTrue(q.is_quadratic())

        # Constant expressions
        T = Constant([[1, 2, 3], [3, 5, 5]])
        exp = (T + T) * self.B
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (3, 2))

        # Expression that would break sign multiplication without promotion.
        c = Constant([[2], [2], [-2]])
        exp = [[1], [2]] + c * self.C
        self.assertEqual(exp.sign, s.UNKNOWN)

    def test_matmul_expression(self):
        """Test matmul function, corresponding to .__matmul__( operator.
        """
        # Vectors
        c = Constant([[2], [2]])
        exp = c.__matmul__(self.x)
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        # self.assertEqual(exp.name(), c.name() + " .__matmul__( " + self.x.name())
        self.assertEqual(exp.shape, (1, ))

        with self.assertRaises(Exception) as cm:
            self.x.__matmul__(2)
        self.assertEqual(str(cm.exception),
                         "Scalar operands are not allowed, use '*' instead")

        # Incompatible dimensions
        with self.assertRaises(ValueError) as cm:
            (self.x.__matmul__(np.array([2, 2, 3])))

        # Incompatible dimensions
        with self.assertRaises(Exception) as cm:
            Constant([[2, 1], [2, 2]]).__matmul__(self.C)

        # Affine times affine is okay
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            q = self.A.__matmul__(self.B)
            self.assertTrue(q.is_quadratic())

        # # Nonaffine times nonconstant raises error
        # with warnings.catch_warnings():
        #     warnings.simplefilter("ignore")
        #     with self.assertRaises(Exception) as cm:
        #         (self.A.__matmul__(self.B).__matmul__(self.A))
        #     self.assertEqual(str(cm.exception), "Cannot multiply UNKNOWN and AFFINE.")

        # Constant expressions
        T = Constant([[1, 2, 3], [3, 5, 5]])
        exp = (T + T).__matmul__(self.B)
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (3, 2))

        # Expression that would break sign multiplication without promotion.
        c = Constant([[2], [2], [-2]])
        exp = [[1], [2]] + c.__matmul__(self.C)
        self.assertEqual(exp.sign, s.UNKNOWN)

    # Test the DivExpresion class.
    def test_div_expression(self):
        # Vectors
        exp = self.x / 2
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        # self.assertEqual(exp.canonical_form[0].shape, (2, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), c.name() + " * " + self.x.name())
        self.assertEqual(exp.shape, (2, ))

        with self.assertRaises(Exception) as cm:
            (self.x / [2, 2, 3])
        print(cm.exception)
        self.assertEqual(str(cm.exception),
                         "Can only divide by a scalar constant.")

        # Constant expressions.
        c = Constant(2)
        exp = c / (3 - 5)
        self.assertEqual(exp.curvature, s.CONSTANT)
        self.assertEqual(exp.shape, tuple())
        self.assertEqual(exp.sign, s.NONPOS)

        # Parameters.
        p = Parameter(nonneg=True)
        exp = 2 / p
        p.value = 2
        self.assertEqual(exp.value, 1)

        rho = Parameter(nonneg=True)
        rho.value = 1

        self.assertEqual(rho.sign, s.NONNEG)
        self.assertEqual(Constant(2).sign, s.NONNEG)
        self.assertEqual((Constant(2) / Constant(2)).sign, s.NONNEG)
        self.assertEqual((Constant(2) * rho).sign, s.NONNEG)
        self.assertEqual((rho / 2).sign, s.NONNEG)

    # Test the NegExpression class.
    def test_neg_expression(self):
        # Vectors
        exp = -self.x
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (2, ))
        assert exp.is_affine()
        self.assertEqual(exp.sign, s.UNKNOWN)
        assert not exp.is_nonneg()
        # self.assertEqual(exp.canonical_form[0].shape, (2, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), "-%s" % self.x.name())
        self.assertEqual(exp.shape, self.x.shape)

        # Matrices
        exp = -self.C
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (3, 2))

    # Test promotion of scalar constants.
    def test_scalar_const_promotion(self):
        # Vectors
        exp = self.x + 2
        self.assertEqual(exp.curvature, s.AFFINE)
        assert exp.is_affine()
        self.assertEqual(exp.sign, s.UNKNOWN)
        assert not exp.is_nonpos()
        # self.assertEqual(exp.canonical_form[0].shape, (2, 1))
        # self.assertEqual(exp.canonical_form[1], [])
        # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name())
        self.assertEqual(exp.shape, (2, ))

        self.assertEqual((4 - self.x).shape, (2, ))
        self.assertEqual((4 * self.x).shape, (2, ))
        self.assertEqual((4 <= self.x).shape, (2, ))
        self.assertEqual((4 == self.x).shape, (2, ))
        self.assertEqual((self.x >= 4).shape, (2, ))

        # Matrices
        exp = (self.A + 2) + 4
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual((3 * self.A).shape, (2, 2))

        self.assertEqual(exp.shape, (2, 2))

    # Test indexing expression.
    def test_index_expression(self):
        # Tuple of integers as key.
        exp = self.x[1]
        # self.assertEqual(exp.name(), "x[1,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        assert exp.is_affine()
        self.assertEqual(exp.shape, tuple())
        # coeff = exp.canonical_form[0].coefficients()[self.x][0]
        # self.assertEqual(coeff[0,1], 1)
        self.assertEqual(exp.value, None)

        exp = self.x[1].T
        # self.assertEqual(exp.name(), "x[1,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, tuple())

        with self.assertRaises(Exception) as cm:
            (self.x[2, 0])
        self.assertEqual(str(cm.exception), "Too many indices for expression.")

        with self.assertRaises(Exception) as cm:
            (self.x[2])
        self.assertEqual(str(cm.exception),
                         "Index 2 is out of bounds for axis 0 with size 2.")

        # Slicing
        exp = self.C[0:2, 1]
        # self.assertEqual(exp.name(), "C[0:2,1]")
        self.assertEqual(exp.shape, (2, ))
        exp = self.C[0:, 0:2]
        # self.assertEqual(exp.name(), "C[0:,0:2]")
        self.assertEqual(exp.shape, (3, 2))
        exp = self.C[0::2, 0::2]
        # self.assertEqual(exp.name(), "C[0::2,0::2]")
        self.assertEqual(exp.shape, (2, 1))
        exp = self.C[:3, :1:2]
        # self.assertEqual(exp.name(), "C[0:3,0]")
        self.assertEqual(exp.shape, (3, 1))
        exp = self.C[0:, 0]
        # self.assertEqual(exp.name(), "C[0:,0]")
        self.assertEqual(exp.shape, (3, ))

        c = Constant([[1, -2], [0, 4]])
        exp = c[1, 1]
        self.assertEqual(exp.curvature, s.CONSTANT)
        self.assertEqual(exp.sign, s.UNKNOWN)
        self.assertEqual(c[0, 1].sign, s.UNKNOWN)
        self.assertEqual(c[1, 0].sign, s.UNKNOWN)
        self.assertEqual(exp.shape, tuple())
        self.assertEqual(exp.value, 4)

        c = Constant([[1, -2, 3], [0, 4, 5], [7, 8, 9]])
        exp = c[0:3, 0:4:2]
        self.assertEqual(exp.curvature, s.CONSTANT)
        assert exp.is_constant()
        self.assertEqual(exp.shape, (3, 2))
        self.assertEqual(exp[0, 1].value, 7)

        # Slice of transpose
        exp = self.C.T[0:2, 1:2]
        self.assertEqual(exp.shape, (2, 1))

        # Arithmetic expression indexing
        exp = (self.x + self.z)[1]
        # self.assertEqual(exp.name(), "x[1,0] + z[1,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.sign, s.UNKNOWN)
        self.assertEqual(exp.shape, tuple())

        exp = (self.x + self.a)[1:2]
        # self.assertEqual(exp.name(), "x[1,0] + a")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (1, ))

        exp = (self.x - self.z)[1:2]
        # self.assertEqual(exp.name(), "x[1,0] - z[1,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (1, ))

        exp = (self.x - self.a)[1]
        # self.assertEqual(exp.name(), "x[1,0] - a")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, tuple())

        exp = (-self.x)[1]
        # self.assertEqual(exp.name(), "-x[1,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, tuple())

        c = Constant([[1, 2], [3, 4]])
        exp = (c * self.x)[1]
        # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, tuple())

        c = Constant([[1, 2], [3, 4]])
        exp = (c * self.a)[1, 0:1]
        # self.assertEqual(exp.name(), "2 * a")
        self.assertEqual(exp.curvature, s.AFFINE)
        self.assertEqual(exp.shape, (1, ))

    def test_none_idx(self):
        """Test None as index.
        """
        expr = self.a[None, None]
        self.assertEqual(expr.shape, (1, 1))

        expr = self.x[:, None]
        self.assertEqual(expr.shape, (2, 1))

        expr = self.x[None, :]
        self.assertEqual(expr.shape, (1, 2))

        expr = Constant([1, 2])[None, :]
        self.assertEqual(expr.shape, (1, 2))
        self.assertItemsAlmostEqual(expr.value, [1, 2])

    def test_out_of_bounds(self):
        """Test out of bounds indices.
        """
        with self.assertRaises(Exception) as cm:
            self.x[100]
        self.assertEqual(str(cm.exception),
                         "Index 100 is out of bounds for axis 0 with size 2.")

        with self.assertRaises(Exception) as cm:
            self.x[-100]
        self.assertEqual(
            str(cm.exception),
            "Index -100 is out of bounds for axis 0 with size 2.")

        exp = self.x[:-100]
        self.assertEqual(exp.size, (0, ))
        self.assertItemsAlmostEqual(exp.value, np.array([]))

        exp = self.C[100:2]
        self.assertEqual(exp.shape, (0, 2))

        exp = self.C[:, -199:2]
        self.assertEqual(exp.shape, (3, 2))

        exp = self.C[:, -199:-3]
        self.assertEqual(exp.shape, (3, 0))

    def test_neg_indices(self):
        """Test negative indices.
        """
        c = Constant([[1, 2], [3, 4]])
        exp = c[-1, -1]
        self.assertEqual(exp.value, 4)
        self.assertEqual(exp.shape, tuple())
        self.assertEqual(exp.curvature, s.CONSTANT)

        c = Constant([1, 2, 3, 4])
        exp = c[1:-1]
        self.assertItemsAlmostEqual(exp.value, [2, 3])
        self.assertEqual(exp.shape, (2, ))
        self.assertEqual(exp.curvature, s.CONSTANT)

        c = Constant([1, 2, 3, 4])
        exp = c[::-1]
        self.assertItemsAlmostEqual(exp.value, [4, 3, 2, 1])
        self.assertEqual(exp.shape, (4, ))
        self.assertEqual(exp.curvature, s.CONSTANT)

        x = Variable(4)
        self.assertEqual(x[::-1].shape, (4, ))
        Problem(Minimize(0), [x[::-1] == c]).solve()
        self.assertItemsAlmostEqual(x.value, [4, 3, 2, 1])

        x = Variable(2)
        self.assertEqual(x[::-1].shape, (2, ))

        x = Variable(100, name="x")
        self.assertEqual("x[0:99]", str(x[:-1]))

        c = Constant([[1, 2], [3, 4]])
        expr = c[0, 2:0:-1]
        self.assertEqual(expr.shape, (1, ))
        self.assertAlmostEqual(expr.value, 3)

        expr = c[0, 2::-1]
        self.assertEqual(expr.shape, (2, ))
        self.assertItemsAlmostEqual(expr.value, [3, 1])

    def test_logical_indices(self):
        """Test indexing with boolean arrays.
        """
        A = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
        C = Constant(A)

        # Boolean array.
        expr = C[A <= 2]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[A <= 2], expr.value)

        expr = C[A % 2 == 0]
        self.assertEqual(expr.shape, (6, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[A % 2 == 0], expr.value)

        # Boolean array for rows, index for columns.
        expr = C[np.array([True, False, True]), 3]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[np.array([True, False, True]), 3],
                                    expr.value)

        # Index for row, boolean array for columns.
        expr = C[1, np.array([True, False, False, True])]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[1,
                                      np.array([True, False, False, True])],
                                    expr.value)

        # Boolean array for rows, slice for columns.
        expr = C[np.array([True, True, True]), 1:3]
        self.assertEqual(expr.shape, (3, 2))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[np.array([True, True, True]), 1:3],
                                    expr.value)

        # Slice for row, boolean array for columns.
        expr = C[1:-1, np.array([True, False, True, True])]
        self.assertEqual(expr.shape, (1, 3))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(
            A[1:-1, np.array([True, False, True, True])], expr.value)

        # Boolean arrays for rows and columns.
        # Not sure what this does.
        expr = C[np.array([True, True, True]),
                 np.array([True, False, True, True])]
        self.assertEqual(expr.shape, (3, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(
            A[np.array([True, True, True]),
              np.array([True, False, True, True])], expr.value)

    def test_selector_list_indices(self):
        """Test indexing with lists/ndarrays of indices.
        """
        A = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
        C = Constant(A)

        # List for rows.
        expr = C[[1, 2]]
        self.assertEqual(expr.shape, (2, 4))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[[1, 2]], expr.value)

        # List for rows, index for columns.
        expr = C[[0, 2], 3]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[[0, 2], 3], expr.value)

        # Index for row, list for columns.
        expr = C[1, [0, 2]]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[1, [0, 2]], expr.value)

        # List for rows, slice for columns.
        expr = C[[0, 2], 1:3]
        self.assertEqual(expr.shape, (2, 2))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[[0, 2], 1:3], expr.value)

        # Slice for row, list for columns.
        expr = C[1:-1, [0, 2]]
        self.assertEqual(expr.shape, (1, 2))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[1:-1, [0, 2]], expr.value)

        # Lists for rows and columns.
        expr = C[[0, 1], [1, 3]]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[[0, 1], [1, 3]], expr.value)

        # Ndarray for rows, list for columns.
        expr = C[np.array([0, 1]), [1, 3]]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[np.array([0, 1]), [1, 3]], expr.value)

        # Ndarrays for rows and columns.
        expr = C[np.array([0, 1]), np.array([1, 3])]
        self.assertEqual(expr.shape, (2, ))
        self.assertEqual(expr.sign, s.NONNEG)
        self.assertItemsAlmostEqual(A[np.array([0, 1]),
                                      np.array([1, 3])], expr.value)

    def test_powers(self):
        exp = self.x**2
        self.assertEqual(exp.curvature, s.CONVEX)
        exp = self.x**0.5
        self.assertEqual(exp.curvature, s.CONCAVE)
        exp = self.x**-1
        self.assertEqual(exp.curvature, s.CONVEX)

    def test_sum(self):
        """Test built-in sum. Not good usage.
        """
        self.a.value = 1
        expr = sum(self.a)
        self.assertEqual(expr.value, 1)

        self.x.value = [1, 2]
        expr = sum(self.x)
        self.assertEqual(expr.value, 3)

    def test_var_copy(self):
        """Test the copy function for variable types.
        """
        x = Variable((3, 4), name="x")
        y = x.copy()
        self.assertEqual(y.shape, (3, 4))
        self.assertEqual(y.name(), "x")

        x = Variable((5, 5), PSD=True, name="x")
        y = x.copy()
        self.assertEqual(y.shape, (5, 5))

    def test_param_copy(self):
        """Test the copy function for Parameters.
        """
        x = Parameter((3, 4), name="x", nonneg=True)
        y = x.copy()
        self.assertEqual(y.shape, (3, 4))
        self.assertEqual(y.name(), "x")
        self.assertEqual(y.sign, "NONNEGATIVE")

    def test_constant_copy(self):
        """Test the copy function for Constants.
        """
        x = Constant(2)
        y = x.copy()
        self.assertEqual(y.shape, tuple())
        self.assertEqual(y.value, 2)

    def test_is_pwl(self):
        """Test is_pwl()
        """
        A = np.ones((2, 3))
        b = np.ones(2)

        expr = A * self.y - b
        self.assertEqual(expr.is_pwl(), True)

        expr = maximum(1, 3 * self.y)
        self.assertEqual(expr.is_pwl(), True)

        expr = abs(self.y)
        self.assertEqual(expr.is_pwl(), True)

        expr = pnorm(3 * self.y, 1)
        self.assertEqual(expr.is_pwl(), True)

        expr = pnorm(3 * self.y**2, 1)
        self.assertEqual(expr.is_pwl(), False)
Beispiel #16
0
    def test_variable(self):
        """Test the Variable class.
        """
        x = Variable(2, complex=False)
        y = Variable(2, complex=True)
        z = Variable(2, imag=True)

        assert not x.is_complex()
        assert not x.is_imag()
        assert y.is_complex()
        assert not y.is_imag()
        assert z.is_complex()
        assert z.is_imag()

        with self.assertRaises(Exception) as cm:
            x.value = np.array([1j, 0.])
        self.assertEqual(str(cm.exception), "Variable value must be real.")

        y.value = np.array([1., 0.])
        y.value = np.array([1j, 0.])

        with self.assertRaises(Exception) as cm:
            z.value = np.array([1., 0.])
        self.assertEqual(str(cm.exception),
                         "Variable value must be imaginary.")
Beispiel #17
0
    def test_hermitian(self):
        """Test Hermitian variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), hermitian=True)
        self.assertEqual(
            str(cm.exception),
            "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), hermitian=True)
        assert v.is_hermitian()
        # v = Variable((2,2), PSD=True)
        # assert v.is_symmetric()
        # v = Variable((2,2), NSD=True)
        # assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_hermitian()

        v = Variable((2, 2), hermitian=True)
        expr = v + v
        assert expr.is_hermitian()
        expr = -v
        assert expr.is_hermitian()
        expr = v.T
        assert expr.is_hermitian()
        expr = real(v)
        assert expr.is_hermitian()
        expr = imag(v)
        assert expr.is_hermitian()
        expr = conj(v)
        assert expr.is_hermitian()
        expr = promote(Variable(), (2, 2))
        assert expr.is_hermitian()
Beispiel #18
0
def exp_canon(expr, args):
    x = promote(args[0], expr.shape)
    t = Variable(expr.shape)
    ones = Constant(np.ones(expr.shape))
    constraints = [ExpCone(x, ones, t)]
    return t, constraints
Beispiel #19
0
    def test_variable(self):
        x = Variable(2)
        y = Variable(2)
        assert y.name() != x.name()

        x = Variable(2, name='x')
        y = Variable()
        self.assertEqual(x.name(), 'x')
        self.assertEqual(x.shape, (2, ))
        self.assertEqual(y.shape, tuple())
        self.assertEqual(x.curvature, s.AFFINE)
        # self.assertEqual(x.canonical_form[0].shape, (2, 1))
        # self.assertEqual(x.canonical_form[1], [])

        self.assertEqual(repr(self.x), "Variable((2,))")
        self.assertEqual(repr(self.A), "Variable((2, 2))")

        # # Scalar variable
        # coeff = self.a.coefficients()
        # self.assertEqual(coeff[self.a.id], [1])

        # # Vector variable.
        # coeffs = x.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [x.id])
        # vec = coeffs[x.id][0]
        # self.assertEqual(vec.shape, (2,2))
        # self.assertEqual(vec[0,0], 1)

        # # Matrix variable.
        # coeffs = self.A.coefficients()
        # self.assertItemsEqual(coeffs.keys(), [self.A.id])
        # self.assertEqual(len(coeffs[self.A.id]), 2) or 0 in self.shape
        # mat = coeffs[self.A.id][1]
        # self.assertEqual(mat.shape, (2,4))
        # self.assertEqual(mat[0,2], 1)

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 2), diag=True, symmetric=True)
        self.assertEqual(
            str(cm.exception),
            "Cannot set more than one special attribute in Variable.")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, 0))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0).")

        with self.assertRaises(Exception) as cm:
            p = Variable((2, .5))
        self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0.5).")
Beispiel #20
0
 def setUp(self) -> None:
     self.x = Variable(name='x')
     self.y = Variable(3, name='y')
     self.z = Variable(name='z')