Пример #1
0
 def test_numpy_matrix(self):
     interface = intf.get_matrix_interface(np.matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     mat = interface.const_to_matrix([[1], [2], [3]])
     self.assertEquals(mat[0, 0], 1)
     # identity
     mat = interface.identity(4)
     cvxopt_dense = intf.get_matrix_interface(cvxopt.matrix)
     cmp_mat = interface.const_to_matrix(cvxopt_dense.identity(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert not (mat - cmp_mat).any()
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     assert not (mat - np.matrix("2 4; 4 6")).any()
     # Sign
     self.sign_for_intf(interface)
Пример #2
0
 def test_ndarray(self):
     interface = intf.get_matrix_interface(np.ndarray)
     # const_to_matrix
     mat = interface.const_to_matrix([1,2,3])
     self.assertEquals(interface.size(mat), (3,1))
     mat = interface.const_to_matrix([1,2])
     self.assertEquals(interface.size(mat), (2,1))
     # CVXOPT sparse conversion
     tmp = intf.get_matrix_interface(cvxopt.spmatrix).const_to_matrix([1,2,3])
     mat = interface.const_to_matrix(tmp)
     assert (mat == interface.const_to_matrix([1,2,3])).all()
     # identity
     mat = interface.identity(4)
     cvxopt_dense = intf.get_matrix_interface(cvxopt.matrix)
     cmp_mat = interface.const_to_matrix(cvxopt_dense.identity(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert (mat == cmp_mat).all()
     # scalar_matrix
     mat = interface.scalar_matrix(2,4,3)
     self.assertEquals(interface.size(mat), (4,3))
     self.assertEquals(interface.index(mat, (1,2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1,2,3],[3,4,5]])
     mat = interface.reshape(mat, (6,1))
     self.assertEquals(interface.index(mat, (4,0)), 4)
     # index
     mat = interface.const_to_matrix([[1,2,3,4],[3,4,5,6]])
     self.assertEquals( interface.index(mat, (0,1)), 3)
     mat = interface.index(mat, (slice(1,4,2), slice(0,2,None)))
     self.assertEquals(list(mat.flatten('C')), [2,4,4,6])
     # Scalars and matrices.
     scalar = interface.const_to_matrix(2)
     mat = interface.const_to_matrix([1,2,3])
     assert (scalar*mat == interface.const_to_matrix([2,4,6])).all()
     assert (scalar - mat == interface.const_to_matrix([1,0,-1])).all()
Пример #3
0
 def test_conversion_between_intf(self):
     """Test conversion between every pair of interfaces.
     """
     interfaces = [intf.get_matrix_interface(cvxopt.matrix),
                   intf.get_matrix_interface(cvxopt.spmatrix),
                   intf.get_matrix_interface(np.ndarray),
                   intf.get_matrix_interface(np.matrix),
                   intf.get_matrix_interface(sp.csc_matrix)]
     cmp_mat = [[1,2,3,4],[3,4,5,6],[-1,0,2,4]]
     for i in range(len(interfaces)):
         for j in range(i+1, len(interfaces)):
             intf1 = interfaces[i]
             mat1 = intf1.const_to_matrix(cmp_mat)
             intf2 = interfaces[j]
             mat2 = intf2.const_to_matrix(cmp_mat)
             for col in range(len(cmp_mat)):
                 for row in range(len(cmp_mat[0])):
                     key = (slice(row, row+1, None),
                            slice(col, col+1, None))
                     self.assertEqual(intf1.index(mat1, key),
                                      intf2.index(mat2, key))
                     # Convert between the interfaces.
                     self.assertEqual(cmp_mat[col][row],
                                      intf1.index(intf1.const_to_matrix(mat2), key))
                     self.assertEqual(intf2.index(intf2.const_to_matrix(mat1), key),
                                      cmp_mat[col][row])
Пример #4
0
 def test_numpy_matrix(self):
     interface = intf.get_matrix_interface(np.matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEqual(interface.size(mat), (3, 1))
     mat = interface.const_to_matrix([[1], [2], [3]])
     self.assertEqual(mat[0, 0], 1)
     # identity
     # mat = interface.identity(4)
     # cvxopt_dense = intf.get_matrix_interface(cvxopt.matrix)
     # cmp_mat = interface.const_to_matrix(cvxopt_dense.identity(4))
     # self.assertEqual(interface.size(mat), interface.size(cmp_mat))
     # assert not (mat - cmp_mat).any()
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEqual(interface.size(mat), (4, 3))
     self.assertEqual(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEqual(interface.index(mat, (4, 0)), 4)
     mat = interface.const_to_matrix(1, convert_scalars=True)
     self.assertEqual(type(interface.reshape(mat, (1, 1))), type(mat))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEqual(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     assert not (mat - np.matrix("2 4; 4 6")).any()
     # Sign
     self.sign_for_intf(interface)
Пример #5
0
 def test_cvxopt_sparse(self):
     interface = intf.get_matrix_interface(cvxopt.spmatrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert not mat - cmp_mat
     assert intf.is_sparse(mat)
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     # Test scalars.
     scalar = interface.scalar_matrix(1, 1, 1)
     self.assertEquals(type(scalar), cvxopt.spmatrix)
     scalar = interface.scalar_matrix(1, 1, 3)
     self.assertEquals(scalar.size, (1, 3))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertEquals(list(mat), [2, 4, 4, 6])
     # Sign
     self.sign_for_intf(interface)
Пример #6
0
    def test_mul_elemwise(self):
        """Tests problems with mul_elemwise.
        """
        c = [[1, -1], [2, -2]]
        expr = mul_elemwise(c, self.A)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.A == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, -5] + [10, -10])

        # Test with a sparse matrix.
        import cvxopt
        interface = intf.get_matrix_interface(cvxopt.spmatrix)
        c = interface.const_to_matrix([1,2])
        expr = mul_elemwise(c, self.x)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.x == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, 10])

        # Test promotion.
        c = [[1, -1], [2, -2]]
        expr = mul_elemwise(c, self.a)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.a == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, -5] + [10, -10])
Пример #7
0
 def test_cvxopt_dense(self):
     interface = intf.get_matrix_interface(cvxopt.matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     sp_mat = sp.coo_matrix(([1, 2], ([3, 4], [2, 1])), (5, 5))
     mat = interface.const_to_matrix(sp_mat)
     self.assertEquals(interface.size(mat), (5, 5))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(type(mat), type(cmp_mat))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert not mat - cmp_mat
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertEquals(list(mat), [2, 4, 4, 6])
     # Sign
     self.sign_for_intf(interface)
Пример #8
0
 def test_scipy_sparse(self):
     interface = intf.get_matrix_interface(sp.csc_matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1,2,3])
     self.assertEquals(interface.size(mat), (3,1))
     C = cvxopt.spmatrix([1,1,1,1,1],[0,1,2,0,0,],[0,0,0,1,2])
     mat = interface.const_to_matrix(C)
     self.assertEquals(interface.size(mat), (3, 3))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert (mat - cmp_mat).nnz == 0
     # scalar_matrix
     mat = interface.scalar_matrix(2,4,3)
     self.assertEquals(interface.size(mat), (4,3))
     self.assertEquals(interface.index(mat, (1,2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1,2,3],[3,4,5]])
     mat = interface.reshape(mat, (6,1))
     self.assertEquals(interface.index(mat, (4,0)), 4)
     # Test scalars.
     scalar = interface.scalar_matrix(1, 1, 1)
     self.assertEquals(type(scalar), np.ndarray)
     scalar = interface.scalar_matrix(1, 1, 3)
     self.assertEquals(scalar.shape, (1,3))
     # index
     mat = interface.const_to_matrix([[1,2,3,4],[3,4,5,6]])
     self.assertEquals( interface.index(mat, (0,1)), 3)
     mat = interface.index(mat, (slice(1,4,2), slice(0,2,None)))
     assert not (mat - np.matrix("2 4; 4 6")).any()
Пример #9
0
    def test_mul_elemwise(self):
        """Tests problems with mul_elemwise.
        """
        c = [[1, -1], [2, -2]]
        expr = mul_elemwise(c, self.A)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.A == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, -5] + [10, -10])

        # Test with a sparse matrix.
        import cvxopt
        interface = intf.get_matrix_interface(cvxopt.spmatrix)
        c = interface.const_to_matrix([1,2])
        expr = mul_elemwise(c, self.x)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.x == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, 10])

        # Test promotion.
        c = [[1, -1], [2, -2]]
        expr = mul_elemwise(c, self.a)
        obj = Minimize(normInf(expr))
        p = Problem(obj, [self.a == 5])
        result = p.solve()
        self.assertAlmostEqual(result, 10)
        self.assertItemsAlmostEqual(expr.value, [5, -5] + [10, -10])
Пример #10
0
def quad_form(x, P):
    """ Alias for :math:`x^T P x`.

    """
    x, P = map(Expression.cast_to_const, (x, P))
    # Check dimensions.
    n = P.size[0]
    if P.size[1] != n or x.size != (n,1):
        raise Exception("Invalid dimensions for arguments.")
    if x.is_constant():
        return x.T * P * x
    elif P.is_constant():
        np_intf = intf.get_matrix_interface(np.ndarray)
        P = np_intf.const_to_matrix(P.value)
        # Force symmetry
        P = (P + P.T) / 2.0
        scale, M1, M2 = _decomp_quad(P)
        ret = 0
        if M1.size > 0:
            ret += scale * sum_squares(Constant(M1.T) * x)
        if M2.size > 0:
            ret -= scale * sum_squares(Constant(M2.T) * x)
        return ret
    else:
        raise Exception("At least one argument to quad_form must be constant.")
Пример #11
0
 def test_cvxopt_sparse(self):
     interface = intf.get_matrix_interface(cvxopt.spmatrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert not mat - cmp_mat
     assert intf.is_sparse(mat)
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     mat = interface.const_to_matrix(1, convert_scalars=True)
     self.assertEquals(type(interface.reshape(mat, (1, 1))), type(mat))
     # Test scalars.
     scalar = interface.scalar_matrix(1, 1, 1)
     self.assertEquals(type(scalar), cvxopt.spmatrix)
     scalar = interface.scalar_matrix(1, 1, 3)
     self.assertEquals(scalar.size, (1, 3))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertEquals(list(mat), [2, 4, 4, 6])
     # Sign
     self.sign_for_intf(interface)
Пример #12
0
 def test_ndarray(self):
     interface = intf.get_matrix_interface(np.ndarray)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     mat = interface.const_to_matrix([1, 2])
     self.assertEquals(interface.size(mat), (2, 1))
     # CVXOPT sparse conversion
     tmp = intf.get_matrix_interface(cvxopt.spmatrix).const_to_matrix(
         [1, 2, 3])
     mat = interface.const_to_matrix(tmp)
     assert (mat == interface.const_to_matrix([1, 2, 3])).all()
     # identity
     mat = interface.identity(4)
     cvxopt_dense = intf.get_matrix_interface(cvxopt.matrix)
     cmp_mat = interface.const_to_matrix(cvxopt_dense.identity(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert (mat == cmp_mat).all()
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     mat = interface.const_to_matrix(1, convert_scalars=True)
     self.assertEquals(type(interface.reshape(mat, (1, 1))), type(mat))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertEquals(list(mat.flatten('C')), [2, 4, 4, 6])
     # Scalars and matrices.
     scalar = interface.const_to_matrix(2)
     mat = interface.const_to_matrix([1, 2, 3])
     assert (scalar * mat == interface.const_to_matrix([2, 4, 6])).all()
     assert (scalar - mat == interface.const_to_matrix([1, 0, -1])).all()
     # Sign
     self.sign_for_intf(interface)
     # Size.
     assert interface.size(np.array([1, 2, 3])) == (3, 1)
Пример #13
0
 def test_conversion_between_intf(self):
     """Test conversion between every pair of interfaces.
     """
     interfaces = [intf.get_matrix_interface(np.ndarray),
                   intf.get_matrix_interface(np.matrix),
                   intf.get_matrix_interface(sp.csc_matrix)]
     cmp_mat = [[1, 2, 3, 4], [3, 4, 5, 6], [-1, 0, 2, 4]]
     for i in range(len(interfaces)):
         for j in range(i+1, len(interfaces)):
             intf1 = interfaces[i]
             mat1 = intf1.const_to_matrix(cmp_mat)
             intf2 = interfaces[j]
             mat2 = intf2.const_to_matrix(cmp_mat)
             for col in range(len(cmp_mat)):
                 for row in range(len(cmp_mat[0])):
                     key = (slice(row, row+1, None),
                            slice(col, col+1, None))
                     self.assertEqual(intf1.index(mat1, key),
                                      intf2.index(mat2, key))
                     # Convert between the interfaces.
                     self.assertEqual(cmp_mat[col][row],
                                      intf1.index(intf1.const_to_matrix(mat2), key))
                     self.assertEqual(intf2.index(intf2.const_to_matrix(mat1), key),
                                      cmp_mat[col][row])
Пример #14
0
 def test_scipy_sparse(self):
     """Test cvxopt sparse interface.
     """
     interface = intf.get_matrix_interface(sp.csc_matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEqual(interface.shape(mat), (3, 1))
     # C = cvxopt.spmatrix([1, 1, 1, 1, 1], [0, 1, 2, 0, 0, ], [0, 0, 0, 1, 2])
     # mat = interface.const_to_matrix(C)
     # self.assertEqual(interface.shape(mat), (3, 3))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEqual(interface.shape(mat), interface.shape(cmp_mat))
     assert (mat - cmp_mat).nnz == 0
     # scalar_matrix
     mat = interface.scalar_matrix(2, (4, 3))
     self.assertEqual(interface.shape(mat), (4, 3))
     self.assertEqual(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEqual(interface.index(mat, (4, 0)), 4)
     # Test scalars.
     scalar = interface.scalar_matrix(1, (1, 1))
     self.assertEqual(type(scalar), np.ndarray)
     scalar = interface.scalar_matrix(1, (1, 3))
     self.assertEqual(scalar.shape, (1, 3))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEqual(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     assert not (mat - np.array([[2, 4], [4, 6]])).any()
     # scalar value
     mat = sp.eye(1)
     self.assertEqual(intf.scalar_value(mat), 1.0)
     # Sign
     self.sign_for_intf(interface)
     # Complex
     # define sparse matrix [[0, 1j],[-1j,0]]
     row = np.array([0, 1])
     col = np.array([1, 0])
     data = np.array([1j, -1j])
     A = sp.csr_matrix((data, (row, col)), shape=(2, 2))
     mat = interface.const_to_matrix(A)
     self.assertEquals(mat[0, 1], 1j)
     self.assertEquals(mat[1, 0], -1j)
Пример #15
0
def quad_form(x, P):
    """ Alias for :math:`x^T P x`.

    """
    x, P = map(Expression.cast_to_const, (x, P))
    # Check dimensions.
    n = P.size[0]
    if P.size[1] != n or x.size != (n, 1):
        raise Exception("Invalid dimensions for arguments.")
    if x.is_constant():
        return x.T * P * x
    elif P.is_constant():
        np_intf = intf.get_matrix_interface(np.ndarray)
        P = np_intf.const_to_matrix(P.value)
        sgn, scale, M = _decomp_quad(P)
        return sgn * scale * square(norm(Constant(M.T) * x))
    else:
        raise Exception("At least one argument to quad_form must be constant.")
Пример #16
0
def quad_form(x, P):
    """ Alias for :math:`x^T P x`.

    """
    x, P = list(map(Expression.cast_to_const, (x, P)))
    # Check dimensions.
    n = P.size[0]
    if P.size[1] != n or x.size != (n,1):
        raise Exception("Invalid dimensions for arguments.")
    if x.is_constant():
        return x.T * P * x
    elif P.is_constant():
        np_intf = intf.get_matrix_interface(np.ndarray)
        P = np_intf.const_to_matrix(P.value)
        sgn, scale, M = _decomp_quad(P)
        return sgn * scale * square(norm(Constant(M.T) * x))
    else:
        raise Exception("At least one argument to quad_form must be constant.")
Пример #17
0
 def test_scipy_sparse(self):
     interface = intf.get_matrix_interface(sp.csc_matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     C = cvxopt.spmatrix([1, 1, 1, 1, 1], [
         0,
         1,
         2,
         0,
         0,
     ], [0, 0, 0, 1, 2])
     mat = interface.const_to_matrix(C)
     self.assertEquals(interface.size(mat), (3, 3))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert (mat - cmp_mat).nnz == 0
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     mat = interface.const_to_matrix(1, convert_scalars=True)
     self.assertEquals(type(interface.reshape(mat, (1, 1))), type(mat))
     # Test scalars.
     scalar = interface.scalar_matrix(1, 1, 1)
     self.assertEquals(type(scalar), np.ndarray)
     scalar = interface.scalar_matrix(1, 1, 3)
     self.assertEquals(scalar.shape, (1, 3))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     assert not (mat - np.matrix("2 4; 4 6")).any()
     # scalar value
     mat = sp.eye(1)
     self.assertEqual(intf.scalar_value(mat), 1.0)
     # Sign
     self.sign_for_intf(interface)
Пример #18
0
def quad_form(x, P):
    """ Alias for :math:`x^T P x`.

    """
    x, P = map(Expression.cast_to_const, (x, P))
    # Check dimensions.
    n = P.size[0]
    if P.size[1] != n or x.size != (n,1):
        raise Exception("Invalid dimensions for arguments.")
    if x.is_constant():
        return x.T * P * x
    elif P.is_constant():
        np_intf = intf.get_matrix_interface(np.ndarray)
        P = np_intf.const_to_matrix(P.value)
        # P must be symmetric.
        if not np.allclose(P, P.T):
            msg = "P is not symmetric."
            raise CvxPyDomainError(msg)
        sgn, scale, M = _decomp_quad(P)
        return sgn * scale * square(norm(Constant(M.T) * x))
    else:
        raise Exception("At least one argument to quad_form must be constant.")
Пример #19
0
 def test_numpy_matrix(self) -> None:
     interface = intf.get_matrix_interface(np.matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEqual(interface.shape(mat), (3, 1))
     mat = interface.const_to_matrix([[1], [2], [3]])
     self.assertEqual(mat[0, 0], 1)
     mat = interface.scalar_matrix(2, (4, 3))
     self.assertEqual(interface.shape(mat), (4, 3))
     self.assertEqual(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEqual(interface.index(mat, (4, 0)), 4)
     mat = interface.const_to_matrix(1, convert_scalars=True)
     self.assertEqual(type(interface.reshape(mat, (1, 1))), type(mat))
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEqual(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertFalse((mat - np.array([[2, 4], [4, 6]])).any())
     # Sign
     self.sign_for_intf(interface)
Пример #20
0
 def test_cvxopt_dense(self):
     interface = intf.get_matrix_interface(cvxopt.matrix)
     # const_to_matrix
     mat = interface.const_to_matrix([1, 2, 3])
     self.assertEquals(interface.size(mat), (3, 1))
     # identity
     mat = interface.identity(4)
     cmp_mat = interface.const_to_matrix(np.eye(4))
     self.assertEquals(type(mat), type(cmp_mat))
     self.assertEquals(interface.size(mat), interface.size(cmp_mat))
     assert not mat - cmp_mat
     # scalar_matrix
     mat = interface.scalar_matrix(2, 4, 3)
     self.assertEquals(interface.size(mat), (4, 3))
     self.assertEquals(interface.index(mat, (1, 2)), 2)
     # reshape
     mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]])
     mat = interface.reshape(mat, (6, 1))
     self.assertEquals(interface.index(mat, (4, 0)), 4)
     # index
     mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]])
     self.assertEquals(interface.index(mat, (0, 1)), 3)
     mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None)))
     self.assertEquals(list(mat), [2, 4, 4, 6])
Пример #21
0
class Problem(u.Canonical):
    """A convex optimization problem.

    Attributes
    ----------
    objective : Minimize or Maximize
        The expression to minimize or maximize.
    constraints : list
        The constraints on the problem variables.
    """

    # The solve methods available.
    REGISTERED_SOLVE_METHODS = {}
    # Interfaces for interacting with matrices.
    _SPARSE_INTF = intf.DEFAULT_SPARSE_INTERFACE
    _DENSE_INTF = intf.DEFAULT_INTERFACE
    _CVXOPT_DENSE_INTF = intf.get_matrix_interface(cvxopt.matrix)
    _CVXOPT_SPARSE_INTF = intf.get_matrix_interface(cvxopt.spmatrix)

    def __init__(self, objective, constraints=None):
        if constraints is None:
            constraints = []
        self.objective = objective
        self.constraints = constraints
        self._value = None
        self._status = None

    @property
    def value(self):
        """The value from the last time the problem was solved.

        Returns
        -------
        float or None
        """
        return self._value

    @property
    def status(self):
        """The status from the last time the problem was solved.

        Returns
        -------
        str
        """
        return self._status

    def is_dcp(self):
        """Does the problem satisfy DCP rules?
        """
        return all(exp.is_dcp() for exp in self.constraints + [self.objective])

    def _filter_constraints(self, constraints):
        """Separate the constraints by type.

        Parameters
        ----------
        constraints : list
            A list of constraints.

        Returns
        -------
        dict
            A map of type key to an ordered set of constraints.
        """
        constr_map = {
            s.EQ: [],
            s.LEQ: [],
            s.SOC: [],
            s.SOC_EW: [],
            s.SDP: [],
            s.EXP: []
        }
        for c in constraints:
            if isinstance(c, lo.LinEqConstr):
                constr_map[s.EQ].append(c)
            elif isinstance(c, lo.LinLeqConstr):
                constr_map[s.LEQ].append(c)
            elif isinstance(c, SOC):
                constr_map[s.SOC].append(c)
            elif isinstance(c, SDP):
                constr_map[s.SDP].append(c)
            elif isinstance(c, ExpCone):
                constr_map[s.EXP].append(c)
        return constr_map

    def canonicalize(self):
        """Computes the graph implementation of the problem.

        Returns
        -------
        tuple
            (affine objective,
             constraints dict)
        """
        constraints = []
        obj, constr = self.objective.canonical_form
        constraints += constr
        unique_constraints = list(unique(self.constraints, key=lambda c: c.id))
        for constr in unique_constraints:
            constraints += constr.canonical_form[1]
        constr_map = self._filter_constraints(constraints)

        return (obj, constr_map)

    def _format_for_solver(self, constr_map, solver):
        """Formats the problem for the solver.

        Parameters
        ----------
        constr_map : dict
            A map of constraint type to a list of constraints.
        solver: str
            The solver being targetted.

        Returns
        -------
        dict
            The dimensions of the cones.
        """
        dims = {}
        dims["f"] = sum(c.size[0] * c.size[1] for c in constr_map[s.EQ])
        dims["l"] = sum(c.size[0] * c.size[1] for c in constr_map[s.LEQ])
        # Formats SOC, SOC_EW, and SDP constraints for the solver.
        nonlin = constr_map[s.SOC] + constr_map[s.SDP]
        for constr in nonlin:
            for ineq_constr in constr.format():
                constr_map[s.LEQ].append(ineq_constr)
        # Elemwise SOC constraints have an SOC constraint
        # for each element in their arguments.
        dims["q"] = []
        for constr in constr_map[s.SOC]:
            for cone_size in constr.size:
                dims["q"].append(cone_size[0])
        dims["s"] = [c.size[0] for c in constr_map[s.SDP]]

        # Format exponential cone constraints.
        if solver == s.CVXOPT:
            for constr in constr_map[s.EXP]:
                constr_map[s.EQ] += constr.format(s.CVXOPT)
        elif solver == s.SCS:
            for constr in constr_map[s.EXP]:
                constr_map[s.LEQ] += constr.format(s.SCS)
            dims["ep"] = sum(c.size[0] * c.size[1] for c in constr_map[s.EXP])

        # Remove redundant constraints.
        for key in [s.EQ, s.LEQ]:
            constraints = unique(constr_map[key], key=lambda c: c.constr_id)
            constr_map[key] = list(constraints)

        return dims

    @staticmethod
    def _constraints_count(constr_map):
        """Returns the number of internal constraints.
        """
        return sum([len(cset) for cset in constr_map.values()])

    def _choose_solver(self, constr_map):
        """Determines the appropriate solver.

        Parameters
        ----------
        constr_map: dict
            A dict of the canonicalized constraints.

        Returns
        -------
        str
            The solver that will be used.
        """
        # If no constraints, use ECOS.
        if self._constraints_count(constr_map) == 0:
            return s.ECOS
        # If SDP, defaults to CVXOPT.
        elif constr_map[s.SDP]:
            return s.CVXOPT
        # If EXP cone without SDP, defaults to SCS.
        elif constr_map[s.EXP]:
            return s.SCS
        # Otherwise use ECOS.
        else:
            return s.ECOS

    def _validate_solver(self, constr_map, solver):
        """Raises an exception if the solver cannot solve the problem.

        Parameters
        ----------
        constr_map: dict
            A dict of the canonicalized constraints.
        solver : str
            The solver to be used.
        """
        if (constr_map[s.SDP] and not solver in s.SDP_CAPABLE) or \
           (constr_map[s.EXP] and not solver in s.EXP_CAPABLE) or \
           (self._constraints_count(constr_map) == 0 and solver == s.SCS):
            raise Exception("The solver %s cannot solve the problem." % solver)

    def variables(self):
        """Returns a list of the variables in the problem.
        """
        vars_ = self.objective.variables()
        for constr in self.constraints:
            vars_ += constr.variables()
        # Remove duplicates.
        return list(set(vars_))

    def parameters(self):
        """Returns a list of the parameters in the problem.
        """
        params = self.objective.parameters()
        for constr in self.constraints:
            params += constr.parameters()
        # Remove duplicates.
        return list(set(params))

    def solve(self, *args, **kwargs):
        """Solves the problem using the specified method.

        Parameters
        ----------
        method : function
            The solve method to use.
        solver : str, optional
            The solver to use.
        verbose : bool, optional
            Overrides the default of hiding solver output.
        solver_specific_opts : dict, optional
            A dict of options that will be passed to the specific solver.
            In general, these options will override any default settings
            imposed by cvxpy.

        Returns
        -------
        float
            The optimal value for the problem, or a string indicating
            why the problem could not be solved.
        """
        func_name = kwargs.pop("method", None)
        if func_name is not None:
            func = Problem.REGISTERED_SOLVE_METHODS[func_name]
            return func(self, *args, **kwargs)
        else:
            return self._solve(*args, **kwargs)

    @classmethod
    def register_solve(cls, name, func):
        """Adds a solve method to the Problem class.

        Parameters
        ----------
        name : str
            The keyword for the method.
        func : function
            The function that executes the solve method.
        """
        cls.REGISTERED_SOLVE_METHODS[name] = func

    def get_problem_data(self, solver):
        """Returns the problem data used in the call to the solver.

        Parameters
        ----------
        solver : str
            The solver the problem data is for.

        Returns
        -------
        tuple
            arguments to solver
        """
        objective, constr_map = self.canonicalize()
        # Raise an error if the solver cannot handle the problem.
        self._validate_solver(constr_map, solver)
        dims = self._format_for_solver(constr_map, solver)
        all_ineq = constr_map[s.EQ] + constr_map[s.LEQ]
        var_offsets, var_sizes, x_length = self._get_var_offsets(
            objective, all_ineq)

        if solver == s.ECOS and not (constr_map[s.SDP] or constr_map[s.EXP]):
            args, offset = self._ecos_problem_data(objective, constr_map, dims,
                                                   var_offsets, x_length)
        elif solver == s.CVXOPT and not constr_map[s.EXP]:
            args, offset = self._cvxopt_problem_data(objective, constr_map,
                                                     dims, var_offsets,
                                                     x_length)
        elif solver == s.SCS:
            args, offset = self._scs_problem_data(objective, constr_map, dims,
                                                  var_offsets, x_length)
        else:
            raise Exception("Cannot return problem data for the solver %s." %
                            solver)
        return args

    def _solve(self,
               solver=None,
               ignore_dcp=False,
               verbose=False,
               solver_specific_opts=None):
        """Solves a DCP compliant optimization problem.

        Saves the values of primal and dual variables in the variable
        and constraint objects, respectively.

        Parameters
        ----------
        solver : str, optional
            The solver to use. Defaults to ECOS.
        ignore_dcp : bool, optional
            Overrides the default of raising an exception if the problem is not
            DCP.
        verbose : bool, optional
            Overrides the default of hiding solver output.
        solver_specific_opts : dict, optional
            A dict of options that will be passed to the specific solver.
            In general, these options will override any default settings
            imposed by cvxpy.

        Returns
        -------
        float
            The optimal value for the problem, or a string indicating
            why the problem could not be solved.
        """
        # Safely set default as empty dict.
        if solver_specific_opts is None:
            solver_specific_opts = {}

        if not self.is_dcp():
            if ignore_dcp:
                print("Problem does not follow DCP rules. "
                      "Solving a convex relaxation.")
            else:
                raise Exception("Problem does not follow DCP rules.")

        objective, constr_map = self.canonicalize()
        # Choose a default solver if none specified.
        if solver is None:
            solver = self._choose_solver(constr_map)
        else:
            # Raise an error if the solver cannot handle the problem.
            self._validate_solver(constr_map, solver)

        dims = self._format_for_solver(constr_map, solver)

        all_ineq = constr_map[s.EQ] + constr_map[s.LEQ]
        var_offsets, var_sizes, x_length = self._get_var_offsets(
            objective, all_ineq)

        if solver == s.CVXOPT:
            result = self._cvxopt_solve(objective, constr_map, dims,
                                        var_offsets, x_length, verbose,
                                        solver_specific_opts)
        elif solver == s.SCS:
            result = self._scs_solve(objective, constr_map, dims, var_offsets,
                                     x_length, verbose, solver_specific_opts)
        elif solver == s.ECOS:
            result = self._ecos_solve(objective, constr_map, dims, var_offsets,
                                      x_length, verbose, solver_specific_opts)
        else:
            raise Exception("Unknown solver.")

        status, value, x, y, z = result
        if status == s.OPTIMAL:
            self._save_values(x, self.variables(), var_offsets)
            self._save_dual_values(y, constr_map[s.EQ], EqConstraint)
            self._save_dual_values(z, constr_map[s.LEQ], LeqConstraint)
            self._value = value
        else:
            self._handle_failure(status)
        self._status = status
        return self.value

    def _ecos_problem_data(self, objective, constr_map, dims, var_offsets,
                           x_length):
        """Returns the problem data for the call to ECOS.

        Parameters
        ----------
            objective: Expression
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
        Returns
        -------
        tuple
            ((c, G, h, dims, A, b), offset)
        """
        c, obj_offset = self._get_obj(objective, var_offsets, x_length,
                                      self._DENSE_INTF, self._DENSE_INTF)
        # Convert obj_offset to a scalar.
        obj_offset = self._DENSE_INTF.scalar_value(obj_offset)

        A, b = self._constr_matrix(constr_map[s.EQ], var_offsets, x_length,
                                   self._SPARSE_INTF, self._DENSE_INTF)
        G, h = self._constr_matrix(constr_map[s.LEQ], var_offsets, x_length,
                                   self._SPARSE_INTF, self._DENSE_INTF)
        # Convert c,h,b to 1D arrays.
        c, h, b = map(intf.from_2D_to_1D, [c.T, h, b])
        # Return the arguments that would be passed to ECOS.
        return ((c, G, h, dims, A, b), obj_offset)

    def _ecos_solve(self, objective, constr_map, dims, var_offsets, x_length,
                    verbose, opts):
        """Calls the ECOS solver and returns the result.

        Parameters
        ----------
            objective: Expression
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
            verbose: bool
                Should the solver show output?
            opts: dict
                List of user-specific options for ECOS
        Returns
        -------
        tuple
            (status, optimal objective, optimal x,
             optimal equality constraint dual,
             optimal inequality constraint dual)

        """
        prob_data = self._ecos_problem_data(objective, constr_map, dims,
                                            var_offsets, x_length)
        obj_offset = prob_data[1]
        results = ecos.solve(*prob_data[0], verbose=verbose)
        status = s.SOLVER_STATUS[s.ECOS][results['info']['exitFlag']]
        if status == s.OPTIMAL:
            primal_val = results['info']['pcost']
            value = self.objective._primal_to_result(primal_val - obj_offset)
            return (status, value, results['x'], results['y'], results['z'])
        else:
            return (status, None, None, None, None)

    def _cvxopt_problem_data(self, objective, constr_map, dims, var_offsets,
                             x_length):
        """Returns the problem data for the call to CVXOPT.

        Assumes no exponential cone constraints.

        Parameters
        ----------
            objective: Expression
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
        Returns
        -------
        tuple
            ((c, G, h, dims, A, b), offset)
        """
        c, obj_offset = self._get_obj(objective, var_offsets, x_length,
                                      self._CVXOPT_DENSE_INTF,
                                      self._CVXOPT_DENSE_INTF)
        # Convert obj_offset to a scalar.
        obj_offset = self._CVXOPT_DENSE_INTF.scalar_value(obj_offset)

        A, b = self._constr_matrix(constr_map[s.EQ], var_offsets, x_length,
                                   self._CVXOPT_SPARSE_INTF,
                                   self._CVXOPT_DENSE_INTF)
        G, h = self._constr_matrix(constr_map[s.LEQ], var_offsets, x_length,
                                   self._CVXOPT_SPARSE_INTF,
                                   self._CVXOPT_DENSE_INTF)
        # Return the arguments that would be passed to CVXOPT.
        return ((c.T, G, h, dims, A, b), obj_offset)

    def _cvxopt_solve(self, objective, constr_map, dims, var_offsets, x_length,
                      verbose, opts):
        """Calls the CVXOPT conelp or cpl solver and returns the result.

        Parameters
        ----------
            objective: Expression
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            sorted_vars: list
                An ordered list of the problem variables.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
            verbose: bool
                Should the solver show output?
            opts: dict
                List of user-specific options for CVXOPT;
                will be inserted into cvxopt.solvers.options.

        Returns
        -------
        tuple
            (status, optimal objective, optimal x,
             optimal equality constraint dual,
             optimal inequality constraint dual)

        """
        prob_data = self._cvxopt_problem_data(objective, constr_map, dims,
                                              var_offsets, x_length)
        c, G, h, dims, A, b = prob_data[0]
        obj_offset = prob_data[1]
        # Save original cvxopt solver options.
        old_options = cvxopt.solvers.options
        # Silence cvxopt if verbose is False.
        cvxopt.solvers.options['show_progress'] = verbose
        # Always do one step of iterative refinement after solving KKT system.
        cvxopt.solvers.options['refinement'] = 1

        # Apply any user-specific options
        for key, value in opts.items():
            cvxopt.solvers.options[key] = value

        # Target cvxopt clp if nonlinear constraints exist
        if constr_map[s.EXP]:
            # Get the nonlinear constraints.
            F = self._merge_nonlin(constr_map[s.EXP], var_offsets, x_length)
            # Get custom kktsolver.
            kktsolver = get_kktsolver(G, dims, A, F)
            results = cvxopt.solvers.cpl(c,
                                         F,
                                         G,
                                         h,
                                         dims,
                                         A,
                                         b,
                                         kktsolver=kktsolver)
        else:
            # Get custom kktsolver.
            kktsolver = get_kktsolver(G, dims, A)
            results = cvxopt.solvers.conelp(c,
                                            G,
                                            h,
                                            dims,
                                            A,
                                            b,
                                            kktsolver=kktsolver)
        # Restore original cvxopt solver options.
        cvxopt.solvers.options = old_options
        status = s.SOLVER_STATUS[s.CVXOPT][results['status']]
        if status == s.OPTIMAL:
            primal_val = results['primal objective']
            value = self.objective._primal_to_result(primal_val - obj_offset)
            if constr_map[s.EXP]:
                ineq_dual = results['zl']
            else:
                ineq_dual = results['z']
            return (status, value, results['x'], results['y'], ineq_dual)
        else:
            return (status, None, None, None, None)

    def _scs_problem_data(self, objective, constr_map, dims, var_offsets,
                          x_length):
        """Returns the problem data for the call to SCS.

        Parameters
        ----------
            objective: Expression
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
        Returns
        -------
        tuple
            ((data, dims), offset)
        """
        c, obj_offset = self._get_obj(objective, var_offsets, x_length,
                                      self._DENSE_INTF, self._DENSE_INTF)
        # Convert obj_offset to a scalar.
        obj_offset = self._DENSE_INTF.scalar_value(obj_offset)

        A, b = self._constr_matrix(constr_map[s.EQ] + constr_map[s.LEQ],
                                   var_offsets, x_length, self._SPARSE_INTF,
                                   self._DENSE_INTF)
        # Convert c, b to 1D arrays.
        c, b = map(intf.from_2D_to_1D, [c.T, b])
        data = {"c": c}
        data["A"] = A
        data["b"] = b
        return ((data, dims), obj_offset)

    def _scs_solve(self, objective, constr_map, dims, var_offsets, x_length,
                   verbose, opts):
        """Calls the SCS solver and returns the result.

        Parameters
        ----------
            objective: LinExpr
                The canonicalized objective.
            constr_map: dict
                A dict of the canonicalized constraints.
            dims: dict
                A dict with information about the types of constraints.
            var_offsets: dict
                A dict mapping variable id to offset in the stacked variable x.
            x_length: int
                The height of x.
            verbose: bool
                Should the solver show output?
            opts: dict
                A dict of the solver parameters passed to scs

        Returns
        -------
        tuple
            (status, optimal objective, optimal x,
             optimal equality constraint dual,
             optimal inequality constraint dual)
        """
        prob_data = self._scs_problem_data(objective, constr_map, dims,
                                           var_offsets, x_length)
        obj_offset = prob_data[1]
        # Set the options to be VERBOSE plus any user-specific options.
        opts = dict({"VERBOSE": verbose}.items() + opts.items())
        use_indirect = opts["USE_INDIRECT"] if "USE_INDIRECT" in opts else False
        results = scs.solve(*prob_data[0],
                            opts=opts,
                            USE_INDIRECT=use_indirect)
        status = s.SOLVER_STATUS[s.SCS][results["info"]["status"]]
        if status == s.OPTIMAL:
            primal_val = results["info"]["pobj"]
            value = self.objective._primal_to_result(primal_val - obj_offset)
            eq_dual = results["y"][0:dims["f"]]
            ineq_dual = results["y"][dims["f"]:]
            return (status, value, results["x"], eq_dual, ineq_dual)
        else:
            return (status, None, None, None, None)

    def _handle_failure(self, status):
        """Updates value fields based on the cause of solver failure.

        Parameters
        ----------
            status: str
                The status of the solver.
        """
        # Set all primal and dual variable values to None.
        for var_ in self.variables():
            var_.save_value(None)
        for constr in self.constraints:
            constr.save_value(None)
        # Set the problem value.
        if status == s.INFEASIBLE:
            self._value = self.objective._primal_to_result(np.inf)
        elif status == s.UNBOUNDED:
            self._value = self.objective._primal_to_result(-np.inf)
        else:  # Solver error
            self._value = None

    def _get_var_offsets(self, objective, constraints):
        """Maps each variable to a horizontal offset.

        Parameters
        ----------
        objective : Expression
            The canonicalized objective.
        constraints : list
            The canonicalized constraints.

        Returns
        -------
        tuple
            (map of variable to offset, length of variable vector)
        """
        vars_ = lu.get_expr_vars(objective)
        for constr in constraints:
            vars_ += lu.get_expr_vars(constr.expr)
        var_offsets = OrderedDict()
        var_sizes = {}
        # Ensure the variables are always in the same
        # order for the same problem.
        var_names = list(set(vars_))
        var_names.sort(key=lambda (var_id, var_size): var_id)
        # Map var ids to offsets.
        vert_offset = 0
        for var_id, var_size in var_names:
            var_sizes[var_id] = var_size
            var_offsets[var_id] = vert_offset
            vert_offset += var_size[0] * var_size[1]

        return (var_offsets, var_sizes, vert_offset)

    def _save_dual_values(self, result_vec, constraints, constr_type):
        """Saves the values of the dual variables.

        Parameters
        ----------
        results_vec : array_like
            A vector containing the dual variable values.
        constraints : list
            A list of the LinEqConstr/LinLeqConstr in the problem.
        constr_type : type
            EqConstr or LeqConstr
        """
        constr_offsets = {}
        offset = 0
        for constr in constraints:
            constr_offsets[constr.constr_id] = offset
            offset += constr.size[0] * constr.size[1]
        active_constraints = []
        for constr in self.constraints:
            # Ignore constraints of the wrong type.
            if type(constr) == constr_type:
                active_constraints.append(constr)
        self._save_values(result_vec, active_constraints, constr_offsets)

    def _save_values(self, result_vec, objects, offset_map):
        """Saves the values of the optimal primal/dual variables.

        Parameters
        ----------
        results_vec : array_like
            A vector containing the variable values.
        objects : list
            The variables or constraints where the values will be stored.
        offset_map : dict
            A map of object id to offset in the results vector.
        """
        if len(result_vec) > 0:
            # Cast to desired matrix type.
            result_vec = self._DENSE_INTF.const_to_matrix(result_vec)
        for obj in objects:
            rows, cols = obj.size
            if obj.id in offset_map:
                offset = offset_map[obj.id]
                # Handle scalars
                if (rows, cols) == (1, 1):
                    value = intf.index(result_vec, (offset, 0))
                else:
                    value = self._DENSE_INTF.zeros(rows, cols)
                    self._DENSE_INTF.block_add(
                        value, result_vec[offset:offset + rows * cols], 0, 0,
                        rows, cols)
                offset += rows * cols
            else:  # The variable was multiplied by zero.
                value = self._DENSE_INTF.zeros(rows, cols)
            obj.save_value(value)

    def _get_obj(self, objective, var_offsets, x_length, matrix_intf,
                 vec_intf):
        """Wraps _constr_matrix so it can be called for the objective.
        """
        dummy_constr = lu.create_eq(objective)
        return self._constr_matrix([dummy_constr], var_offsets, x_length,
                                   matrix_intf, vec_intf)

    def _constr_matrix(self, constraints, var_offsets, x_length, matrix_intf,
                       vec_intf):
        """Returns a matrix and vector representing a list of constraints.

        In the matrix, each constraint is given a block of rows.
        Each variable coefficient is inserted as a block with upper
        left corner at matrix[variable offset, constraint offset].
        The constant term in the constraint is added to the vector.

        Parameters
        ----------
        constraints : list
            A list of constraints.
        var_offsets : dict
            A dict of variable id to horizontal offset.
        x_length : int
            The length of the x vector.
        matrix_intf : interface
            The matrix interface to use for creating the constraints matrix.
        vec_intf : interface
            The matrix interface to use for creating the constant vector.

        Returns
        -------
        tuple
            A (matrix, vector) tuple.
        """

        rows = sum([c.size[0] * c.size[1] for c in constraints])
        cols = x_length
        V, I, J = [], [], []
        const_vec = vec_intf.zeros(rows, 1)
        vert_offset = 0
        for constr in constraints:
            coeffs = op2mat.get_coefficients(constr.expr)
            for id_, size, block in coeffs:
                vert_start = vert_offset
                vert_end = vert_start + constr.size[0] * constr.size[1]
                if id_ is lo.CONSTANT_ID:
                    # Flatten the block.
                    block = self._DENSE_INTF.const_to_matrix(block)
                    block_size = intf.size(block)
                    block = self._DENSE_INTF.reshape(
                        block, (block_size[0] * block_size[1], 1))
                    const_vec[vert_start:vert_end, :] += block
                else:
                    horiz_offset = var_offsets[id_]
                    if intf.is_scalar(block):
                        block = intf.scalar_value(block)
                        V.append(block)
                        I.append(vert_start)
                        J.append(horiz_offset)
                    else:
                        # Block is a numpy matrix or
                        # scipy CSC sparse matrix.
                        if not intf.is_sparse(block):
                            block = self._SPARSE_INTF.const_to_matrix(block)
                        block = block.tocoo()
                        V.extend(block.data)
                        I.extend(block.row + vert_start)
                        J.extend(block.col + horiz_offset)
            vert_offset += constr.size[0] * constr.size[1]

        # Create the constraints matrix.
        if len(V) > 0:
            matrix = sp.coo_matrix((V, (I, J)), (rows, cols))
            # Convert the constraints matrix to the correct type.
            matrix = matrix_intf.const_to_matrix(matrix, convert_scalars=True)
        else:  # Empty matrix.
            matrix = matrix_intf.zeros(rows, cols)
        return (matrix, -const_vec)

    def _merge_nonlin(self, nl_constr, var_offsets, x_length):
        """ TODO: ensure that this works with numpy data structs...
        """
        rows = sum([constr.size[0] * constr.size[1] for constr in nl_constr])
        cols = x_length

        big_x = self._CVXOPT_DENSE_INTF.zeros(cols, 1)
        for constr in nl_constr:
            constr.place_x0(big_x, var_offsets, self._CVXOPT_DENSE_INTF)

        def F(x=None, z=None):
            if x is None:
                return rows, big_x
            big_f = self._CVXOPT_DENSE_INTF.zeros(rows, 1)
            big_Df = self._CVXOPT_SPARSE_INTF.zeros(rows, cols)
            if z:
                big_H = self._CVXOPT_SPARSE_INTF.zeros(cols, cols)

            offset = 0
            for constr in nl_constr:
                constr_entries = constr.size[0] * constr.size[1]
                local_x = constr.extract_variables(x, var_offsets,
                                                   self._CVXOPT_DENSE_INTF)
                if z:
                    f, Df, H = constr.f(local_x,
                                        z[offset:offset + constr_entries])
                else:
                    result = constr.f(local_x)
                    if result:
                        f, Df = result
                    else:
                        return None
                big_f[offset:offset + constr_entries] = f
                constr.place_Df(big_Df, Df, var_offsets, offset,
                                self._CVXOPT_SPARSE_INTF)
                if z:
                    constr.place_H(big_H, H, var_offsets,
                                   self._CVXOPT_SPARSE_INTF)
                offset += constr_entries

            if z is None:
                return big_f, big_Df
            return big_f, big_Df, big_H

        return F

    def __str__(self):
        return repr(self)

    def __repr__(self):
        return "Problem(%s, %s)" % (repr(self.objective), repr(
            self.constraints))
Пример #22
0
class ExpCone(NonlinearConstraint):
    """A reformulated exponential cone constraint.

    Operates elementwise on x, y, z.

    Original cone:
    K = {(x,y,z) | y > 0, ye^(x/y) <= z}
         U {(x,y,z) | x <= 0, y = 0, z >= 0}
    Reformulated cone:
    K = {(x,y,z) | y, z > 0, y * log(y) + x <= y * log(z)}
         U {(x,y,z) | x <= 0, y = 0, z >= 0}

    Attributes
    ----------
        x: Variable x in the exponential cone.
        y: Variable y in the exponential cone.
        z: Variable z in the exponential cone.
    """
    CVXOPT_DENSE_INTF = intf.get_matrix_interface(cvxopt.matrix)
    CVXOPT_SPARSE_INTF = intf.get_matrix_interface(cvxopt.spmatrix)

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.size = (int(self.x.size[0]), int(self.x.size[1]))
        super(ExpCone, self).__init__(self._solver_hook,
                                      [self.x, self.y, self.z])

    def __str__(self):
        return "ExpCone(%s, %s, %s)" % (self.x, self.y, self.z)

    def format(self, eq_constr, leq_constr, dims, solver):
        """Formats EXP constraints for the solver.

        Parameters
        ----------
        eq_constr : list
            A list of the equality constraints in the canonical problem.
        leq_constr : list
            A list of the inequality constraints in the canonical problem.
        dims : dict
            A dict with the dimensions of the conic constraints.
        solver : str
            The solver being called.
        """
        if solver.name() == s.CVXOPT:
            eq_constr += self.__CVXOPT_format[0]
        elif solver.name() == s.SCS:
            leq_constr += self.__SCS_format[1]
        elif solver.name() == s.ECOS:
            leq_constr += self.__ECOS_format[1]
        else:
            raise SolverError("Solver does not support exponential cone.")
        # Update dims.
        dims[s.EXP_DIM] += self.size[0] * self.size[1]

    @pu.lazyprop
    def __ECOS_format(self):
        return ([], format_elemwise([self.x, self.z, self.y]))

    @pu.lazyprop
    def __SCS_format(self):
        return ([], format_elemwise([self.x, self.y, self.z]))

    @pu.lazyprop
    def __CVXOPT_format(self):
        constraints = []
        for i, var in enumerate(self.vars_):
            if not var.type is VARIABLE:
                lone_var = lu.create_var(var.size)
                constraints.append(lu.create_eq(lone_var, var))
                self.vars_[i] = lone_var
        return (constraints, [])

    def _solver_hook(self, vars_=None, scaling=None):
        """A function used by CVXOPT's nonlinear solver.

        Based on f(x,y,z) = y * log(y) + x - y * log(z).

        Parameters
        ----------
            vars_: A cvxopt dense matrix with values for (x,y,z).
            scaling: A scaling for the Hessian.

        Returns
        -------
            _solver_hook() returns the constraint size and a feasible point.
            _solver_hook(x) returns the function value and gradient at x.
            _solver_hook(x, z) returns the function value, gradient,
            and (z scaled) Hessian at x.
        """
        entries = self.size[0] * self.size[1]
        if vars_ is None:
            x_init = entries * [0.0]
            y_init = entries * [0.5]
            z_init = entries * [1.0]
            return self.size[0], cvxopt.matrix(x_init + y_init + z_init)
        # Unpack vars_
        x = vars_[0:entries]
        y = vars_[entries:2 * entries]
        z = vars_[2 * entries:]
        # Out of domain.
        # TODO what if y == 0.0?
        if min(y) <= 0.0 or min(z) <= 0.0:
            return None
        # Evaluate the function.
        f = self.CVXOPT_DENSE_INTF.zeros(entries, 1)
        for i in range(entries):
            f[i] = x[i] - y[i] * math.log(z[i]) + y[i] * math.log(y[i])
        # Compute the gradient.
        Df = self.CVXOPT_DENSE_INTF.zeros(entries, 3 * entries)
        for i in range(entries):
            Df[i, i] = 1.0
            Df[i, entries + i] = math.log(y[i]) - math.log(z[i]) + 1.0
            Df[i, 2 * entries + i] = -y[i] / z[i]

        if scaling is None:
            return f, Df
        # Compute the Hessian.
        big_H = self.CVXOPT_SPARSE_INTF.zeros(3 * entries, 3 * entries)
        for i in range(entries):
            H = cvxopt.matrix([
                [0.0, 0.0, 0.0],
                [0.0, 1.0 / y[i], -1.0 / z[i]],
                [0.0, -1.0 / z[i], y[i] / (z[i]**2)],
            ])
            big_H[i:3 * entries:entries,
                  i:3 * entries:entries] = scaling[i] * H
        return f, Df, big_H