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.size, (2, 1)) self.assertEqual(y.size, (1, 1)) self.assertEqual(x.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(x.canonical_form[0].size, (2, 1)) self.assertEqual(x.canonical_form[1], []) self.assertEquals(repr(self.x), "Variable(2, 1)") self.assertEquals(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) # mat = coeffs[self.A.id][1] # self.assertEqual(mat.shape, (2,4)) # self.assertEqual(mat[0,2], 1) 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, 1) 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 tranposing variables. def test_transpose_variable(self): var = self.a.T self.assertEquals(var.name(), "a") self.assertEquals(var.size, (1, 1)) self.a.save_value(2) self.assertEquals(var.value, 2) var = self.x.T self.assertEquals(var.name(), "x.T") self.assertEquals(var.size, (1, 2)) self.x.save_value(matrix([1, 2])) self.assertEquals(var.value[0, 0], 1) self.assertEquals(var.value[0, 1], 2) var = self.C.T self.assertEquals(var.name(), "C.T") self.assertEquals(var.size, (2, 3)) # coeffs = var.canonical_form[0].coefficients() # mat = coeffs.values()[0][0] # self.assertEqual(mat.size, (2,6)) # self.assertEqual(mat[1,3], 1) index = var[1, 0] self.assertEquals(index.name(), "C.T[1, 0]") self.assertEquals(index.size, (1, 1)) var = self.x.T.T self.assertEquals(var.name(), "x.T.T") self.assertEquals(var.size, (2, 1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2) self.assertEqual(c.value, 2) self.assertEqual(c.size, (1, 1)) self.assertEqual(c.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual(Constant(-2).sign, u.Sign.NEGATIVE_KEY) self.assertEqual(Constant(0).sign, u.Sign.ZERO_KEY) self.assertEqual(c.canonical_form[0].size, (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.size, (1, 2)) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual((-c).sign, u.Sign.NEGATIVE_KEY) self.assertEqual((0 * c).sign, u.Sign.ZERO_KEY) c = Constant([[2], [-2]]) self.assertEqual(c.sign, u.Sign.UNKNOWN_KEY) # Test sign of a complex expression. c = Constant([1, 2]) A = Constant([[1, 1], [1, 1]]) exp = c.T * A * c self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) self.assertEqual((c.T * c).sign, u.Sign.POSITIVE_KEY) exp = c.T.T self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) exp = c.T * self.A self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) # Test repr. self.assertEqual(repr(c), "Constant(CONSTANT, POSITIVE, (2, 1))") def test_1D_array(self): """Test NumPy 1D arrays as constants. """ c = np.array([1, 2]) p = Parameter(2) p.value = [1, 1] self.assertEquals((c * p).value, 3) self.assertEqual((c * self.x).size, (1, 1)) # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1, 1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1, 1) for Parameter value.") val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Initialize a parameter with a value. p = Parameter(value=10) self.assertEqual(p.value, 10) # Test assigning None. p.value = 10 p.value = None assert p.value is None with self.assertRaises(Exception) as cm: p = Parameter(2, 1, sign="negative", value=[2, 1]) self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") with self.assertRaises(Exception) as cm: p = Parameter(4, 3, sign="positive", value=[1, 2]) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 1) for Parameter value.") # Test repr. p = Parameter(4, 3, sign="negative") self.assertEqual(repr(p), 'Parameter(4, 3, sign="NEGATIVE")') # Test the AddExpresion class. def test_add_expression(self): # Vectors c = Constant([2, 2]) exp = self.x + c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: AddExpression([self.A, self.C]) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # 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, 1))") # Test the SubExpresion class. def test_sub_expression(self): # Vectors c = Constant([2, 2]) exp = self.x - c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Test repr. self.assertEqual(repr(self.x - c), "Expression(AFFINE, UNKNOWN, (2, 1))") # Test the MulExpresion class. def test_mul_expression(self): # Vectors c = Constant([[2], [2]]) exp = c * self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((c[0] * self.x).sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (1, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: ([2, 2, 3] * self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (3, 1) (2, 1)") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1], [2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: (self.A * self.B) self.assertEqual(str(cm.exception), "Cannot multiply two non-constants.") # Constant expressions T = Constant([[1, 2, 3], [3, 5, 5]]) exp = (T + T) * self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (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, u.Sign.UNKNOWN_KEY) # Scalar constants on the right should be moved left. expr = self.C * 2 self.assertEqual(expr.args[0].value, 2) # Scalar variables on the left should be moved right. expr = self.a * [2, 1] self.assertItemsAlmostEqual(expr.args[0].value, [2, 1]) # Test the DivExpresion class. def test_div_expression(self): # Vectors exp = self.x / 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (2, 1)) 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, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.size, (1, 1)) self.assertEqual(exp.sign, u.Sign.NEGATIVE_KEY) # Parameters. p = Parameter(sign="positive") exp = 2 / p p.value = 2 self.assertEquals(exp.value, 1) rho = Parameter(sign="positive") rho.value = 1 self.assertEquals(rho.sign, u.Sign.POSITIVE_KEY) self.assertEquals(Constant(2).sign, u.Sign.POSITIVE_KEY) self.assertEquals((Constant(2) / Constant(2)).sign, u.Sign.POSITIVE_KEY) self.assertEquals((Constant(2) * rho).sign, u.Sign.POSITIVE_KEY) self.assertEquals((rho / 2).sign, u.Sign.POSITIVE_KEY) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_positive() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3, 2)) # Test promotion of scalar constants. def test_scalar_const_promotion(self): # Vectors exp = self.x + 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_negative() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2, 1)) self.assertEqual((4 - self.x).size, (2, 1)) self.assertEqual((4 * self.x).size, (2, 1)) self.assertEqual((4 <= self.x).size, (2, 1)) self.assertEqual((4 == self.x).size, (2, 1)) self.assertEqual((self.x >= 4).size, (2, 1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((3 * self.A).size, (2, 2)) self.assertEqual(exp.size, (2, 2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1, 0] # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEquals(exp.size, (1, 1)) # coeff = exp.canonical_form[0].coefficients()[self.x][0] # self.assertEqual(coeff[0,1], 1) self.assertEqual(exp.value, None) exp = self.x[1, 0].T # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: (self.x[2, 0]) self.assertEqual(str(cm.exception), "Index/slice out of bounds.") # Slicing exp = self.C[0:2, 1] # self.assertEquals(exp.name(), "C[0:2,1]") self.assertEquals(exp.size, (2, 1)) exp = self.C[0:, 0:2] # self.assertEquals(exp.name(), "C[0:,0:2]") self.assertEquals(exp.size, (3, 2)) exp = self.C[0::2, 0::2] # self.assertEquals(exp.name(), "C[0::2,0::2]") self.assertEquals(exp.size, (2, 1)) exp = self.C[:3, :1:2] # self.assertEquals(exp.name(), "C[0:3,0]") self.assertEquals(exp.size, (3, 1)) exp = self.C[0:, 0] # self.assertEquals(exp.name(), "C[0:,0]") self.assertEquals(exp.size, (3, 1)) c = Constant([[1, -2], [0, 4]]) exp = c[1, 1] self.assertEqual(exp.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(c[0, 1].sign, u.Sign.UNKNOWN_KEY) self.assertEqual(c[1, 0].sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1, 1)) 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, u.Curvature.CONSTANT_KEY) assert exp.is_constant() self.assertEquals(exp.size, (3, 2)) self.assertEqual(exp[0, 1].value, 7) # Slice of transpose exp = self.C.T[0:2, 1] self.assertEquals(exp.size, (2, 1)) # Arithmetic expression indexing exp = (self.x + self.z)[1, 0] # self.assertEqual(exp.name(), "x[1,0] + z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x + self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x - self.z)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x - self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (-self.x)[1, 0] # self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c * self.x)[1, 0] # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c * self.a)[1, 0] # self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) def test_neg_indices(self): """Test negative indices. """ c = Constant([[1, 2], [3, 4]]) exp = c[-1, -1] self.assertEquals(exp.value, 4) self.assertEquals(exp.size, (1, 1)) self.assertEquals(exp.curvature, u.Curvature.CONSTANT_KEY) c = Constant([1, 2, 3, 4]) exp = c[1:-1] self.assertItemsAlmostEqual(exp.value, [2, 3]) self.assertEquals(exp.size, (2, 1)) self.assertEquals(exp.curvature, u.Curvature.CONSTANT_KEY) c = Constant([1, 2, 3, 4]) exp = c[::-1] self.assertItemsAlmostEqual(exp.value, [4, 3, 2, 1]) self.assertEquals(exp.size, (4, 1)) self.assertEquals(exp.curvature, u.Curvature.CONSTANT_KEY) x = Variable(4) Problem(Minimize(0), [x[::-1] == c]).solve() self.assertItemsAlmostEqual(x.value, [4, 3, 2, 1]) self.assertEquals(x[::-1].size, (4, 1)) x = Variable(2) self.assertEquals(x[::-1].size, (2, 1)) x = Variable(100, name="x") self.assertEquals("x[:-1, 0]", str(x[:-1])) def test_logical_indices(self): """Test indexing with logical arrays. """ pass def test_powers(self): exp = self.x**2 self.assertEqual(exp.curvature, u.Curvature.CONVEX_KEY) exp = self.x**0.5 self.assertEqual(exp.curvature, u.Curvature.CONCAVE_KEY) exp = self.x**-1 self.assertEqual(exp.curvature, u.Curvature.CONVEX_KEY) def test_sum(self): """Test built-in sum. Not good usage. """ self.a.value = 1 expr = sum(self.a) self.assertEquals(expr.value, 1) self.x.value = [1, 2] expr = sum(self.x) self.assertEquals(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.assertEquals(y.size, (3, 4)) self.assertEquals(y.name(), "x") x = Semidef(5, name="x") y = x.copy() self.assertEquals(y.size, (5, 5)) def test_param_copy(self): """Test the copy function for Parameters. """ x = Parameter(3, 4, name="x", sign="positive") y = x.copy() self.assertEquals(y.size, (3, 4)) self.assertEquals(y.name(), "x") self.assertEquals(y.sign, "POSITIVE") def test_constant_copy(self): """Test the copy function for Constants. """ x = Constant(2) y = x.copy() self.assertEquals(y.size, (1, 1)) self.assertEquals(y.value, 2)
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_INTERFACE # 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.size, (2,1)) self.assertEqual(y.size, (1,1)) self.assertEqual(x.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(x.canonical_form[0].size, (2,1)) self.assertEqual(x.canonical_form[1], []) self.assertEquals(repr(self.x), "Variable(2, 1)") self.assertEquals(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) # mat = coeffs[self.A.id][1] # self.assertEqual(mat.shape, (2,4)) # self.assertEqual(mat[0,2], 1) # Test tranposing variables. def test_transpose_variable(self): var = self.a.T self.assertEquals(var.name(), "a") self.assertEquals(var.size, (1,1)) self.a.save_value(2) self.assertEquals(var.value, 2) var = self.x.T self.assertEquals(var.name(), "x.T") self.assertEquals(var.size, (1,2)) self.x.save_value( matrix([1,2]) ) self.assertEquals(var.value[0,0], 1) self.assertEquals(var.value[0,1], 2) var = self.C.T self.assertEquals(var.name(), "C.T") self.assertEquals(var.size, (2,3)) # coeffs = var.canonical_form[0].coefficients() # mat = coeffs.values()[0][0] # self.assertEqual(mat.size, (2,6)) # self.assertEqual(mat[1,3], 1) index = var[1,0] self.assertEquals(index.name(), "C.T[1, 0]") self.assertEquals(index.size, (1,1)) var = self.x.T.T self.assertEquals(var.name(), "x.T.T") self.assertEquals(var.size, (2,1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2) self.assertEqual(c.value, 2) self.assertEqual(c.size, (1,1)) self.assertEqual(c.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual(Constant(-2).sign, u.Sign.NEGATIVE_KEY) self.assertEqual(Constant(0).sign, u.Sign.ZERO_KEY) self.assertEqual(c.canonical_form[0].size, (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.size, (1, 2)) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual((-c).sign, u.Sign.NEGATIVE_KEY) self.assertEqual((0*c).sign, u.Sign.ZERO_KEY) c = Constant([[2], [-2]]) self.assertEqual(c.sign, u.Sign.UNKNOWN_KEY) # Test sign of a complex expression. c = Constant([1, 2]) A = Constant([[1,1],[1,1]]) exp = c.T*A*c self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) self.assertEqual((c.T*c).sign, u.Sign.POSITIVE_KEY) exp = c.T.T self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) exp = c.T*self.A self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) # Test repr. self.assertEqual(repr(c), "Constant(CONSTANT, POSITIVE, (2, 1))") # def test_1D_array(self): # """Test NumPy 1D arrays as constants. # """ # c = np.array([1,2]) # p = Parameter(2) # with warnings.catch_warnings(record=True) as w: # # Cause all warnings to always be triggered. # warnings.simplefilter("always") # # Trigger a warning. # Constant(c) # self.x + c # p.value = c # # Verify some things # self.assertEqual(len(w), 3) # for warning in w: # self.assertEqual(str(warning.message), "NumPy 1D arrays are treated as column vectors.") # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1,1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1, 1) for Parameter value.") val = -np.ones((4,3)) val[0,0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Initialize a parameter with a value. p = Parameter(value=10) self.assertEqual(p.value, 10) with self.assertRaises(Exception) as cm: p = Parameter(2, 1, sign="negative", value=[2,1]) self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") with self.assertRaises(Exception) as cm: p = Parameter(4, 3, sign="positive", value=[1,2]) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 1) for Parameter value.") # Test repr. p = Parameter(4, 3, sign="negative") self.assertEqual(repr(p), 'Parameter(4, 3, sign="NEGATIVE")') # Test the AddExpresion class. def test_add_expression(self): # Vectors c = Constant([2,2]) exp = self.x + c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: AddExpression([self.A, self.C]) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # 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, 1))") # Test the SubExpresion class. def test_sub_expression(self): # Vectors c = Constant([2,2]) exp = self.x - c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Test repr. self.assertEqual(repr(self.x - c), "Expression(AFFINE, UNKNOWN, (2, 1))") # Test the MulExpresion class. def test_mul_expression(self): # Vectors c = Constant([[2],[2]]) exp = c*self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((c[0]*self.x).sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (1,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1,1)) with self.assertRaises(Exception) as cm: ([2,2,3]*self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (3, 1) (2, 1)") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1],[2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: (self.A * self.B) self.assertEqual(str(cm.exception), "Cannot multiply two non-constants.") # Constant expressions T = Constant([[1,2,3],[3,5,5]]) exp = (T + T) * self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (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, u.Sign.UNKNOWN_KEY) # Scalar constants on the right should be moved left # instead of taking the transpose. expr = self.C*2 self.assertEqual(expr.args[0].value, 2) # Test the DivExpresion class. def test_div_expression(self): # Vectors exp = self.x/2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (2,1)) 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, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.size, (1,1)) self.assertEqual(exp.sign, u.Sign.NEGATIVE_KEY) # Parameters. p = Parameter(sign="positive") exp = 2/p p.value = 2 self.assertEquals(exp.value, 1) rho = Parameter(sign="positive") rho.value = 1 self.assertEquals(rho.sign, u.Sign.POSITIVE_KEY) self.assertEquals(Constant(2).sign, u.Sign.POSITIVE_KEY) self.assertEquals((Constant(2)/Constant(2)).sign, u.Sign.POSITIVE_KEY) self.assertEquals((Constant(2)*rho).sign, u.Sign.POSITIVE_KEY) self.assertEquals((rho/2).sign, u.Sign.POSITIVE_KEY) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_positive() self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3,2)) # Test promotion of scalar constants. def test_scalar_const_promotion(self): # Vectors exp = self.x + 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_negative() self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2,1)) self.assertEqual((4 - self.x).size, (2,1)) self.assertEqual((4 * self.x).size, (2,1)) self.assertEqual((4 <= self.x).size, (2,1)) self.assertEqual((4 == self.x).size, (2,1)) self.assertEqual((self.x >= 4).size, (2,1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((3 * self.A).size, (2,2)) self.assertEqual(exp.size, (2,2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1,0] # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEquals(exp.size, (1,1)) # coeff = exp.canonical_form[0].coefficients()[self.x][0] # self.assertEqual(coeff[0,1], 1) self.assertEqual(exp.value, None) exp = self.x[1,0].T # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) with self.assertRaises(Exception) as cm: (self.x[2,0]) self.assertEqual(str(cm.exception), "Index/slice out of bounds.") # Slicing exp = self.C[0:2,1] # self.assertEquals(exp.name(), "C[0:2,1]") self.assertEquals(exp.size, (2,1)) exp = self.C[0:,0:2] # self.assertEquals(exp.name(), "C[0:,0:2]") self.assertEquals(exp.size, (3,2)) exp = self.C[0::2,0::2] # self.assertEquals(exp.name(), "C[0::2,0::2]") self.assertEquals(exp.size, (2,1)) exp = self.C[:3,:1:2] # self.assertEquals(exp.name(), "C[0:3,0]") self.assertEquals(exp.size, (3,1)) exp = self.C[0:,0] # self.assertEquals(exp.name(), "C[0:,0]") self.assertEquals(exp.size, (3,1)) c = Constant([[1,-2],[0,4]]) exp = c[1, 1] self.assertEqual(exp.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(c[0,1].sign, u.Sign.UNKNOWN_KEY) self.assertEqual(c[1,0].sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1,1)) 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, u.Curvature.CONSTANT_KEY) assert exp.is_constant() self.assertEquals(exp.size, (3,2)) self.assertEqual(exp[0,1].value, 7) # Slice of transpose exp = self.C.T[0:2,1] self.assertEquals(exp.size, (2,1)) # Arithmetic expression indexing exp = (self.x + self.z)[1,0] # self.assertEqual(exp.name(), "x[1,0] + z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x + self.a)[1,0] # self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.z)[1,0] # self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.a)[1,0] # self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (-self.x)[1,0] # self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.x)[1,0] # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.a)[1,0] # self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) def test_neg_indices(self): """Test negative indices. """ c = Constant([[1,2],[3,4]]) exp = c[-1, -1] self.assertEquals(exp.value, 4) self.assertEquals(exp.size, (1, 1)) self.assertEquals(exp.curvature, u.Curvature.CONSTANT_KEY) c = Constant([1,2,3,4]) exp = c[1:-1] self.assertItemsAlmostEqual(exp.value, [2, 3]) self.assertEquals(exp.size, (2, 1)) self.assertEquals(exp.curvature, u.Curvature.CONSTANT_KEY) def test_logical_indices(self): """Test indexing with logical arrays. """ pass
class TestConstraints(unittest.TestCase): """ 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), "LeqConstraint(%s, %s)" % (repr(self.x), repr(self.x))) constr = self.x <= 2 * self.x self.assertEqual( repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2 * self.x))) constr = 2 * self.x >= self.x self.assertEqual( repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2 * self.x))) def test_eq_constraint(self): """Test the EqConstraint class. """ constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2, 1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.x.save_value(2) self.z.save_value(2) assert constr.value self.x.save_value(3) assert not constr.value with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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.B]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.A) self.assertTrue(copy.args[1] is self.B) def test_leq_constraint(self): """Test the LeqConstraint class. """ constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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.B]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.A) self.assertTrue(copy.args[1] is self.B) def test_psd_constraint(self): """Test the PSD constraint <<. """ constr = self.A >> self.B self.assertEqual(constr.name(), "A >> B") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.A.save_value(np.matrix("2 -1; 1 2")) self.B.save_value(np.matrix("1 0; 0 1")) assert constr.value self.B.save_value(np.matrix("3 0; 0 3")) assert not constr.value with self.assertRaises(Exception) as cm: (self.x >> self.y) 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.A]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.B) self.assertTrue(copy.args[1] is self.A) def test_nsd_constraint(self): """Test the PSD constraint <<. """ constr = self.A << self.B self.assertEqual(constr.name(), "B >> A") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.B.save_value(np.matrix("2 -1; 1 2")) self.A.save_value(np.matrix("1 0; 0 1")) assert constr.value self.A.save_value(np.matrix("3 0; 0 3")) assert not constr.value with self.assertRaises(Exception) as cm: (self.x << self.y) self.assertEqual(str(cm.exception), "Non-square matrix in positive definite constraint.") def test_lt(self): """Test the < operator. """ constr = self.x < self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.x < self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_geq(self): """Test the >= operator. """ constr = self.z >= self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y >= self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_gt(self): """Test the > operator. """ constr = self.z > self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y > self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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, 1)) def test_chained_constraints(self): """Tests that chaining constraints raises an error. """ with self.assertRaises(Exception) as cm: (self.z <= self.x <= 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.") with self.assertRaises(Exception) as cm: (self.x == self.z == 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.")
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() self.assertItemsEqual(vars_, [self.a, self.x, self.b, self.A]) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() self.assertItemsEqual(params, [p1, p2, p3]) # Test silencing and enabling solver messages. def test_verbose(self): # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in ["ecos", "cvxopt"]: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a), [self.a >= 2]) p.solve(verbose=verbose, solver=solver) p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) out = sys.stdout.getvalue() # release output outputs[verbose].append(out.upper()) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output in outputs[True]: assert len(output) > 0 for output in outputs[False]: assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test",lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a,b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1,b=3,method="test") self.assertEqual(result, (1,3)) result = p.solve(1,method="test") self.assertEqual(result, (1,2)) result = p.solve(1,method="test",b=4) self.assertEqual(result, (1,4)) def test_consistency(self): """Test that variables and constraints keep a consistent order. """ import itertools num_solves = 4 vars_lists = [] ineqs_lists = [] for k in range(num_solves): sum = 0 constraints = [] for i in range(100): var = Variable(name=str(i)) sum += var constraints.append(var >= i) obj = Minimize(sum) p = Problem(obj, constraints) objective, constr_map, dims = p.canonicalize() all_ineq = itertools.chain(constr_map[s.EQ], constr_map[s.INEQ]) var_info = p._get_var_offsets(objective, all_ineq) sorted_vars, var_offsets, x_length = var_info vars_lists.append([int(v.name()) for v in sorted_vars]) ineqs_lists.append(constr_map[s.INEQ]) # Verify order of variables is consistent. for i in range(num_solves): self.assertEqual(range(100), vars_lists[i]) for i in range(num_solves): for idx, constr in enumerate(ineqs_lists[i]): var = constr.variables()[0] self.assertEqual(idx, int(var.name())) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective,constr_map,dims = self.canonicalize() return (len(constr_map[s.EQ]),len(constr_map[s.INEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj),[eq,eq,le,le]) result = p.solve(method="test") self.assertEqual(result, (1,1)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3*self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3*self.a - self.b + 100), [self.a >= 2, self.b + 5*self.c - 2 == self.a, self.b <= 5 + self.c]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0/6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5-1.0/6) self.assertAlmostEqual(self.c.value, -1.0/6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1,2]) A = matrix([[3,5],[1,2]]) I = Constant([[1,0],[0,1]]) p = Problem(Minimize(c.T*self.x + self.a), [A*self.x >= [-1,1], 4*I*self.z == self.x, self.z >= [2,2], self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T*self.x.value + self.a.value self.assertAlmostEqual(obj[0], result) self.assertItemsAlmostEqual(self.x.value, [8,8], places=3) self.assertItemsAlmostEqual(self.z.value, [2,2], places=3) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2,(2,3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T*self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), p._DENSE_INTF.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1,2],[3,4]] ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1],[1]]*(self.x + self.a + 1)), [self.a + self.x >= [1,2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1,2],[3,4]]*a a.value = 2 assert not (exp.value - 2*numpy.array([[1,2],[3,4]]).T).any() # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3*normInf(self.a + 2*self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2*self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertAlmostEqual(list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 15) self.assertAlmostEqual(list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2,-2,-2,-2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual(str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1,2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1,2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem(Minimize(norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z) ) ), [self.x >= [2,3], self.z <= [-1,-4], norm2(self.x + self.z) <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3,4])*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize( norm1(self.x + self.z) ), [self.x >= [2,3], [[1,2],[3,4]]*self.z == [-1,-4], norm2(self.x + self.z) <= 100]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4,3]) self.assertItemsAlmostEqual(self.z.value, [-4,1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2,(2,3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6*[0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0,0]), [self.x[0,0] <= 2, self.x[1,0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x, [2,3]) n = 10 A = matrix(range(n*n), (n,n)) x = Variable(n,n) p = Problem(Minimize(sum(x)), [x == A]) result = p.solve() answer = n*n*(n*n+1)/2 - n*n self.assertAlmostEqual(result, answer) # Matrix variables import __builtin__ p = Problem(Maximize( __builtin__.sum(self.A[i,i] + self.A[i,1-i] for i in range(2)) ), [self.A <= [[1,-2],[-3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1,-2,-3,4]) # Indexing arithmetic expressions. exp = [[1,2],[3,4]]*self.z + self.x p = Problem(Minimize(exp[1,0]), [self.x == self.z, self.z == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum(self.C)), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) p = Problem(Maximize(sum(self.C[0:3:2,1])), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2,1], [1,2]) p = Problem(Maximize(sum( (self.C[0:2,:] + self.A)[:,0:2] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,:], [1,2,1,2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) p = Problem(Maximize( [[3],[4]]*(self.C[0:2,:] + self.A)[:,0] ), [self.C[1:3,:] <= 2, self.C[0,:] == 1, [[1],[2]]*(self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1, 3*self.A[:,0] <= 3]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,2]) self.assertItemsAlmostEqual(self.A.value, [1,-.5,1,1]) p = Problem(Minimize(norm2((self.C[0:2,:] + self.A)[:,0] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,-2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) # Transpose of slice. p = Problem(Maximize(sum(self.C)), [self.C[1:3,:].T <= 2, self.C[0,:].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1,5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1,2], self.y == [3,4,5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1,4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2,2)) p = Problem( Minimize( sum(vstack(self.A, self.C)) ), [self.A >= 2*c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1,2)) p = Problem( Minimize( sum(vstack(c*self.A, c*self.B)) ), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1,-1]) p = Problem( Minimize( c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) result = p.solve() self.assertAlmostEqual(result, 0) # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum(self.x)), [self.x.T >= matrix([1,2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1,2]) p = Problem(Minimize(sum(self.C)), [matrix([1,1]).T*self.C.T >= matrix([0,1,2]).T]) result = p.solve() value = self.C.value constraints = [1*self.C[i,0] + 1*self.C[i,1] >= i for i in range(3)] p = Problem(Minimize(sum(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0,1] - self.A.T[1,0]), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T print exp.size p = Problem(Minimize(sum(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1,-1]) p = Problem(Minimize(max(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum(max(c, 2, 2 + c).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum(square(c.T).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum(self.C)), [self.C.T[:,1:3] <= 2, self.C.T[:,0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.A*c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a*2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T*c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T)*c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1,0] - self.A[0,1]) p = Problem(obj, [lambda_max(self.A) <= 100, self.A[0,0] == 2, self.A[1,1] == 2, self.A[1,0] == 2]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_exp) p = Problem(Minimize(obj), [self.x >= [2,3], self.z <= [-1,-4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0*self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 0)
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_to_str(self): """Test string representations. """ obj = Minimize(self.a) prob = Problem(obj) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr([]))) constraints = [self.x * 2 == self.x, self.x == 0] prob = Problem(obj, constraints) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr(constraints))) # Test str. result = "minimize %(name)s\nsubject to %(name)s == 0\n 0 <= %(name)s" % { "name": self.a.name() } prob = Problem(Minimize(self.a), [self.a == 0, self.a >= 0]) self.assertEqual(str(prob), result) def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() ref = [self.a, self.x, self.b, self.A] if PY2: self.assertItemsEqual(vars_, ref) else: self.assertCountEqual(vars_, ref) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() ref = [p1, p2, p3] if PY2: self.assertItemsEqual(params, ref) else: self.assertCountEqual(params, ref) def test_get_problem_data(self): """Test get_problem_data method. """ with self.assertRaises(Exception) as cm: Problem(Maximize(exp(self.a))).get_problem_data(s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") data = Problem(Maximize(exp(self.a) + 2)).get_problem_data(s.SCS) dims = data["dims"] self.assertEqual(dims['ep'], 1) self.assertEqual(data["c"].shape, (2, )) self.assertEqual(data["A"].shape, (3, 2)) data = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.ECOS) dims = data["dims"] self.assertEqual(dims["q"], [3]) self.assertEqual(data["c"].shape, (3, )) self.assertEqual(data["A"].shape, (0, 3)) self.assertEqual(data["G"].shape, (3, 3)) data = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.CVXOPT) dims = data["dims"] self.assertEqual(dims["q"], [3]) self.assertEqual(data["c"].size, (3, 1)) self.assertEqual(data["A"].size, (0, 3)) self.assertEqual(data["G"].size, (3, 3)) def test_unpack_results(self): """Test unpack results method. """ with self.assertRaises(Exception) as cm: Problem(Minimize(exp(self.a))).unpack_results("blah", None) self.assertEqual(str(cm.exception), "Unknown solver.") prob = Problem(Minimize(exp(self.a)), [self.a == 0]) args = prob.get_problem_data(s.SCS) data = {"c": args["c"], "A": args["A"], "b": args["b"]} results_dict = scs.solve(data, args["dims"]) prob = Problem(Minimize(exp(self.a)), [self.a == 0]) prob.unpack_results(s.SCS, results_dict) self.assertAlmostEqual(self.a.value, 0, places=4) self.assertAlmostEqual(prob.value, 1, places=3) self.assertAlmostEqual(prob.status, s.OPTIMAL) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) args = prob.get_problem_data(s.ECOS) results_dict = ecos.solve(args["c"], args["G"], args["h"], args["dims"], args["A"], args["b"]) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) prob.unpack_results(s.ECOS, results_dict) self.assertItemsAlmostEqual(self.x.value, [0, 0]) self.assertAlmostEqual(prob.value, 0) self.assertAlmostEqual(prob.status, s.OPTIMAL) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) args = prob.get_problem_data(s.CVXOPT) results_dict = cvxopt.solvers.conelp(args["c"], args["G"], args["h"], args["dims"], args["A"], args["b"]) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) prob.unpack_results(s.CVXOPT, results_dict) self.assertItemsAlmostEqual(self.x.value, [0, 0]) self.assertAlmostEqual(prob.value, 0) self.assertAlmostEqual(prob.status, s.OPTIMAL) # Test silencing and enabling solver messages. def test_verbose(self): import sys # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in installed_solvers(): # Don't test GLPK because there's a race # condition in setting CVXOPT solver options. if solver in ["GLPK", "GLPK_MI"]: continue # if solver == "GLPK": # # GLPK's stdout is separate from python, # # so we have to do this. # # Note: This probably breaks (badly) on Windows. # import os # import tempfile # stdout_fd = 1 # tmp_handle = tempfile.TemporaryFile(bufsize = 0) # os.dup2(tmp_handle.fileno(), stdout_fd) # else: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a + self.x[0]), [self.a >= 2, self.x >= 2]) if SOLVERS[solver].MIP_CAPABLE: p.constraints.append(Bool() == 0) p.solve(verbose=verbose, solver=solver) if SOLVERS[solver].EXP_CAPABLE: p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) # if solver == "GLPK": # # GLPK's stdout is separate from python, # # so we have to do this. # tmp_handle.seek(0) # out = tmp_handle.read() # tmp_handle.close() # else: out = sys.stdout.getvalue() # release output outputs[verbose].append((out, solver)) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output, solver in outputs[True]: print(solver) assert len(output) > 0 for output, solver in outputs[False]: print(solver) assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test", lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a, b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1, b=3, method="test") self.assertEqual(result, (1, 3)) result = p.solve(1, method="test") self.assertEqual(result, (1, 2)) result = p.solve(1, method="test", b=4) self.assertEqual(result, (1, 4)) def test_consistency(self): """Test that variables and constraints keep a consistent order. """ import itertools num_solves = 4 vars_lists = [] ineqs_lists = [] var_ids_order_created = [] for k in range(num_solves): sum = 0 constraints = [] var_ids = [] for i in range(100): var = Variable(name=str(i)) var_ids.append(var.id) sum += var constraints.append(var >= i) var_ids_order_created.append(var_ids) obj = Minimize(sum) p = Problem(obj, constraints) objective, constraints = p.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.ECOS]) # Sort by offset. vars_ = sorted(sym_data.var_offsets.items(), key=lambda key_val: key_val[1]) vars_ = [var_id for (var_id, offset) in vars_] vars_lists.append(vars_) ineqs_lists.append(sym_data.constr_map[s.LEQ]) # Verify order of variables is consistent. for i in range(num_solves): self.assertEqual(var_ids_order_created[i], vars_lists[i]) for i in range(num_solves): for idx, constr in enumerate(ineqs_lists[i]): var_id, _ = lu.get_expr_vars(constr.expr)[0] self.assertEqual(var_ids_order_created[i][idx], var_id) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective, constraints = self.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.ECOS]) return (len(sym_data.constr_map[s.EQ]), len(sym_data.constr_map[s.LEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj), [eq, eq, le, le]) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Internal constraints. X = Semidef(2) obj = sum_entries(X + X) p = Problem(Minimize(obj)) result = p.solve(method="test") self.assertEqual(result, (0, 1)) # Duplicates from non-linear constraints. exp = norm(self.x, 2) prob = Problem(Minimize(0), [exp <= 1, exp <= 2]) result = prob.solve(method="test") self.assertEqual(result, (0, 4)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3 * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3 * self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3 * self.a - self.b + 100), [ self.a >= 2, self.b + 5 * self.c - 2 == self.a, self.b <= 5 + self.c ]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0 / 6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5 - 1.0 / 6) self.assertAlmostEqual(self.c.value, -1.0 / 6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1, 2]) A = matrix([[3, 5], [1, 2]]) I = Constant([[1, 0], [0, 1]]) p = Problem(Minimize(c.T * self.x + self.a), [ A * self.x >= [-1, 1], 4 * I * self.z == self.x, self.z >= [2, 2], self.a >= 2 ]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T * self.x.value + self.a.value self.assertAlmostEqual(obj[0, 0], result) self.assertItemsAlmostEqual(self.x.value, [8, 8], places=3) self.assertItemsAlmostEqual(self.z.value, [2, 2], places=3) def test_ecos_noineq(self): """Test ECOS with no inequality constraints. """ T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve(solver=s.ECOS) self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T * self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), intf.DEFAULT_INTERFACE.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1], [1]] * (self.x + self.a + 1)), [self.a + self.x >= [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1, 2], [3, 4]] * a a.value = 2 assert not (exp.value - 2 * numpy.array([[1, 2], [3, 4]]).T).any() def test_parameter_problems(self): """Test problems with parameters. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Maximize(p1 * self.a), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) p1.value = 2 p2.value = -numpy.ones((3, 1)) p3.value = numpy.ones((4, 4)) result = p.solve() self.assertAlmostEqual(result, -6) # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3 * normInf(self.a + 2 * self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2 * self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(float(result), 12) self.assertAlmostEqual( float(list(self.x.value)[1] - list(self.z.value)[1]), 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(float(result), 15) self.assertAlmostEqual( float(list(self.x.value)[1] - list(self.z.value)[1]), 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Row arguments. p = Problem(Minimize(norm2((self.x - self.z).T) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum_entries(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2, -2, -2, -2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual( str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1, 2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1, 2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem( Minimize( norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z))), [ self.x >= [2, 3], self.z <= [-1, -4], norm2(self.x + self.z) <= 2 ]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3, 4]) * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize(norm1(self.x + self.z)), [ self.x >= [2, 3], [[1, 2], [3, 4]] * self.z == [-1, -4], norm2(self.x + self.z) <= 100 ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4, 3]) self.assertItemsAlmostEqual(self.z.value, [-4, 1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6 * [0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0, 0]), [self.x[0, 0] <= 2, self.x[1, 0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x.value, [2, 3]) n = 10 A = matrix(range(n * n), (n, n)) x = Variable(n, n) p = Problem(Minimize(sum_entries(x)), [x == A]) result = p.solve() answer = n * n * (n * n + 1) / 2 - n * n self.assertAlmostEqual(result, answer) # Matrix variables p = Problem( Maximize(sum(self.A[i, i] + self.A[i, 1 - i] for i in range(2))), [self.A <= [[1, -2], [-3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1, -2, -3, 4]) # Indexing arithmetic expressions. exp = [[1, 2], [3, 4]] * self.z + self.x p = Problem(Minimize(exp[1, 0]), [self.x == self.z, self.z == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) p = Problem(Maximize(sum_entries(self.C[0:3:2, 1])), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2, 1], [1, 2]) p = Problem(Maximize(sum_entries((self.C[0:2, :] + self.A)[:, 0:2])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, :], [1, 2, 1, 2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) p = Problem(Maximize([[3], [4]] * (self.C[0:2, :] + self.A)[:, 0]), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, [[1], [2]] * (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1, 3 * self.A[:, 0] <= 3 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, 2]) self.assertItemsAlmostEqual(self.A.value, [1, -.5, 1, 1]) p = Problem(Minimize(norm2((self.C[0:2, :] + self.A)[:, 0])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, -2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) # Transpose of slice. p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3, :].T <= 2, self.C[0, :].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1, 5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1, 2], self.y == [3, 4, 5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1, 4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2, 2)) p = Problem(Minimize(sum_entries(vstack(self.A, self.C))), [self.A >= 2 * c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1, 2)) p = Problem(Minimize(sum_entries(vstack(c * self.A, c * self.B))), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1, -1]) p = Problem(Minimize(c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") # Test the hstack atom. def test_hstack(self): c = matrix(1, (1, 5)) p = Problem(Minimize(c * hstack(self.x.T, self.y.T).T), [self.x == [1, 2], self.y == [3, 4, 5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1, 4)) p = Problem(Minimize(c * hstack(self.x.T, self.x.T).T), [self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2, 2)) p = Problem(Minimize(sum_entries(hstack(self.A.T, self.C.T))), [self.A >= 2 * c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) D = Variable(3, 3) expr = hstack(self.C, D) p = Problem(Minimize(expr[0, 1] + sum_entries(hstack(expr, expr))), [self.C >= 0, D >= 0, D[0, 0] == 2, self.C[0, 1] == 3]) result = p.solve() self.assertAlmostEqual(result, 13) c = matrix([1, -1]) p = Problem(Minimize(c.T * hstack(square(self.a).T, sqrt(self.b).T).T), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") def test_bad_objective(self): """Test using a cvxpy expression as an objective. """ with self.assertRaises(Exception) as cm: Problem(self.x + 2) self.assertEqual(str(cm.exception), "Problem objective must be Minimize or Maximize.") # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum_entries(self.x)), [self.x.T >= matrix([1, 2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1, 2]) p = Problem(Minimize(sum_entries(self.C)), [matrix([1, 1]).T * self.C.T >= matrix([0, 1, 2]).T]) result = p.solve() value = self.C.value constraints = [ 1 * self.C[i, 0] + 1 * self.C[i, 1] >= i for i in range(3) ] p = Problem(Minimize(sum_entries(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0, 1] - self.A.T[1, 0]), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T p = Problem(Minimize(sum_entries(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1, -1]) p = Problem(Minimize(max_elemwise(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum_entries(max_elemwise(c, 2, 2 + c).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum_entries(square(c.T).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum_entries(self.C)), [self.C.T[:, 1:3] <= 2, self.C.T[:, 0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.A * c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a * 2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T * c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T) * c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum_entries(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum_entries(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1, 0] - self.A[0, 1]) p = Problem(obj, [ lambda_max(self.A) <= 100, self.A[0, 0] == 2, self.A[1, 1] == 2, self.A[1, 0] == 2 ]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_entries_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_entries_exp) p = Problem(Minimize(obj), [self.x >= [2, 3], self.z <= [-1, -4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_entries_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0 * self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 0) assert self.a.value is not None def test_div(self): """Tests a problem with division. """ obj = Minimize(normInf(self.A / 5)) p = Problem(obj, [self.A >= 5]) result = p.solve() self.assertAlmostEqual(result, 1) 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]) def test_invalid_solvers(self): """Tests that errors occur when you use an invalid solver. """ with self.assertRaises(Exception) as cm: Problem(Minimize(-log(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(lambda_max(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(self.a)).solve(solver=s.SCS) self.assertEqual(str(cm.exception), "The solver SCS cannot solve the problem.") def test_reshape(self): """Tests problems with reshape. """ # Test on scalars. self.assertEqual(reshape(1, 1, 1).value, 1) # Test vector to matrix. x = Variable(4) mat = matrix([[1, -1], [2, -2]]) vec = matrix([1, 2, 3, 4]) vec_mat = matrix([[1, 2], [3, 4]]) expr = reshape(x, 2, 2) obj = Minimize(sum_entries(mat * expr)) prob = Problem(obj, [x == vec]) result = prob.solve() self.assertAlmostEqual(result, sum(mat * vec_mat)) # Test on matrix to vector. c = [1, 2, 3, 4] expr = reshape(self.A, 4, 1) obj = Minimize(expr.T * c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) self.assertItemsAlmostEqual(reshape(expr, 2, 2).value, [-1, -2, 3, 4]) # Test matrix to matrix. expr = reshape(self.C, 2, 3) mat = numpy.matrix([[1, -1], [2, -2]]) C_mat = numpy.matrix([[1, 4], [2, 5], [3, 6]]) obj = Minimize(sum_entries(mat * expr)) prob = Problem(obj, [self.C == C_mat]) result = prob.solve() reshaped = numpy.reshape(C_mat, (2, 3), 'F') self.assertAlmostEqual(result, (mat.dot(reshaped)).sum()) self.assertItemsAlmostEqual(expr.value, C_mat) # Test promoted expressions. c = matrix([[1, -1], [2, -2]]) expr = reshape(c * self.a, 1, 4) obj = Minimize(expr * [1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2 * c) expr = reshape(c * self.a, 4, 1) obj = Minimize(expr.T * [1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2 * c) def test_vec(self): """Tests problems with vec. """ c = [1, 2, 3, 4] expr = vec(self.A) obj = Minimize(expr.T * c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) def test_diag_prob(self): """Test a problem with diag. """ C = Variable(3, 3) obj = Maximize(C[0, 2]) constraints = [ diag(C) == 1, C[0, 1] == 0.6, C[1, 2] == -0.3, C == Semidef(3) ] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 0.583151) def test_presolve_constant_constraints(self): """Test that the presolver removes constraints with no variables. """ x = Variable() obj = Maximize(sqrt(x)) prob = Problem(obj) data = prob.get_problem_data(s.ECOS) A = data["A"] G = data["G"] for row in range(A.shape[0]): assert A[row, :].nnz > 0 for row in range(G.shape[0]): assert G[row, :].nnz > 0 def test_presolve_parameters(self): """Test presolve with parameters. """ # Test with parameters. gamma = Parameter(sign="positive") x = Variable() obj = Minimize(x) prob = Problem(obj, [gamma == 1, x >= 0]) gamma.value = 0 prob.solve(solver=s.SCS) self.assertEqual(prob.status, s.INFEASIBLE) gamma.value = 1 prob.solve(solver=s.CVXOPT) self.assertEqual(prob.status, s.OPTIMAL) def test_parameter_expressions(self): """Test that expressions with parameters are updated properly. """ x = Variable() y = Variable() x0 = Parameter() xSquared = x0 * x0 + 2 * x0 * (x - x0) # initial guess for x x0.value = 2 # make the constraint x**2 - y == 0 g = xSquared - y # set up the problem obj = abs(x - 1) prob = Problem(Minimize(obj), [g == 0]) prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(g.value, 0) # Test multiplication. prob = Problem(Minimize(x0 * x), [x == 1]) x0.value = 2 prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(prob.value, 1) def test_geo_mean(self): import numpy as np x = Variable(2) cost = geo_mean(x) prob = Problem(Maximize(cost), [x <= 1]) prob.solve() self.assertAlmostEqual(prob.value, 1) prob = Problem(Maximize(cost), [sum(x) <= 1]) prob.solve() self.assertItemsAlmostEqual(x.value, [.5, .5]) x = Variable(3, 3) self.assertRaises(ValueError, geo_mean, x) x = Variable(3, 1) g = geo_mean(x) self.assertSequenceEqual(g.w, [Fraction(1, 3)] * 3) x = Variable(1, 5) g = geo_mean(x) self.assertSequenceEqual(g.w, [Fraction(1, 5)] * 5) # check that we get the right answer for # max geo_mean(x) s.t. sum(x) <= 1 p = np.array([.07, .12, .23, .19, .39]) def short_geo_mean(x, p): p = np.array(p) / sum(p) x = np.array(x) return np.prod(x**p) x = Variable(5) prob = Problem(Maximize(geo_mean(x, p)), [sum(x) <= 1]) prob.solve() x = np.array(x.value).flatten() x_true = p / sum(p) self.assertTrue(np.allclose(prob.value, geo_mean(list(x), p).value)) self.assertTrue(np.allclose(prob.value, short_geo_mean(x, p))) self.assertTrue(np.allclose(x, x_true, 1e-3)) # check that we get the right answer for # max geo_mean(x) s.t. norm(x) <= 1 x = Variable(5) prob = Problem(Maximize(geo_mean(x, p)), [norm(x) <= 1]) prob.solve() x = np.array(x.value).flatten() x_true = np.sqrt(p / sum(p)) self.assertTrue(np.allclose(prob.value, geo_mean(list(x), p).value)) self.assertTrue(np.allclose(prob.value, short_geo_mean(x, p))) self.assertTrue(np.allclose(x, x_true, 1e-3)) def test_pnorm(self): import numpy as np x = Variable(3, name='x') a = np.array([1.0, 2, 3]) for p in (1, 1.6, 1.2, 2, 1.99, 3, 3.7, np.inf): prob = Problem(Minimize(pnorm(x, p=p)), [x.T * a >= 1]) prob.solve() # formula is true for any a >= 0 with p > 1 if p == np.inf: x_true = np.ones_like(a) / sum(a) elif p == 1: # only works for the particular a = [1,2,3] x_true = np.array([0, 0, 1.0 / 3]) else: x_true = a**(1.0 / (p - 1)) / a.dot(a**(1.0 / (p - 1))) x_alg = np.array(x.value).flatten() self.assertTrue(np.allclose(x_alg, x_true, 1e-3)) self.assertTrue(np.allclose(prob.value, np.linalg.norm(x_alg, p))) self.assertTrue( np.allclose(np.linalg.norm(x_alg, p), pnorm(x_alg, p).value)) def test_power(self): x = Variable() prob = Problem( Minimize(power(x, 1.7) + power(x, -2.3) - power(x, .45))) prob.solve() x = x.value self.assertTrue(__builtins__['abs'](1.7 * x**.7 - 2.3 * x**-3.3 - .45 * x**-.55) <= 1e-3)
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() self.assertItemsEqual(vars_, [self.a, self.x, self.b, self.A]) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() self.assertItemsEqual(params, [p1, p2, p3]) # Test silencing and enabling solver messages. def test_verbose(self): # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in ["ecos", "cvxopt"]: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a), [self.a >= 2]) p.solve(verbose=verbose, solver=solver) p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) out = sys.stdout.getvalue() # release output outputs[verbose].append(out.upper()) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output in outputs[True]: assert len(output) > 0 for output in outputs[False]: assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test", lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a, b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1, b=3, method="test") self.assertEqual(result, (1, 3)) result = p.solve(1, method="test") self.assertEqual(result, (1, 2)) result = p.solve(1, method="test", b=4) self.assertEqual(result, (1, 4)) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective, constr_map, dims = self.canonicalize() return (len(constr_map[s.EQ]), len(constr_map[s.INEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj), [eq, eq, le, le]) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3 * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3 * self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3 * self.a - self.b + 100), [ self.a >= 2, self.b + 5 * self.c - 2 == self.a, self.b <= 5 + self.c ]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0 / 6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5 - 1.0 / 6) self.assertAlmostEqual(self.c.value, -1.0 / 6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1, 2]) A = matrix([[3, 5], [1, 2]]) I = Constant([[1, 0], [0, 1]]) p = Problem(Minimize(c.T * self.x + self.a), [ A * self.x >= [-1, 1], 4 * I * self.z == self.x, self.z >= [2, 2], self.a >= 2 ]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T * self.x.value + self.a.value self.assertAlmostEqual(obj[0], result) self.assertItemsAlmostEqual(self.x.value, [8, 8], places=3) self.assertItemsAlmostEqual(self.z.value, [2, 2], places=3) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T * self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), p._DENSE_INTF.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1], [1]] * (self.x + self.a + 1)), [self.a + self.x >= [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1, 2], [3, 4]] * a a.value = 2 assert not (exp.value - 2 * numpy.array([[1, 2], [3, 4]]).T).any() # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3 * normInf(self.a + 2 * self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2 * self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertAlmostEqual( list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 15) self.assertAlmostEqual( list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12.6158) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2, -2, -2, -2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual( str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1, 2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1, 2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem( Minimize( norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z))), [ self.x >= [2, 3], self.z <= [-1, -4], norm2(self.x + self.z) <= 2 ]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3, 4]) * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize(norm1(self.x + self.z)), [ self.x >= [2, 3], [[1, 2], [3, 4]] * self.z == [-1, -4], norm2(self.x + self.z) <= 100 ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4, 3]) self.assertItemsAlmostEqual(self.z.value, [-4, 1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6 * [0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0, 0]), [self.x[0, 0] <= 2, self.x[1, 0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x, [2, 3]) n = 10 A = matrix(range(n * n), (n, n)) x = Variable(n, n) p = Problem(Minimize(sum(x)), [x == A]) result = p.solve() answer = n * n * (n * n + 1) / 2 - n * n self.assertAlmostEqual(result, answer) # Matrix variables import __builtin__ p = Problem( Maximize( __builtin__.sum(self.A[i, i] + self.A[i, 1 - i] for i in range(2))), [self.A <= [[1, -2], [-3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1, -2, -3, 4]) # Indexing arithmetic expressions. exp = [[1, 2], [3, 4]] * self.z + self.x p = Problem(Minimize(exp[1, 0]), [self.x == self.z, self.z == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum(self.C)), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) p = Problem(Maximize(sum(self.C[0:3:2, 1])), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2, 1], [1, 2]) p = Problem(Maximize(sum((self.C[0:2, :] + self.A)[:, 0:2])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, :], [1, 2, 1, 2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) p = Problem(Maximize([[3], [4]] * (self.C[0:2, :] + self.A)[:, 0]), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, [[1], [2]] * (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1, 3 * self.A[:, 0] <= 3 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, 2]) self.assertItemsAlmostEqual(self.A.value, [1, -.5, 1, 1]) p = Problem(Minimize(norm2((self.C[0:2, :] + self.A)[:, 0])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, -2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) # Transpose of slice. p = Problem(Maximize(sum(self.C)), [self.C[1:3, :].T <= 2, self.C[0, :].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1, 5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1, 2], self.y == [3, 4, 5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1, 4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2, 2)) p = Problem(Minimize(sum(vstack(self.A, self.C))), [self.A >= 2 * c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1, 2)) p = Problem(Minimize(sum(vstack(c * self.A, c * self.B))), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1, -1]) p = Problem(Minimize(c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) result = p.solve() self.assertAlmostEqual(result, 0) # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum(self.x)), [self.x.T >= matrix([1, 2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1, 2]) p = Problem(Minimize(sum(self.C)), [matrix([1, 1]).T * self.C.T >= matrix([0, 1, 2]).T]) result = p.solve() value = self.C.value constraints = [ 1 * self.C[i, 0] + 1 * self.C[i, 1] >= i for i in range(3) ] p = Problem(Minimize(sum(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0, 1] - self.A.T[1, 0]), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T print exp.size p = Problem(Minimize(sum(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1, -1]) p = Problem(Minimize(max(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum(max(c, 2, 2 + c).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum(square(c.T).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum(self.C)), [self.C.T[:, 1:3] <= 2, self.C.T[:, 0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.A * c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a * 2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T * c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T) * c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1, 0] - self.A[0, 1]) p = Problem(obj, [ lambda_max(self.A) <= 100, self.A[0, 0] == 2, self.A[1, 1] == 2, self.A[1, 0] == 2 ]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_exp) p = Problem(Minimize(obj), [self.x >= [2, 3], self.z <= [-1, -4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0 * self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 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.size, (2, 1)) self.assertEqual(y.size, (1, 1)) self.assertEqual(x.curvature, s.AFFINE) self.assertEqual(x.canonical_form[0].size, (2, 1)) self.assertEqual(x.canonical_form[1], []) self.assertEqual(repr(self.x), "Variable(2, 1)") 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) # mat = coeffs[self.A.id][1] # self.assertEqual(mat.shape, (2,4)) # self.assertEqual(mat[0,2], 1) 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, 1) 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 = NonNegative() with self.assertRaises(Exception) as cm: x.value = -2 self.assertEqual(str(cm.exception), "Invalid sign for NonNegative value.") # Small negative values are rounded to 0. x.value = -1e-8 self.assertEqual(x.value, 0) # Test tranposing variables. def test_transpose_variable(self): var = self.a.T self.assertEqual(var.name(), "a") self.assertEqual(var.size, (1, 1)) self.a.save_value(2) self.assertEqual(var.value, 2) var = self.x.T self.assertEqual(var.name(), "x.T") self.assertEqual(var.size, (1, 2)) self.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.size, (2, 3)) # coeffs = var.canonical_form[0].coefficients() # mat = coeffs.values()[0][0] # self.assertEqual(mat.size, (2,6)) # self.assertEqual(mat[1,3], 1) index = var[1, 0] self.assertEqual(index.name(), "C.T[1, 0]") self.assertEqual(index.size, (1, 1)) var = self.x.T.T self.assertEqual(var.name(), "x.T.T") self.assertEqual(var.size, (2, 1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2) self.assertEqual(c.value, 2) self.assertEqual(c.size, (1, 1)) self.assertEqual(c.curvature, s.CONSTANT) self.assertEqual(c.sign, s.POSITIVE) self.assertEqual(Constant(-2).sign, s.NEGATIVE) self.assertEqual(Constant(0).sign, s.ZERO) self.assertEqual(c.canonical_form[0].size, (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.size, (1, 2)) self.assertEqual(c.sign, s.POSITIVE) self.assertEqual((-c).sign, s.NEGATIVE) self.assertEqual((0*c).sign, s.ZERO) c = Constant([[2], [-2]]) self.assertEqual(c.sign, s.UNKNOWN) # Test sign of a complex expression. c = Constant([1, 2]) A = Constant([[1, 1], [1, 1]]) exp = c.T*A*c self.assertEqual(exp.sign, s.POSITIVE) self.assertEqual((c.T*c).sign, s.POSITIVE) exp = c.T.T self.assertEqual(exp.sign, s.POSITIVE) exp = c.T*self.A self.assertEqual(exp.sign, s.UNKNOWN) # Test repr. self.assertEqual(repr(c), "Constant(CONSTANT, POSITIVE, (2, 1))") 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).size, (1, 1)) # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1, 1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1, 1) for Parameter value.") val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Initialize a parameter with a value. p = Parameter(value=10) self.assertEqual(p.value, 10) # Test assigning None. p.value = 10 p.value = None assert p.value is None with self.assertRaises(Exception) as cm: p = Parameter(2, 1, sign="negative", value=[2, 1]) self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") with self.assertRaises(Exception) as cm: p = Parameter(4, 3, sign="positive", value=[1, 2]) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 1) for Parameter value.") # Test repr. p = Parameter(4, 3, sign="negative") self.assertEqual(repr(p), 'Parameter(4, 3, sign="NEGATIVE")') # 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].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: AddExpression([self.A, self.C]) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # 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, 1))") # 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].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Test repr. self.assertEqual(repr(self.x - c), "Expression(AFFINE, UNKNOWN, (2, 1))") # 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].size, (1, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: ([2, 2, 3]*self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (3, 1) (2, 1)") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1], [2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Affine times affine is okay with warnings.catch_warnings(): warnings.simplefilter("ignore") q = self.A * 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 * self.B) * 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) * self.B self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (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) # Scalar constants on the right should be moved left. expr = self.C*2 self.assertEqual(expr.args[0].value, 2) # Scalar variables on the left should be moved right. expr = self.a*[2, 1] self.assertItemsAlmostEqual(expr.args[0].value, [2, 1]) def test_matmul_expression(self): """Test matmul function, corresponding to .__matmul__( operator. """ if PY35: # 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.canonical_form[0].size, (1, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " .__matmul__( " + self.x.name()) self.assertEqual(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: self.x.__matmul__(2) self.assertEqual(str(cm.exception), "Scalar operands are not allowed, use '*' instead") with self.assertRaises(Exception) as cm: (self.x.__matmul__(np.array([2, 2, 3]))) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1], [2, 2]]) .__matmul__(self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # 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.size, (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) else: pass # 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].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (2, 1)) 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.size, (1, 1)) self.assertEqual(exp.sign, s.NEGATIVE) # Parameters. p = Parameter(sign="positive") exp = 2/p p.value = 2 self.assertEqual(exp.value, 1) rho = Parameter(sign="positive") rho.value = 1 self.assertEqual(rho.sign, s.POSITIVE) self.assertEqual(Constant(2).sign, s.POSITIVE) self.assertEqual((Constant(2)/Constant(2)).sign, s.POSITIVE) self.assertEqual((Constant(2)*rho).sign, s.POSITIVE) self.assertEqual((rho/2).sign, s.POSITIVE) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, s.AFFINE) assert exp.is_affine() self.assertEqual(exp.sign, s.UNKNOWN) assert not exp.is_positive() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (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_negative() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2, 1)) self.assertEqual((4 - self.x).size, (2, 1)) self.assertEqual((4 * self.x).size, (2, 1)) self.assertEqual((4 <= self.x).size, (2, 1)) self.assertEqual((4 == self.x).size, (2, 1)) self.assertEqual((self.x >= 4).size, (2, 1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual((3 * self.A).size, (2, 2)) self.assertEqual(exp.size, (2, 2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1, 0] # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, s.AFFINE) assert exp.is_affine() self.assertEqual(exp.size, (1, 1)) # coeff = exp.canonical_form[0].coefficients()[self.x][0] # self.assertEqual(coeff[0,1], 1) self.assertEqual(exp.value, None) exp = self.x[1, 0].T # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: (self.x[2, 0]) self.assertEqual(str(cm.exception), "Index/slice out of bounds.") # Slicing exp = self.C[0:2, 1] # self.assertEqual(exp.name(), "C[0:2,1]") self.assertEqual(exp.size, (2, 1)) exp = self.C[0:, 0:2] # self.assertEqual(exp.name(), "C[0:,0:2]") self.assertEqual(exp.size, (3, 2)) exp = self.C[0::2, 0::2] # self.assertEqual(exp.name(), "C[0::2,0::2]") self.assertEqual(exp.size, (2, 1)) exp = self.C[:3, :1:2] # self.assertEqual(exp.name(), "C[0:3,0]") self.assertEqual(exp.size, (3, 1)) exp = self.C[0:, 0] # self.assertEqual(exp.name(), "C[0:,0]") self.assertEqual(exp.size, (3, 1)) 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.size, (1, 1)) 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.size, (3, 2)) self.assertEqual(exp[0, 1].value, 7) # Slice of transpose exp = self.C.T[0:2, 1] self.assertEqual(exp.size, (2, 1)) # Arithmetic expression indexing exp = (self.x + self.z)[1, 0] # 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.size, (1, 1)) exp = (self.x + self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) exp = (self.x - self.z)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) exp = (self.x - self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) exp = (-self.x)[1, 0] # self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c*self.x)[1, 0] # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c*self.a)[1, 0] # self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, s.AFFINE) self.assertEqual(exp.size, (1, 1)) 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.size, (1, 1)) 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.size, (2, 1)) 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.size, (4, 1)) self.assertEqual(exp.curvature, s.CONSTANT) x = Variable(4) Problem(Minimize(0), [x[::-1] == c]).solve() self.assertItemsAlmostEqual(x.value, [4, 3, 2, 1]) self.assertEqual(x[::-1].size, (4, 1)) x = Variable(2) self.assertEqual(x[::-1].size, (2, 1)) x = Variable(100, name="x") self.assertEqual("x[:-1, 0]", str(x[:-1])) c = Constant([[1, 2], [3, 4]]) expr = c[0, 2:0:-1] self.assertEqual(expr.size, (1, 1)) self.assertAlmostEqual(expr.value, 3) expr = c[0, 2::-1] self.assertEqual(expr.size, (1, 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.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[A <= 2], expr.value) expr = C[A % 2 == 0] self.assertEqual(expr.size, (6, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (3, 2)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (1, 3)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (3, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (2, 4)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[[1, 2]], expr.value) # List for rows, index for columns. expr = C[[0, 2], 3] self.assertEqual(expr.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[[0, 2], 3], expr.value) # Index for row, list for columns. expr = C[1, [0, 2]] self.assertEqual(expr.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[1, [0, 2]], expr.value) # List for rows, slice for columns. expr = C[[0, 2], 1:3] self.assertEqual(expr.size, (2, 2)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[[0, 2], 1:3], expr.value) # Slice for row, list for columns. expr = C[1:-1, [0, 2]] self.assertEqual(expr.size, (1, 2)) self.assertEqual(expr.sign, s.POSITIVE) self.assertItemsAlmostEqual(A[1:-1, [0, 2]], expr.value) # Lists for rows and columns. expr = C[[0, 1], [1, 3]] self.assertEqual(expr.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (2, 1)) self.assertEqual(expr.sign, s.POSITIVE) 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.size, (3, 4)) self.assertEqual(y.name(), "x") x = Semidef(5, name="x") y = x.copy() self.assertEqual(y.size, (5, 5)) def test_param_copy(self): """Test the copy function for Parameters. """ x = Parameter(3, 4, name="x", sign="positive") y = x.copy() self.assertEqual(y.size, (3, 4)) self.assertEqual(y.name(), "x") self.assertEqual(y.sign, "POSITIVE") def test_constant_copy(self): """Test the copy function for Constants. """ x = Constant(2) y = x.copy() self.assertEqual(y.size, (1, 1)) self.assertEqual(y.value, 2)
class TestConstraints(unittest.TestCase): """ 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') # Test the EqConstraint class. def test_eq_constraint(self): constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2,1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.x.save_value(2) self.z.save_value(2) assert constr.value self.x.save_value(3) assert not constr.value with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Test the LeqConstraint class. def test_leq_constraint(self): constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2,1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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[0], (3,1)) self.assertEqual(len(constr.format()), 2)
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_to_str(self): """Test string representations. """ obj = Minimize(self.a) prob = Problem(obj) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr([]))) constraints = [self.x * 2 == self.x, self.x == 0] prob = Problem(obj, constraints) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr(constraints))) # Test str. result = "minimize %(name)s\nsubject to %(name)s == 0\n 0 <= %(name)s" % { "name": self.a.name() } prob = Problem(Minimize(self.a), [self.a == 0, self.a >= 0]) self.assertEqual(str(prob), result) def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() self.assertItemsEqual(vars_, [self.a, self.x, self.b, self.A]) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() self.assertItemsEqual(params, [p1, p2, p3]) def test_get_problem_data(self): """Test get_problem_data method. """ with self.assertRaises(Exception) as cm: Problem(Maximize(exp(self.a))).get_problem_data(s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") args = Problem(Maximize(exp(self.a) + 2)).get_problem_data(s.SCS) data, dims = args self.assertEqual(dims['ep'], 1) self.assertEqual(data["c"].shape, (2, )) self.assertEqual(data["A"].shape, (3, 2)) args = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.ECOS) c, G, h, dims, A, b = args self.assertEqual(dims["q"], [3]) self.assertEqual(c.shape, (3, )) self.assertEqual(A.shape, (0, 3)) self.assertEqual(G.shape, (3, 3)) args = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.CVXOPT) c, G, h, dims, A, b = args self.assertEqual(dims["q"], [3]) self.assertEqual(c.size, (3, 1)) self.assertEqual(A.size, (0, 3)) self.assertEqual(G.size, (3, 3)) # Test silencing and enabling solver messages. def test_verbose(self): # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in [s.ECOS, s.CVXOPT, s.SCS]: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a + self.x[0]), [self.a >= 2, self.x >= 2]) p.solve(verbose=verbose, solver=solver) if solver != s.ECOS: p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) out = sys.stdout.getvalue() # release output outputs[verbose].append(out.upper()) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output in outputs[True]: assert len(output) > 0 for output in outputs[False]: assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test", lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a, b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1, b=3, method="test") self.assertEqual(result, (1, 3)) result = p.solve(1, method="test") self.assertEqual(result, (1, 2)) result = p.solve(1, method="test", b=4) self.assertEqual(result, (1, 4)) def test_consistency(self): """Test that variables and constraints keep a consistent order. """ import itertools num_solves = 4 vars_lists = [] ineqs_lists = [] var_ids_order_created = [] for k in range(num_solves): sum = 0 constraints = [] var_ids = [] for i in range(100): var = Variable(name=str(i)) var_ids.append(var.id) sum += var constraints.append(var >= i) var_ids_order_created.append(var_ids) obj = Minimize(sum) p = Problem(obj, constraints) objective, constraints = p.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.ECOS]) # Sort by offset. vars_ = sorted(sym_data.var_offsets.items(), key=lambda (var_id, offset): offset) vars_ = [var_id for (var_id, offset) in vars_] vars_lists.append(vars_) ineqs_lists.append(sym_data.constr_map[s.LEQ]) # Verify order of variables is consistent. for i in range(num_solves): self.assertEqual(var_ids_order_created[i], vars_lists[i]) for i in range(num_solves): for idx, constr in enumerate(ineqs_lists[i]): var_id, _ = lu.get_expr_vars(constr.expr)[0] self.assertEqual(var_ids_order_created[i][idx], var_id) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective, constraints = self.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.ECOS]) return (len(sym_data.constr_map[s.EQ]), len(sym_data.constr_map[s.LEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj), [eq, eq, le, le]) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Internal constraints. X = Semidef(2) obj = sum_entries(X + X) p = Problem(Minimize(obj)) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Duplicates from non-linear constraints. exp = norm(self.x, 2) prob = Problem(Minimize(0), [exp <= 1, exp <= 2]) result = prob.solve(method="test") self.assertEqual(result, (0, 4)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3 * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3 * self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3 * self.a - self.b + 100), [ self.a >= 2, self.b + 5 * self.c - 2 == self.a, self.b <= 5 + self.c ]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0 / 6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5 - 1.0 / 6) self.assertAlmostEqual(self.c.value, -1.0 / 6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1, 2]) A = matrix([[3, 5], [1, 2]]) I = Constant([[1, 0], [0, 1]]) p = Problem(Minimize(c.T * self.x + self.a), [ A * self.x >= [-1, 1], 4 * I * self.z == self.x, self.z >= [2, 2], self.a >= 2 ]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T * self.x.value + self.a.value self.assertAlmostEqual(obj[0], result) self.assertItemsAlmostEqual(self.x.value, [8, 8], places=3) self.assertItemsAlmostEqual(self.z.value, [2, 2], places=3) def test_ecos_noineq(self): """Test ECOS with no inequality constraints. """ T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve(solver=s.ECOS) self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T * self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), intf.DEFAULT_INTERFACE.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1], [1]] * (self.x + self.a + 1)), [self.a + self.x >= [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1, 2], [3, 4]] * a a.value = 2 assert not (exp.value - 2 * numpy.array([[1, 2], [3, 4]]).T).any() def test_parameter_problems(self): """Test problems with parameters. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Maximize(p1 * self.a), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) p1.value = 2 p2.value = -numpy.ones((3, 1)) p3.value = numpy.ones((4, 4)) result = p.solve() self.assertAlmostEqual(result, -6) # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3 * normInf(self.a + 2 * self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2 * self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertAlmostEqual( list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 15) self.assertAlmostEqual( list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Row arguments. p = Problem(Minimize(norm2((self.x - self.z).T) + 5), [self.x >= [2, 3], self.z <= [-1, -4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum_entries(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2, -2, -2, -2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual( str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1, 2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1, 2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem( Minimize( norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z))), [ self.x >= [2, 3], self.z <= [-1, -4], norm2(self.x + self.z) <= 2 ]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3, 4]) * self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize(norm1(self.x + self.z)), [ self.x >= [2, 3], [[1, 2], [3, 4]] * self.z == [-1, -4], norm2(self.x + self.z) <= 100 ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4, 3]) self.assertItemsAlmostEqual(self.z.value, [-4, 1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2, (2, 3)) c = matrix([3, 4]) p = Problem(Minimize(1), [self.A >= T * self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4 * [0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6 * [0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0, 0]), [self.x[0, 0] <= 2, self.x[1, 0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x.value, [2, 3]) n = 10 A = matrix(range(n * n), (n, n)) x = Variable(n, n) p = Problem(Minimize(sum_entries(x)), [x == A]) result = p.solve() answer = n * n * (n * n + 1) / 2 - n * n self.assertAlmostEqual(result, answer) # Matrix variables p = Problem( Maximize(sum(self.A[i, i] + self.A[i, 1 - i] for i in range(2))), [self.A <= [[1, -2], [-3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1, -2, -3, 4]) # Indexing arithmetic expressions. exp = [[1, 2], [3, 4]] * self.z + self.x p = Problem(Minimize(exp[1, 0]), [self.x == self.z, self.z == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) p = Problem(Maximize(sum_entries(self.C[0:3:2, 1])), [self.C[1:3, :] <= 2, self.C[0, :] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2, 1], [1, 2]) p = Problem(Maximize(sum_entries((self.C[0:2, :] + self.A)[:, 0:2])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, :], [1, 2, 1, 2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) p = Problem(Maximize([[3], [4]] * (self.C[0:2, :] + self.A)[:, 0]), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, [[1], [2]] * (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1, 3 * self.A[:, 0] <= 3 ]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, 2]) self.assertItemsAlmostEqual(self.A.value, [1, -.5, 1, 1]) p = Problem(Minimize(norm2((self.C[0:2, :] + self.A)[:, 0])), [ self.C[1:3, :] <= 2, self.C[0, :] == 1, (self.A + self.B)[:, 0] == 3, (self.A + self.B)[:, 1] == 2, self.B == 1 ]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2, 0], [1, -2]) self.assertItemsAlmostEqual(self.A.value, [2, 2, 1, 1]) # Transpose of slice. p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3, :].T <= 2, self.C[0, :].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1, 5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1, 2], self.y == [3, 4, 5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1, 4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2, 2)) p = Problem(Minimize(sum_entries(vstack(self.A, self.C))), [self.A >= 2 * c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1, 2)) p = Problem(Minimize(sum_entries(vstack(c * self.A, c * self.B))), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1, -1]) p = Problem(Minimize(c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") # Test the hstack atom. def test_hstack(self): c = matrix(1, (1, 5)) p = Problem(Minimize(c * hstack(self.x.T, self.y.T).T), [self.x == [1, 2], self.y == [3, 4, 5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1, 4)) p = Problem(Minimize(c * hstack(self.x.T, self.x.T).T), [self.x == [1, 2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2, 2)) p = Problem(Minimize(sum_entries(hstack(self.A.T, self.C.T))), [self.A >= 2 * c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) D = Variable(3, 3) expr = hstack(self.C, D) p = Problem(Minimize(expr[0, 1] + sum_entries(hstack(expr, expr))), [self.C >= 0, D >= 0, D[0, 0] == 2, self.C[0, 1] == 3]) result = p.solve() self.assertAlmostEqual(result, 13) c = matrix([1, -1]) p = Problem(Minimize(c.T * hstack(square(self.a).T, sqrt(self.b).T).T), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") def test_bad_objective(self): """Test using a cvxpy expression as an objective. """ with self.assertRaises(Exception) as cm: Problem(self.x + 2) self.assertEqual(str(cm.exception), "Problem objective must be Minimize or Maximize.") # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum_entries(self.x)), [self.x.T >= matrix([1, 2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1, 2]) p = Problem(Minimize(sum_entries(self.C)), [matrix([1, 1]).T * self.C.T >= matrix([0, 1, 2]).T]) result = p.solve() value = self.C.value constraints = [ 1 * self.C[i, 0] + 1 * self.C[i, 1] >= i for i in range(3) ] p = Problem(Minimize(sum_entries(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0, 1] - self.A.T[1, 0]), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T p = Problem(Minimize(sum_entries(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1, -1]) p = Problem(Minimize(max_elemwise(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum_entries(max_elemwise(c, 2, 2 + c).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1, -1, 2], [1, -1, 2]]) p = Problem(Minimize(sum_entries(square(c.T).T[:, 0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum_entries(self.C)), [self.C.T[:, 1:3] <= 2, self.C.T[:, 0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2 * [1, 2, 2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1, 2]) p = Problem(Minimize(c.T * self.A * c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a * 2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T * c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T) * c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum_entries(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum_entries(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1, 2], [3, 4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1, 0] - self.A[0, 1]) p = Problem(obj, [ lambda_max(self.A) <= 100, self.A[0, 0] == 2, self.A[1, 1] == 2, self.A[1, 0] == 2 ]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_entries_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_entries_exp) p = Problem(Minimize(obj), [self.x >= [2, 3], self.z <= [-1, -4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2, 3]) self.assertItemsAlmostEqual(self.z.value, [-1, -4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_entries_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0 * self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 0) assert self.a.value is not None def test_div(self): """Tests a problem with division. """ obj = Minimize(normInf(self.A / 5)) p = Problem(obj, [self.A >= 5]) result = p.solve() self.assertAlmostEqual(result, 1) 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]) def test_invalid_solvers(self): """Tests that errors occur when you use an invalid solver. """ with self.assertRaises(Exception) as cm: Problem(Minimize(-log(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(lambda_max(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(self.a)).solve(solver=s.SCS) self.assertEqual(str(cm.exception), "The solver SCS cannot solve the problem.") def test_reshape(self): """Tests problems with reshape. """ # Test on scalars. self.assertEqual(reshape(1, 1, 1).value, 1) # Test vector to matrix. x = Variable(4) mat = matrix([[1, -1], [2, -2]]) vec = matrix([1, 2, 3, 4]) vec_mat = matrix([[1, 2], [3, 4]]) expr = reshape(x, 2, 2) obj = Minimize(sum_entries(mat * expr)) prob = Problem(obj, [x == vec]) result = prob.solve() self.assertAlmostEqual(result, sum(mat * vec_mat)) # Test on matrix to vector. c = [1, 2, 3, 4] expr = reshape(self.A, 4, 1) obj = Minimize(expr.T * c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) self.assertItemsAlmostEqual(reshape(expr, 2, 2).value, [-1, -2, 3, 4]) # Test matrix to matrix. expr = reshape(self.C, 2, 3) mat = numpy.matrix([[1, -1], [2, -2]]) C_mat = numpy.matrix([[1, 4], [2, 5], [3, 6]]) obj = Minimize(sum_entries(mat * expr)) prob = Problem(obj, [self.C == C_mat]) result = prob.solve() reshaped = numpy.reshape(C_mat, (2, 3), 'F') self.assertAlmostEqual(result, (mat.dot(reshaped)).sum()) self.assertItemsAlmostEqual(expr.value, C_mat) # Test promoted expressions. c = matrix([[1, -1], [2, -2]]) expr = reshape(c * self.a, 1, 4) obj = Minimize(expr * [1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2 * c) expr = reshape(c * self.a, 4, 1) obj = Minimize(expr.T * [1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2 * c) def test_vec(self): """Tests problems with vec. """ c = [1, 2, 3, 4] expr = vec(self.A) obj = Minimize(expr.T * c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) def test_diag_prob(self): """Test a problem with diag. """ C = Variable(3, 3) obj = Maximize(C[0, 2]) constraints = [ diag(C) == 1, C[0, 1] == 0.6, C[1, 2] == -0.3, C == Semidef(3) ] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 0.583151) def test_presolve_constant_constraints(self): """Test that the presolver removes constraints with no variables. """ x = Variable() obj = Maximize(sqrt(x)) prob = Problem(obj) c, G, h, dims, A, b = prob.get_problem_data(s.ECOS) for row in range(A.shape[0]): assert A[row, :].nnz > 0 for row in range(G.shape[0]): assert G[row, :].nnz > 0 def test_presolve_parameters(self): """Test presolve with parameters. """ # Test with parameters. gamma = Parameter(sign="positive") x = Variable() obj = Minimize(x) prob = Problem(obj, [gamma == 1, x >= 0]) gamma.value = 0 prob.solve(solver=s.SCS) self.assertEqual(prob.status, s.INFEASIBLE) gamma.value = 1 prob.solve(solver=s.CVXOPT) self.assertEqual(prob.status, s.OPTIMAL)
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), "LeqConstraint(%s, %s)" % (repr(self.x), repr(self.x))) constr = self.x <= 2*self.x self.assertEqual(repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2*self.x))) constr = 2*self.x >= self.x self.assertEqual(repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2*self.x))) def test_eq_constraint(self): """Test the EqConstraint class. """ constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2,1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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 = [2,1] self.z.value = [2,2] assert not constr.value self.assertItemsAlmostEqual(constr.violation, [0,1]) self.assertItemsAlmostEqual(constr.residual.value, [0,1]) self.z.value = [2,1] assert constr.value self.assertItemsAlmostEqual(constr.violation, [0,0]) self.assertItemsAlmostEqual(constr.residual.value, [0,0]) with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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.B]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.A) self.assertTrue(copy.args[1] is self.B) def test_leq_constraint(self): """Test the LeqConstraint class. """ constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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 = [2,1] self.z.value = [2,0] assert not constr.value self.assertItemsAlmostEqual(constr.violation, [0,1]) self.assertItemsAlmostEqual(constr.residual.value, [0,1]) self.z.value = [2,2] assert constr.value self.assertItemsAlmostEqual(constr.violation, [0,0]) self.assertItemsAlmostEqual(constr.residual.value, [0,0]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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.B]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.A) self.assertTrue(copy.args[1] is self.B) def test_psd_constraint(self): """Test the PSD constraint <<. """ constr = self.A >> self.B self.assertEqual(constr.name(), "A >> B") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.A.save_value(np.matrix("2 -1; 1 2")) self.B.save_value(np.matrix("1 0; 0 1")) assert constr.value self.assertAlmostEqual(constr.violation, 0) self.assertAlmostEqual(constr.residual.value, 0) self.B.save_value(np.matrix("3 0; 0 3")) assert not constr.value self.assertAlmostEqual(constr.violation, 1) self.assertAlmostEqual(constr.residual.value, 1) with self.assertRaises(Exception) as cm: (self.x >> self.y) 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.A]) self.assertTrue(type(copy) is type(constr)) self.assertTrue(copy.args[0] is self.B) self.assertTrue(copy.args[1] is self.A) def test_nsd_constraint(self): """Test the PSD constraint <<. """ constr = self.A << self.B self.assertEqual(constr.name(), "B >> A") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.B.save_value(np.matrix("2 -1; 1 2")) self.A.save_value(np.matrix("1 0; 0 1")) assert constr.value self.A.save_value(np.matrix("3 0; 0 3")) assert not constr.value with self.assertRaises(Exception) as cm: (self.x << self.y) self.assertEqual(str(cm.exception), "Non-square matrix in positive definite constraint.") def test_lt(self): """Test the < operator. """ constr = self.x < self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.x < self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_geq(self): """Test the >= operator. """ constr = self.z >= self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y >= self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_gt(self): """Test the > operator. """ constr = self.z > self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y > self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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,1)) 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) with self.assertRaises(Exception) as cm: (self.z <= self.x).__bool__() self.assertEqual(str(cm.exception), error_str) with self.assertRaises(Exception) as cm: (self.z <= self.x).__nonzero__() self.assertEqual(str(cm.exception), error_str)
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_to_str(self): """Test string representations. """ obj = Minimize(self.a) prob = Problem(obj) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr([]))) constraints = [self.x*2 == self.x, self.x == 0] prob = Problem(obj, constraints) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr(constraints))) # Test str. result = "minimize %(name)s\nsubject to %(name)s == 0\n 0 <= %(name)s" % {"name": self.a.name()} prob = Problem(Minimize(self.a), [self.a == 0, self.a >= 0]) self.assertEqual(str(prob), result) def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() ref = [self.a, self.x, self.b, self.A] if PY2: self.assertItemsEqual(vars_, ref) else: self.assertCountEqual(vars_, ref) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() ref = [p1, p2, p3] if PY2: self.assertItemsEqual(params, ref) else: self.assertCountEqual(params, ref) def test_get_problem_data(self): """Test get_problem_data method. """ with self.assertRaises(Exception) as cm: Problem(Maximize(exp(self.a))).get_problem_data(s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") data = Problem(Maximize(exp(self.a) + 2)).get_problem_data(s.SCS) dims = data["dims"] self.assertEqual(dims['ep'], 1) self.assertEqual(data["c"].shape, (2,)) self.assertEqual(data["A"].shape, (3, 2)) data = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.ECOS) dims = data["dims"] self.assertEqual(dims["q"], [3]) self.assertEqual(data["c"].shape, (3,)) self.assertEqual(data["A"].shape, (0, 3)) self.assertEqual(data["G"].shape, (3, 3)) data = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.CVXOPT) dims = data["dims"] self.assertEqual(dims["q"], [3]) self.assertEqual(data["c"].size, (3, 1)) self.assertEqual(data["A"].size, (0, 3)) self.assertEqual(data["G"].size, (3, 3)) def test_unpack_results(self): """Test unpack results method. """ with self.assertRaises(Exception) as cm: Problem(Minimize(exp(self.a))).unpack_results("blah", None) self.assertEqual(str(cm.exception), "Unknown solver.") prob = Problem(Minimize(exp(self.a)), [self.a == 0]) args = prob.get_problem_data(s.SCS) data = {"c": args["c"], "A": args["A"], "b": args["b"]} results_dict = scs.solve(data, args["dims"]) prob = Problem(Minimize(exp(self.a)), [self.a == 0]) prob.unpack_results(s.SCS, results_dict) self.assertAlmostEqual(self.a.value, 0, places=3) self.assertAlmostEqual(prob.value, 1, places=3) self.assertAlmostEqual(prob.status, s.OPTIMAL) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) args = prob.get_problem_data(s.ECOS) results_dict = ecos.solve(args["c"], args["G"], args["h"], args["dims"], args["A"], args["b"]) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) prob.unpack_results(s.ECOS, results_dict) self.assertItemsAlmostEqual(self.x.value, [0,0]) self.assertAlmostEqual(prob.value, 0) self.assertAlmostEqual(prob.status, s.OPTIMAL) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) args = prob.get_problem_data(s.CVXOPT) results_dict = cvxopt.solvers.conelp(args["c"], args["G"], args["h"], args["dims"], args["A"], args["b"]) prob = Problem(Minimize(norm(self.x)), [self.x == 0]) prob.unpack_results(s.CVXOPT, results_dict) self.assertItemsAlmostEqual(self.x.value, [0,0]) self.assertAlmostEqual(prob.value, 0) self.assertAlmostEqual(prob.status, s.OPTIMAL) # Test silencing and enabling solver messages. def test_verbose(self): import sys # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in installed_solvers(): # Don't test GLPK because there's a race # condition in setting CVXOPT solver options. if solver in ["GLPK", "GLPK_MI"]: continue # if solver == "GLPK": # # GLPK's stdout is separate from python, # # so we have to do this. # # Note: This probably breaks (badly) on Windows. # import os # import tempfile # stdout_fd = 1 # tmp_handle = tempfile.TemporaryFile(bufsize = 0) # os.dup2(tmp_handle.fileno(), stdout_fd) # else: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a + self.x[0]), [self.a >= 2, self.x >= 2]) if SOLVERS[solver].MIP_CAPABLE: p.constraints.append(Bool() == 0) p.solve(verbose=verbose, solver=solver) if SOLVERS[solver].EXP_CAPABLE: p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) # if solver == "GLPK": # # GLPK's stdout is separate from python, # # so we have to do this. # tmp_handle.seek(0) # out = tmp_handle.read() # tmp_handle.close() # else: out = sys.stdout.getvalue() # release output outputs[verbose].append((out, solver)) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output, solver in outputs[True]: print(solver) assert len(output) > 0 for output, solver in outputs[False]: print(solver) assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test",lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a,b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1,b=3,method="test") self.assertEqual(result, (1,3)) result = p.solve(1,method="test") self.assertEqual(result, (1,2)) result = p.solve(1,method="test",b=4) self.assertEqual(result, (1,4)) def test_consistency(self): """Test that variables and constraints keep a consistent order. """ import itertools num_solves = 4 vars_lists = [] ineqs_lists = [] var_ids_order_created = [] for k in range(num_solves): sum = 0 constraints = [] var_ids = [] for i in range(100): var = Variable(name=str(i)) var_ids.append(var.id) sum += var constraints.append(var >= i) var_ids_order_created.append(var_ids) obj = Minimize(sum) p = Problem(obj, constraints) objective, constraints = p.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.ECOS]) # Sort by offset. vars_ = sorted(sym_data.var_offsets.items(), key=lambda key_val: key_val[1]) vars_ = [var_id for (var_id, offset) in vars_] vars_lists.append(vars_) ineqs_lists.append(sym_data.constr_map[s.LEQ]) # Verify order of variables is consistent. for i in range(num_solves): self.assertEqual(var_ids_order_created[i], vars_lists[i]) for i in range(num_solves): for idx, constr in enumerate(ineqs_lists[i]): var_id, _ = lu.get_expr_vars(constr.expr)[0] self.assertEqual(var_ids_order_created[i][idx], var_id) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective, constraints = self.canonicalize() sym_data = SymData(objective, constraints, SOLVERS[s.CVXOPT]) return (len(sym_data.constr_map[s.EQ]), len(sym_data.constr_map[s.LEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj),[eq,eq,le,le]) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Internal constraints. X = Semidef(2) obj = sum_entries(X + X) p = Problem(Minimize(obj)) result = p.solve(method="test") self.assertEqual(result, (0, 1)) # Duplicates from non-linear constraints. exp = norm(self.x, 2) prob = Problem(Minimize(0), [exp <= 1, exp <= 2]) result = prob.solve(method="test") self.assertEqual(result, (0, 4)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3*self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3*self.a - self.b + 100), [self.a >= 2, self.b + 5*self.c - 2 == self.a, self.b <= 5 + self.c]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0/6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5-1.0/6) self.assertAlmostEqual(self.c.value, -1.0/6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1,2]) A = matrix([[3,5],[1,2]]) I = Constant([[1,0],[0,1]]) p = Problem(Minimize(c.T*self.x + self.a), [A*self.x >= [-1,1], 4*I*self.z == self.x, self.z >= [2,2], self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T*self.x.value + self.a.value self.assertAlmostEqual(obj[0,0], result) self.assertItemsAlmostEqual(self.x.value, [8,8], places=3) self.assertItemsAlmostEqual(self.z.value, [2,2], places=3) def test_ecos_noineq(self): """Test ECOS with no inequality constraints. """ T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve(solver=s.ECOS) self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2,(2,3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T*self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), intf.DEFAULT_INTERFACE.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1,2],[3,4]] ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1],[1]]*(self.x + self.a + 1)), [self.a + self.x >= [1,2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1,2],[3,4]]*a a.value = 2 assert not (exp.value - 2*numpy.array([[1,2],[3,4]]).T).any() def test_parameter_problems(self): """Test problems with parameters. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Maximize(p1*self.a), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) p1.value = 2 p2.value = -numpy.ones((3,1)) p3.value = numpy.ones((4, 4)) result = p.solve() self.assertAlmostEqual(result, -6) # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3*normInf(self.a + 2*self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2*self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(float(result), 12) self.assertAlmostEqual(float(list(self.x.value)[1] - list(self.z.value)[1]), 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(float(result), 15) self.assertAlmostEqual(float(list(self.x.value)[1] - list(self.z.value)[1]), 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Row arguments. p = Problem(Minimize(norm2((self.x - self.z).T) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum_entries(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2,-2,-2,-2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual(str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1,2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1,2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem(Minimize(norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z) ) ), [self.x >= [2,3], self.z <= [-1,-4], norm2(self.x + self.z) <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3,4])*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize( norm1(self.x + self.z) ), [self.x >= [2,3], [[1,2],[3,4]]*self.z == [-1,-4], norm2(self.x + self.z) <= 100]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4,3]) self.assertItemsAlmostEqual(self.z.value, [-4,1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2, (2, 3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6*[0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0,0]), [self.x[0,0] <= 2, self.x[1,0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x.value, [2,3]) n = 10 A = matrix(range(n*n), (n,n)) x = Variable(n,n) p = Problem(Minimize(sum_entries(x)), [x == A]) result = p.solve() answer = n*n*(n*n+1)/2 - n*n self.assertAlmostEqual(result, answer) # Matrix variables p = Problem(Maximize( sum(self.A[i,i] + self.A[i,1-i] for i in range(2)) ), [self.A <= [[1,-2],[-3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1,-2,-3,4]) # Indexing arithmetic expressions. exp = [[1,2],[3,4]]*self.z + self.x p = Problem(Minimize(exp[1,0]), [self.x == self.z, self.z == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) p = Problem(Maximize(sum_entries(self.C[0:3:2,1])), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2,1], [1,2]) p = Problem(Maximize(sum_entries( (self.C[0:2,:] + self.A)[:,0:2] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,:], [1,2,1,2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) p = Problem(Maximize( [[3],[4]]*(self.C[0:2,:] + self.A)[:,0] ), [self.C[1:3,:] <= 2, self.C[0,:] == 1, [[1],[2]]*(self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1, 3*self.A[:,0] <= 3]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,2]) self.assertItemsAlmostEqual(self.A.value, [1,-.5,1,1]) p = Problem(Minimize(norm2((self.C[0:2,:] + self.A)[:,0] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,-2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) # Transpose of slice. p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3,:].T <= 2, self.C[0,:].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1,5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1,2], self.y == [3,4,5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1,4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2,2)) p = Problem( Minimize( sum_entries(vstack(self.A, self.C)) ), [self.A >= 2*c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1,2)) p = Problem( Minimize( sum_entries(vstack(c*self.A, c*self.B)) ), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1,-1]) p = Problem( Minimize( c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") # Test the hstack atom. def test_hstack(self): c = matrix(1, (1,5)) p = Problem(Minimize(c * hstack(self.x.T, self.y.T).T), [self.x == [1,2], self.y == [3,4,5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1,4)) p = Problem(Minimize(c * hstack(self.x.T, self.x.T).T), [self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2,2)) p = Problem( Minimize( sum_entries(hstack(self.A.T, self.C.T)) ), [self.A >= 2*c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) D = Variable(3, 3) expr = hstack(self.C, D) p = Problem( Minimize( expr[0,1] + sum_entries(hstack(expr, expr)) ), [self.C >= 0, D >= 0, D[0,0] == 2, self.C[0,1] == 3]) result = p.solve() self.assertAlmostEqual(result, 13) c = matrix([1,-1]) p = Problem( Minimize( c.T * hstack(square(self.a).T, sqrt(self.b).T).T), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") def test_bad_objective(self): """Test using a cvxpy expression as an objective. """ with self.assertRaises(Exception) as cm: Problem(self.x+2) self.assertEqual(str(cm.exception), "Problem objective must be Minimize or Maximize.") # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum_entries(self.x)), [self.x.T >= matrix([1,2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1,2]) p = Problem(Minimize(sum_entries(self.C)), [matrix([1,1]).T*self.C.T >= matrix([0,1,2]).T]) result = p.solve() value = self.C.value constraints = [1*self.C[i,0] + 1*self.C[i,1] >= i for i in range(3)] p = Problem(Minimize(sum_entries(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0,1] - self.A.T[1,0]), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T p = Problem(Minimize(sum_entries(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1,-1]) p = Problem(Minimize(max_elemwise(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum_entries(max_elemwise(c, 2, 2 + c).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum_entries(square(c.T).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum_entries(self.C)), [self.C.T[:,1:3] <= 2, self.C.T[:,0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.A*c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a*2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T*c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T)*c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum_entries(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum_entries(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1,0] - self.A[0,1]) p = Problem(obj, [lambda_max(self.A) <= 100, self.A[0,0] == 2, self.A[1,1] == 2, self.A[1,0] == 2]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_entries_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_entries_exp) p = Problem(Minimize(obj), [self.x >= [2,3], self.z <= [-1,-4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_entries_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0*self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 0) assert self.a.value is not None def test_div(self): """Tests a problem with division. """ obj = Minimize(normInf(self.A/5)) p = Problem(obj, [self.A >= 5]) result = p.solve() self.assertAlmostEqual(result, 1) 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]) def test_invalid_solvers(self): """Tests that errors occur when you use an invalid solver. """ with self.assertRaises(Exception) as cm: Problem(Minimize(-log(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(lambda_max(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(self.a)).solve(solver=s.SCS) self.assertEqual(str(cm.exception), "The solver SCS cannot solve the problem.") def test_reshape(self): """Tests problems with reshape. """ # Test on scalars. self.assertEqual(reshape(1, 1, 1).value, 1) # Test vector to matrix. x = Variable(4) mat = matrix([[1,-1], [2, -2]]) vec = matrix([1, 2, 3, 4]) vec_mat = matrix([[1, 2], [3, 4]]) expr = reshape(x, 2, 2) obj = Minimize(sum_entries(mat*expr)) prob = Problem(obj, [x == vec]) result = prob.solve() self.assertAlmostEqual(result, sum(mat*vec_mat)) # Test on matrix to vector. c = [1, 2, 3, 4] expr = reshape(self.A, 4, 1) obj = Minimize(expr.T*c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) self.assertItemsAlmostEqual(reshape(expr, 2, 2).value, [-1, -2, 3, 4]) # Test matrix to matrix. expr = reshape(self.C, 2, 3) mat = numpy.matrix([[1,-1], [2, -2]]) C_mat = numpy.matrix([[1, 4], [2, 5], [3, 6]]) obj = Minimize(sum_entries(mat*expr)) prob = Problem(obj, [self.C == C_mat]) result = prob.solve() reshaped = numpy.reshape(C_mat, (2, 3), 'F') self.assertAlmostEqual(result, (mat.dot(reshaped)).sum()) self.assertItemsAlmostEqual(expr.value, C_mat) # Test promoted expressions. c = matrix([[1,-1], [2, -2]]) expr = reshape(c*self.a, 1, 4) obj = Minimize(expr*[1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2*c) expr = reshape(c*self.a, 4, 1) obj = Minimize(expr.T*[1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2*c) def test_vec(self): """Tests problems with vec. """ c = [1, 2, 3, 4] expr = vec(self.A) obj = Minimize(expr.T*c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) def test_diag_prob(self): """Test a problem with diag. """ C = Variable(3, 3) obj = Maximize(C[0, 2]) constraints = [diag(C) == 1, C[0, 1] == 0.6, C[1, 2] == -0.3, C == Semidef(3)] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 0.583151) def test_presolve_constant_constraints(self): """Test that the presolver removes constraints with no variables. """ x = Variable() obj = Maximize(sqrt(x)) prob = Problem(obj) data = prob.get_problem_data(s.ECOS) A = data["A"] G = data["G"] for row in range(A.shape[0]): assert A[row, :].nnz > 0 for row in range(G.shape[0]): assert G[row, :].nnz > 0 def test_presolve_parameters(self): """Test presolve with parameters. """ # Test with parameters. gamma = Parameter(sign="positive") x = Variable() obj = Minimize(x) prob = Problem(obj, [gamma == 1, x >= 0]) gamma.value = 0 prob.solve(solver=s.SCS) self.assertEqual(prob.status, s.INFEASIBLE) gamma.value = 1 prob.solve(solver=s.CVXOPT) self.assertEqual(prob.status, s.OPTIMAL) def test_parameter_expressions(self): """Test that expressions with parameters are updated properly. """ x = Variable() y = Variable() x0 = Parameter() xSquared = x0*x0 + 2*x0*(x - x0) # initial guess for x x0.value = 2 # make the constraint x**2 - y == 0 g = xSquared - y # set up the problem obj = abs(x - 1) prob = Problem( Minimize( obj ), [ g == 0 ] ) prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(g.value, 0) # Test multiplication. prob = Problem( Minimize( x0*x ), [ x == 1 ] ) x0.value = 2 prob.solve() x0.value = 1 prob.solve() self.assertAlmostEqual(prob.value, 1) def test_geo_mean(self): import numpy as np x = Variable(2) cost = geo_mean(x) prob = Problem(Maximize(cost), [x <= 1]) prob.solve() self.assertAlmostEqual(prob.value, 1) prob = Problem(Maximize(cost), [sum(x) <= 1]) prob.solve() self.assertItemsAlmostEqual(x.value, [.5, .5]) x = Variable(3, 3) self.assertRaises(ValueError, geo_mean, x) x = Variable(3, 1) g = geo_mean(x) self.assertSequenceEqual(g.w, [Fraction(1, 3)]*3) x = Variable(1, 5) g = geo_mean(x) self.assertSequenceEqual(g.w, [Fraction(1, 5)]*5) # check that we get the right answer for # max geo_mean(x) s.t. sum(x) <= 1 p = np.array([.07, .12, .23, .19, .39]) def short_geo_mean(x, p): p = np.array(p)/sum(p) x = np.array(x) return np.prod(x**p) x = Variable(5) prob = Problem(Maximize(geo_mean(x, p)), [sum(x) <= 1]) prob.solve() x = np.array(x.value).flatten() x_true = p/sum(p) self.assertTrue(np.allclose(prob.value, geo_mean(list(x), p).value)) self.assertTrue(np.allclose(prob.value, short_geo_mean(x, p))) self.assertTrue(np.allclose(x, x_true, 1e-3)) # check that we get the right answer for # max geo_mean(x) s.t. norm(x) <= 1 x = Variable(5) prob = Problem(Maximize(geo_mean(x, p)), [norm(x) <= 1]) prob.solve() x = np.array(x.value).flatten() x_true = np.sqrt(p/sum(p)) self.assertTrue(np.allclose(prob.value, geo_mean(list(x), p).value)) self.assertTrue(np.allclose(prob.value, short_geo_mean(x, p))) self.assertTrue(np.allclose(x, x_true, 1e-3)) def test_pnorm(self): import numpy as np x = Variable(3, name='x') a = np.array([1.0, 2, 3]) # todo: add -1, .5, .3, -2.3 and testing positivity constraints for p in (1, 1.6, 1.3, 2, 1.99, 3, 3.7, np.inf): prob = Problem(Minimize(pnorm(x, p=p)), [x.T*a >= 1]) prob.solve() # formula is true for any a >= 0 with p > 1 if p == np.inf: x_true = np.ones_like(a)/sum(a) elif p == 1: # only works for the particular a = [1,2,3] x_true = np.array([0, 0, 1.0/3]) else: x_true = a**(1.0/(p-1))/a.dot(a**(1.0/(p-1))) x_alg = np.array(x.value).flatten() self.assertTrue(np.allclose(x_alg, x_true, 1e-3), 'p = {}'.format(p)) self.assertTrue(np.allclose(prob.value, np.linalg.norm(x_alg, p))) self.assertTrue(np.allclose(np.linalg.norm(x_alg, p), pnorm(x_alg, p).value)) def test_pnorm_concave(self): import numpy as np x = Variable(3, name='x') # test positivity constraints a = np.array([-1.0, 2, 3]) for p in (-1, .5, .3, -2.3): prob = Problem(Minimize(sum_entries(abs(x-a))), [pnorm(x, p) >= 0]) prob.solve() self.assertTrue(np.allclose(prob.value, 1)) a = np.array([1.0, 2, 3]) for p in (-1, .5, .3, -2.3): prob = Problem(Minimize(sum_entries(abs(x-a))), [pnorm(x, p) >= 0]) prob.solve() self.assertTrue(np.allclose(prob.value, 0)) def test_power(self): x = Variable() prob = Problem(Minimize(power(x, 1.7) + power(x, -2.3) - power(x, .45))) prob.solve() x = x.value self.assertTrue(__builtins__['abs'](1.7*x**.7 - 2.3*x**-3.3 - .45*x**-.55) <= 1e-3)
class TestConstraints(unittest.TestCase): """ 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), "LeqConstraint(%s, %s)" % (repr(self.x), repr(self.x))) constr = self.x <= 2*self.x self.assertEqual(repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2*self.x))) constr = 2*self.x >= self.x self.assertEqual(repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2*self.x))) def test_eq_constraint(self): """Test the EqConstraint class. """ constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2,1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.x.save_value(2) self.z.save_value(2) assert constr.value self.x.save_value(3) assert not constr.value with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_leq_constraint(self): """Test the LeqConstraint class. """ constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_psd_constraint(self): """Test the PSD constraint <<. """ constr = self.A >> self.B self.assertEqual(constr.name(), "A >> B") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.A.save_value(np.matrix("2 -1; 1 2")) self.B.save_value(np.matrix("1 0; 0 1")) assert constr.value self.B.save_value(np.matrix("3 0; 0 3")) assert not constr.value with self.assertRaises(Exception) as cm: (self.x >> self.y) self.assertEqual(str(cm.exception), "Non-square matrix in positive definite constraint.") def test_nsd_constraint(self): """Test the PSD constraint <<. """ constr = self.A << self.B self.assertEqual(constr.name(), "B >> A") self.assertEqual(constr.size, (2, 2)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.B.save_value(np.matrix("2 -1; 1 2")) self.A.save_value(np.matrix("1 0; 0 1")) assert constr.value self.A.save_value(np.matrix("3 0; 0 3")) assert not constr.value with self.assertRaises(Exception) as cm: (self.x << self.y) self.assertEqual(str(cm.exception), "Non-square matrix in positive definite constraint.") def test_lt(self): """Test the < operator. """ constr = self.x < self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.x < self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_geq(self): """Test the >= operator. """ constr = self.z >= self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y >= self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_gt(self): """Test the > operator. """ constr = self.z > self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y > self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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,1)) def test_chained_constraints(self): """Tests that chaining constraints raises an error. """ with self.assertRaises(Exception) as cm: (self.z <= self.x <= 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.") with self.assertRaises(Exception) as cm: (self.x == self.z == 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.")
class TestExpressions(unittest.TestCase): """ 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_INTERFACE # 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.size, (2,1)) self.assertEqual(y.size, (1,1)) self.assertEqual(x.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(x.canonical_form[0].size, (2,1)) self.assertEqual(x.canonical_form[1], []) # Scalar variable coeff = self.a.coefficients() self.assertEqual(coeff[self.a], [1]) # Vector variable. coeffs = x.coefficients() self.assertItemsEqual(coeffs.keys(), [x]) vec = coeffs[x][0] self.assertEqual(vec.size, (2,2)) self.assertEqual(vec[0,0], 1) # Matrix variable. coeffs = self.A.coefficients() self.assertItemsEqual(coeffs.keys(), [self.A]) self.assertEqual(len(coeffs[self.A]), 2) mat = coeffs[self.A][1] self.assertEqual(mat.size, (2,4)) self.assertEqual(mat[0,2], 1) # Test tranposing variables. def test_transpose_variable(self): var = self.a.T self.assertEquals(var.name(), "a") self.assertEquals(var.size, (1,1)) self.a.save_value(2) self.assertEquals(var.value, 2) var = self.x.T self.assertEquals(var.name(), "x.T") self.assertEquals(var.size, (1,2)) self.x.save_value( matrix([1,2]) ) self.assertEquals(var.value[0,0], 1) self.assertEquals(var.value[0,1], 2) var = self.C.T self.assertEquals(var.name(), "C.T") self.assertEquals(var.size, (2,3)) # coeffs = var.canonical_form[0].coefficients() # mat = coeffs.values()[0][0] # self.assertEqual(mat.size, (2,6)) # self.assertEqual(mat[1,3], 1) index = var[1,0] self.assertEquals(index.name(), "C.T[1,0]") self.assertEquals(index.size, (1,1)) var = self.x.T.T self.assertEquals(var.name(), "x.T.T") self.assertEquals(var.size, (2,1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2) self.assertEqual(c.value, 2) self.assertEqual(c.size, (1,1)) self.assertEqual(c.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual(Constant(-2).sign, u.Sign.NEGATIVE_KEY) self.assertEqual(Constant(0).sign, u.Sign.ZERO_KEY) self.assertEqual(c.canonical_form[0].size, (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.size, (1,2)) self.assertEqual(c._dcp_attr.sign.neg_mat.shape, (1,2)) # Test sign of a complex expression. c = Constant([1, 2]) A = Constant([[1,1],[1,1]]) exp = c.T*A*c self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) self.assertEqual((c.T*c).sign, u.Sign.POSITIVE_KEY) exp = c.T.T self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (2,1)) exp = c.T*self.A self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (1,2)) # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1,1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1,1) for Parameter value.") val = -np.ones((4,3)) val[0,0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Test the AddExpresion class. def test_add_expression(self): # Vectors c = Constant([2,2]) exp = self.x + c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Test the SubExpresion class. def test_sub_expression(self): # Vectors c = Constant([2,2]) exp = self.x - c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Test the MulExpresion class. def test_mul_expression(self): # Vectors c = Constant([[2],[2]]) exp = c*self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((c[0]*self.x).sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (1,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1,1)) with self.assertRaises(Exception) as cm: ([2,2,3]*self.x) print cm.exception self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1],[2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions.") with self.assertRaises(Exception) as cm: (self.A * self.B) self.assertEqual(str(cm.exception), "Cannot multiply two non-constants.") # Constant expressions T = Constant([[1,2,3],[3,5,5]]) exp = (T + T) * self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3,2)) # Expression that would break sign multiplication without promotion. c = Constant([[2],[2],[-2]]) exp = [[1],[2]] + c*self.C self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (1,2)) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_positive() self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3,2)) # Test promotion of scalar constants. def test_scalar_const_promotion(self): # Vectors exp = self.x + 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_negative() self.assertEqual(exp.canonical_form[0].size, (2,1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2,1)) self.assertEqual((4 - self.x).size, (2,1)) self.assertEqual((4 * self.x).size, (2,1)) self.assertEqual((4 <= self.x).size, (2,1)) self.assertEqual((4 == self.x).size, (2,1)) self.assertEqual((self.x >= 4).size, (2,1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((3 * self.A).size, (2,2)) self.assertEqual(exp.size, (2,2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1,0] # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEquals(exp.size, (1,1)) # coeff = exp.canonical_form[0].coefficients()[self.x][0] # self.assertEqual(coeff[0,1], 1) self.assertEqual(exp.value, None) exp = self.x[1,0].T # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) with self.assertRaises(Exception) as cm: (self.x[2,0]) self.assertEqual(str(cm.exception), "Index/slice out of bounds.") # Slicing exp = self.C[0:2,1] # self.assertEquals(exp.name(), "C[0:2,1]") self.assertEquals(exp.size, (2,1)) exp = self.C[0:,0:2] # self.assertEquals(exp.name(), "C[0:,0:2]") self.assertEquals(exp.size, (3,2)) exp = self.C[0::2,0::2] # self.assertEquals(exp.name(), "C[0::2,0::2]") self.assertEquals(exp.size, (2,1)) exp = self.C[:3,:1:2] # self.assertEquals(exp.name(), "C[0:3,0]") self.assertEquals(exp.size, (3,1)) exp = self.C[0:,0] # self.assertEquals(exp.name(), "C[0:,0]") self.assertEquals(exp.size, (3,1)) c = Constant([[1,-2],[0,4]]) exp = c[1,1] self.assertEqual(exp.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) assert exp.is_positive() self.assertEqual(c[0,1].sign, u.Sign.ZERO_KEY) assert c[0, 1].is_zero() self.assertEqual(c[1,0].sign, u.Sign.NEGATIVE_KEY) assert c[1,0].is_negative() self.assertEquals(exp.size, (1,1)) 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, u.Curvature.CONSTANT_KEY) assert exp.is_constant() self.assertEquals(exp.size, (3,2)) self.assertEqual(exp[0,1].value, 7) # Slice of transpose exp = self.C.T[0:2,1] self.assertEquals(exp.size, (2,1)) # Arithmetic expression indexing exp = (self.x + self.z)[1,0] # self.assertEqual(exp.name(), "x[1,0] + z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x + self.a)[1,0] # self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.z)[1,0] # self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.a)[1,0] # self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) exp = (-self.x)[1,0] # self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.x)[1,0] # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.a)[1,0] # self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1,1))
class TestConstraints(unittest.TestCase): """ 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') # Test the EqConstraint class. def test_eq_constraint(self): constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2, 1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.x.save_value(2) self.z.save_value(2) assert constr.value self.x.save_value(3) assert not constr.value with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Test the LeqConstraint class. def test_leq_constraint(self): constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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, 1)) self.assertEqual(len(constr.format()), 2) # Test the SDC class. def test_sdc_constraint(self): exp = self.x + self.z scalar_exp = self.a + self.b constr = SOC(scalar_exp, [exp]) self.assertEqual(constr.size, (3, 1)) self.assertEqual(len(constr.format()), 2)
class TestExpressions(unittest.TestCase): """ 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_INTERFACE # 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.size, (2, 1)) self.assertEqual(y.size, (1, 1)) self.assertEqual(x.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(x.canonical_form[0].size, (2, 1)) self.assertEqual(x.canonical_form[1], []) # 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) mat = coeffs[self.A.id][1] self.assertEqual(mat.shape, (2, 4)) self.assertEqual(mat[0, 2], 1) # Test tranposing variables. def test_transpose_variable(self): var = self.a.T self.assertEquals(var.name(), "a") self.assertEquals(var.size, (1, 1)) self.a.save_value(2) self.assertEquals(var.value, 2) var = self.x.T self.assertEquals(var.name(), "x.T") self.assertEquals(var.size, (1, 2)) self.x.save_value(matrix([1, 2])) self.assertEquals(var.value[0, 0], 1) self.assertEquals(var.value[0, 1], 2) var = self.C.T self.assertEquals(var.name(), "C.T") self.assertEquals(var.size, (2, 3)) # coeffs = var.canonical_form[0].coefficients() # mat = coeffs.values()[0][0] # self.assertEqual(mat.size, (2,6)) # self.assertEqual(mat[1,3], 1) index = var[1, 0] self.assertEquals(index.name(), "C.T[1,0]") self.assertEquals(index.size, (1, 1)) var = self.x.T.T self.assertEquals(var.name(), "x.T.T") self.assertEquals(var.size, (2, 1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2) self.assertEqual(c.value, 2) self.assertEqual(c.size, (1, 1)) self.assertEqual(c.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(c.sign, u.Sign.POSITIVE_KEY) self.assertEqual(Constant(-2).sign, u.Sign.NEGATIVE_KEY) self.assertEqual(Constant(0).sign, u.Sign.ZERO_KEY) self.assertEqual(c.canonical_form[0].size, (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.size, (1, 2)) self.assertEqual(c._dcp_attr.sign.neg_mat.shape, (1, 2)) # Test sign of a complex expression. c = Constant([1, 2]) A = Constant([[1, 1], [1, 1]]) exp = c.T * A * c self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) self.assertEqual((c.T * c).sign, u.Sign.POSITIVE_KEY) exp = c.T.T self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (2, 1)) exp = c.T * self.A self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (1, 2)) # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1, 1)) p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = 1 self.assertEqual(str(cm.exception), "Invalid dimensions (1,1) for Parameter value.") val = -np.ones((4, 3)) val[0, 0] = 2 p = Parameter(4, 3, sign="positive") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") p = Parameter(4, 3, sign="negative") with self.assertRaises(Exception) as cm: p.value = val self.assertEqual(str(cm.exception), "Invalid sign for Parameter value.") # No error for unknown sign. p = Parameter(4, 3) p.value = val # Test the AddExpresion class. def test_add_expression(self): # Vectors c = Constant([2, 2]) exp = self.x + c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Test the SubExpresion class. def test_sub_expression(self): # Vectors c = Constant([2, 2]) exp = self.x - c self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2, 1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (2, 2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") # Test the MulExpresion class. def test_mul_expression(self): # Vectors c = Constant([[2], [2]]) exp = c * self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((c[0] * self.x).sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (1, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: ([2, 2, 3] * self.x) print cm.exception self.assertEqual(str(cm.exception), "Incompatible dimensions (3, 1) (2, 1)") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1], [2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 2) (3, 2)") with self.assertRaises(Exception) as cm: (self.A * self.B) self.assertEqual(str(cm.exception), "Cannot multiply two non-constants.") # Constant expressions T = Constant([[1, 2, 3], [3, 5, 5]]) exp = (T + T) * self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3, 2)) # Expression that would break sign multiplication without promotion. c = Constant([[2], [2], [-2]]) exp = [[1], [2]] + c * self.C self.assertEqual(exp._dcp_attr.sign.pos_mat.shape, (1, 2)) # Test the DivExpresion class. def test_div_expression(self): # Vectors exp = self.x / 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (2, 1)) 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, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.size, (1, 1)) self.assertEqual(exp.sign, u.Sign.NEGATIVE_KEY) # Parameters. p = Parameter(sign="positive") exp = 2 / p p.value = 2 self.assertEquals(exp.value, 1) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_positive() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.size, (3, 2)) # Test promotion of scalar constants. def test_scalar_const_promotion(self): # Vectors exp = self.x + 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) assert not exp.is_negative() self.assertEqual(exp.canonical_form[0].size, (2, 1)) self.assertEqual(exp.canonical_form[1], []) # self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2, 1)) self.assertEqual((4 - self.x).size, (2, 1)) self.assertEqual((4 * self.x).size, (2, 1)) self.assertEqual((4 <= self.x).size, (2, 1)) self.assertEqual((4 == self.x).size, (2, 1)) self.assertEqual((self.x >= 4).size, (2, 1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual((3 * self.A).size, (2, 2)) self.assertEqual(exp.size, (2, 2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1, 0] # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) assert exp.is_affine() self.assertEquals(exp.size, (1, 1)) # coeff = exp.canonical_form[0].coefficients()[self.x][0] # self.assertEqual(coeff[0,1], 1) self.assertEqual(exp.value, None) exp = self.x[1, 0].T # self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) with self.assertRaises(Exception) as cm: (self.x[2, 0]) self.assertEqual(str(cm.exception), "Index/slice out of bounds.") # Slicing exp = self.C[0:2, 1] # self.assertEquals(exp.name(), "C[0:2,1]") self.assertEquals(exp.size, (2, 1)) exp = self.C[0:, 0:2] # self.assertEquals(exp.name(), "C[0:,0:2]") self.assertEquals(exp.size, (3, 2)) exp = self.C[0::2, 0::2] # self.assertEquals(exp.name(), "C[0::2,0::2]") self.assertEquals(exp.size, (2, 1)) exp = self.C[:3, :1:2] # self.assertEquals(exp.name(), "C[0:3,0]") self.assertEquals(exp.size, (3, 1)) exp = self.C[0:, 0] # self.assertEquals(exp.name(), "C[0:,0]") self.assertEquals(exp.size, (3, 1)) c = Constant([[1, -2], [0, 4]]) exp = c[1, 1] self.assertEqual(exp.curvature, u.Curvature.CONSTANT_KEY) self.assertEqual(exp.sign, u.Sign.POSITIVE_KEY) assert exp.is_positive() self.assertEqual(c[0, 1].sign, u.Sign.ZERO_KEY) assert c[0, 1].is_zero() self.assertEqual(c[1, 0].sign, u.Sign.NEGATIVE_KEY) assert c[1, 0].is_negative() self.assertEquals(exp.size, (1, 1)) 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, u.Curvature.CONSTANT_KEY) assert exp.is_constant() self.assertEquals(exp.size, (3, 2)) self.assertEqual(exp[0, 1].value, 7) # Slice of transpose exp = self.C.T[0:2, 1] self.assertEquals(exp.size, (2, 1)) # Arithmetic expression indexing exp = (self.x + self.z)[1, 0] # self.assertEqual(exp.name(), "x[1,0] + z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEqual(exp.sign, u.Sign.UNKNOWN_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x + self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x - self.z)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (self.x - self.a)[1, 0] # self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) exp = (-self.x)[1, 0] # self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c * self.x)[1, 0] # self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1)) c = Constant([[1, 2], [3, 4]]) exp = (c * self.a)[1, 0] # self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, u.Curvature.AFFINE_KEY) self.assertEquals(exp.size, (1, 1))
class TestProblem(BaseTest): """ Unit tests for the expression/expression module. """ 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') def test_to_str(self): """Test string representations. """ obj = Minimize(self.a) prob = Problem(obj) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr([]))) constraints = [self.x*2 == self.x, self.x == 0] prob = Problem(obj, constraints) self.assertEqual(repr(prob), "Problem(%s, %s)" % (repr(obj), repr(constraints))) # Test str. result = "minimize %(name)s\nsubject to %(name)s == 0\n 0 <= %(name)s" % {"name": self.a.name()} prob = Problem(Minimize(self.a), [self.a == 0, self.a >= 0]) self.assertEqual(str(prob), result) def test_variables(self): """Test the variables method. """ p = Problem(Minimize(self.a), [self.a <= self.x, self.b <= self.A + 2]) vars_ = p.variables() self.assertItemsEqual(vars_, [self.a, self.x, self.b, self.A]) def test_parameters(self): """Test the parameters method. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Minimize(p1), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) params = p.parameters() self.assertItemsEqual(params, [p1, p2, p3]) def test_get_problem_data(self): """Test get_problem_data method. """ with self.assertRaises(Exception) as cm: Problem(Maximize(exp(self.a))).get_problem_data(s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Maximize(exp(self.a))).get_problem_data(s.CVXOPT) self.assertEqual(str(cm.exception), "Cannot return problem data for the solver CVXOPT.") args = Problem(Maximize(exp(self.a) + 2)).get_problem_data(s.SCS) data, dims = args self.assertEqual(dims['ep'], 1) self.assertEqual(data["c"].shape, (2,)) self.assertEqual(data["A"].shape, (3, 2)) args = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.ECOS) c, G, h, dims, A, b = args self.assertEqual(dims["q"], [3]) self.assertEqual(c.shape, (3,)) self.assertEqual(A.shape, (0, 3)) self.assertEqual(G.shape, (3, 3)) args = Problem(Minimize(norm(self.x) + 3)).get_problem_data(s.CVXOPT) c, G, h, dims, A, b = args self.assertEqual(dims["q"], [3]) self.assertEqual(c.size, (3, 1)) self.assertEqual(A.size, (0, 3)) self.assertEqual(G.size, (3, 3)) # Test silencing and enabling solver messages. def test_verbose(self): # From http://stackoverflow.com/questions/5136611/capture-stdout-from-a-script-in-python # setup the environment outputs = {True: [], False: []} backup = sys.stdout # #### for verbose in [True, False]: for solver in [s.ECOS, s.CVXOPT, s.SCS]: sys.stdout = StringIO() # capture output p = Problem(Minimize(self.a + self.x[0]), [self.a >= 2, self.x >= 2]) p.solve(verbose=verbose, solver=solver) if solver != s.ECOS: p = Problem(Minimize(self.a), [log(self.a) >= 2]) p.solve(verbose=verbose, solver=solver) out = sys.stdout.getvalue() # release output outputs[verbose].append(out.upper()) # #### sys.stdout.close() # close the stream sys.stdout = backup # restore original stdout for output in outputs[True]: assert len(output) > 0 for output in outputs[False]: assert len(output) == 0 # Test registering other solve methods. def test_register_solve(self): Problem.register_solve("test",lambda self: 1) p = Problem(Minimize(1)) result = p.solve(method="test") self.assertEqual(result, 1) def test(self, a, b=2): return (a,b) Problem.register_solve("test", test) p = Problem(Minimize(0)) result = p.solve(1,b=3,method="test") self.assertEqual(result, (1,3)) result = p.solve(1,method="test") self.assertEqual(result, (1,2)) result = p.solve(1,method="test",b=4) self.assertEqual(result, (1,4)) def test_consistency(self): """Test that variables and constraints keep a consistent order. """ import itertools num_solves = 4 vars_lists = [] ineqs_lists = [] var_ids_order_created = [] for k in range(num_solves): sum = 0 constraints = [] var_ids = [] for i in range(100): var = Variable(name=str(i)) var_ids.append(var.id) sum += var constraints.append(var >= i) var_ids_order_created.append(var_ids) obj = Minimize(sum) p = Problem(obj, constraints) objective, constr_map = p.canonicalize() all_ineq = itertools.chain(constr_map[s.EQ], constr_map[s.LEQ]) var_offsets, var_sizes, x_length = p._get_var_offsets(objective, all_ineq) # Sort by offset. vars_ = sorted(var_offsets.items(), key=lambda (var_id, offset): offset) vars_ = [var_id for (var_id, offset) in vars_] vars_lists.append(vars_) ineqs_lists.append(constr_map[s.LEQ]) # Verify order of variables is consistent. for i in range(num_solves): self.assertEqual(var_ids_order_created[i], vars_lists[i]) for i in range(num_solves): for idx, constr in enumerate(ineqs_lists[i]): var_id, _ = lu.get_expr_vars(constr.expr)[0] self.assertEqual(var_ids_order_created[i][idx], var_id) # Test removing duplicate constraint objects. def test_duplicate_constraints(self): eq = (self.x == 2) le = (self.x <= 2) obj = 0 def test(self): objective, constr_map = self.canonicalize() self._presolve(objective, constr_map) print len(constr_map[s.SOC]) dims = self._format_for_solver(constr_map, s.ECOS) return (len(constr_map[s.EQ]),len(constr_map[s.LEQ])) Problem.register_solve("test", test) p = Problem(Minimize(obj),[eq,eq,le,le]) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Internal constraints. X = Semidef(2) obj = sum_entries(X + X) p = Problem(Minimize(obj)) result = p.solve(method="test") self.assertEqual(result, (1, 1)) # Duplicates from non-linear constraints. exp = norm(self.x, 2) prob = Problem(Minimize(0), [exp <= 1, exp <= 2]) result = prob.solve(method="test") self.assertEqual(result, (0, 4)) # Test the is_dcp method. def test_is_dcp(self): p = Problem(Minimize(normInf(self.a))) self.assertEqual(p.is_dcp(), True) p = Problem(Maximize(normInf(self.a))) self.assertEqual(p.is_dcp(), False) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") p.solve(ignore_dcp=True) # Test problems involving variables with the same name. def test_variable_name_conflict(self): var = Variable(name='a') p = Problem(Maximize(self.a + var), [var == 2 + self.a, var <= 3]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 1) self.assertAlmostEqual(var.value, 3) # Test scalar LP problems. def test_scalar_lp(self): p = Problem(Minimize(3*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) self.assertAlmostEqual(self.a.value, 2) p = Problem(Maximize(3*self.a - self.b), [self.a <= 2, self.b == self.a, self.b <= 5]) result = p.solve() self.assertAlmostEqual(result, 4.0) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 2) # With a constant in the objective. p = Problem(Minimize(3*self.a - self.b + 100), [self.a >= 2, self.b + 5*self.c - 2 == self.a, self.b <= 5 + self.c]) result = p.solve() self.assertAlmostEqual(result, 101 + 1.0/6) self.assertAlmostEqual(self.a.value, 2) self.assertAlmostEqual(self.b.value, 5-1.0/6) self.assertAlmostEqual(self.c.value, -1.0/6) # Test status and value. exp = Maximize(self.a) p = Problem(exp, [self.a <= 2]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.OPTIMAL) assert self.a.value is not None assert p.constraints[0].dual_value is not None # Unbounded problems. p = Problem(Maximize(self.a), [self.a >= 2]) p.solve(solver=s.ECOS) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value > 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2]) result = p.solve(solver=s.CVXOPT) self.assertEqual(result, p.value) self.assertEqual(p.status, s.UNBOUNDED) assert numpy.isinf(p.value) assert p.value < 0 # Infeasible problems. p = Problem(Maximize(self.a), [self.a >= 2, self.a <= 1]) self.a.save_value(2) p.constraints[0].save_value(2) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value < 0 assert self.a.value is None assert p.constraints[0].dual_value is None p = Problem(Minimize(-self.a), [self.a >= 2, self.a <= 1]) result = p.solve(solver=s.ECOS) self.assertEqual(result, p.value) self.assertEqual(p.status, s.INFEASIBLE) assert numpy.isinf(p.value) assert p.value > 0 # Test vector LP problems. def test_vector_lp(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.x), [self.x >= c]) result = p.solve() self.assertAlmostEqual(result, 5) self.assertItemsAlmostEqual(self.x.value, [1,2]) A = matrix([[3,5],[1,2]]) I = Constant([[1,0],[0,1]]) p = Problem(Minimize(c.T*self.x + self.a), [A*self.x >= [-1,1], 4*I*self.z == self.x, self.z >= [2,2], self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 26, places=3) obj = c.T*self.x.value + self.a.value self.assertAlmostEqual(obj[0], result) self.assertItemsAlmostEqual(self.x.value, [8,8], places=3) self.assertItemsAlmostEqual(self.z.value, [2,2], places=3) def test_ecos_noineq(self): """Test ECOS with no inequality constraints. """ T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve(solver=s.ECOS) self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) # Test matrix LP problems. def test_matrix_lp(self): T = matrix(1, (2, 2)) p = Problem(Minimize(1), [self.A == T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, T) T = matrix(2,(2,3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() self.assertAlmostEqual(result, 1) self.assertItemsAlmostEqual(self.A.value, self.B.value) self.assertItemsAlmostEqual(self.C.value, T) assert (self.A.value >= T*self.C.value).all() # Test variables are dense. self.assertEqual(type(self.A.value), p._DENSE_INTF.TARGET_MATRIX) # Test variable promotion. def test_variable_promotion(self): p = Problem(Minimize(self.a), [self.x <= self.a, self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(self.a), [self.A <= self.a, self.A == [[1,2],[3,4]] ]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertAlmostEqual(self.a.value, 4) # Promotion must happen before the multiplication. p = Problem(Minimize([[1],[1]]*(self.x + self.a + 1)), [self.a + self.x >= [1,2]]) result = p.solve() self.assertAlmostEqual(result, 5) # Test parameter promotion. def test_parameter_promotion(self): a = Parameter() exp = [[1,2],[3,4]]*a a.value = 2 assert not (exp.value - 2*numpy.array([[1,2],[3,4]]).T).any() def test_parameter_problems(self): """Test problems with parameters. """ p1 = Parameter() p2 = Parameter(3, sign="negative") p3 = Parameter(4, 4, sign="positive") p = Problem(Maximize(p1*self.a), [self.a + p1 <= p2, self.b <= p3 + p3 + 2]) p1.value = 2 p2.value = -numpy.ones((3,1)) p3.value = numpy.ones((4, 4)) result = p.solve() self.assertAlmostEqual(result, -6) # Test problems with normInf def test_normInf(self): # Constant argument. p = Problem(Minimize(normInf(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(normInf(self.a)), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, 2) p = Problem(Minimize(3*normInf(self.a + 2*self.b) + self.c), [self.a >= 2, self.b <= -1, self.c == 3]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertAlmostEqual(self.a.value + 2*self.b.value, 0) self.assertAlmostEqual(self.c.value, 3) # Maximize p = Problem(Maximize(-normInf(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(normInf(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertAlmostEqual(list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm1 def test_norm1(self): # Constant argument. p = Problem(Minimize(norm1(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm1(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm1(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 15) self.assertAlmostEqual(list(self.x.value)[1] - list(self.z.value)[1], 7) # Test problems with norm2 def test_norm2(self): # Constant argument. p = Problem(Minimize(norm2(-2))) result = p.solve() self.assertAlmostEqual(result, 2) # Scalar arguments. p = Problem(Minimize(norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertAlmostEqual(self.a.value, -2) # Maximize p = Problem(Maximize(-norm2(self.a)), [self.a <= -2]) result = p.solve() self.assertAlmostEqual(result, -2) self.assertAlmostEqual(self.a.value, -2) # Vector arguments. p = Problem(Minimize(norm2(self.x - self.z) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Row arguments. p = Problem(Minimize(norm2((self.x - self.z).T) + 5), [self.x >= [2,3], self.z <= [-1,-4]]) result = p.solve() self.assertAlmostEqual(result, 12.61577) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test problems with abs def test_abs(self): p = Problem(Minimize(sum_entries(abs(self.A))), [-2 >= self.A]) result = p.solve() self.assertAlmostEqual(result, 8) self.assertItemsAlmostEqual(self.A.value, [-2,-2,-2,-2]) # Test problems with quad_form. def test_quad_form(self): with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, self.A))).solve() self.assertEqual(str(cm.exception), "At least one argument to quad_form must be constant.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(1, self.A))).solve() self.assertEqual(str(cm.exception), "Invalid dimensions for arguments.") with self.assertRaises(Exception) as cm: Problem(Minimize(quad_form(self.x, [[-1, 0], [0, 9]]))).solve() self.assertEqual(str(cm.exception), "P has both positive and negative eigenvalues.") P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(self.x, P)), [self.x >= 1]) result = p.solve() self.assertAlmostEqual(result, 13, places=3) c = [1,2] p = Problem(Minimize(quad_form(c, self.A)), [self.A >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) c = [1,2] P = [[4, 0], [0, 9]] p = Problem(Minimize(quad_form(c, P))) result = p.solve() self.assertAlmostEqual(result, 40) # Test combining atoms def test_mixed_atoms(self): p = Problem(Minimize(norm2(5 + norm1(self.z) + norm1(self.x) + normInf(self.x - self.z) ) ), [self.x >= [2,3], self.z <= [-1,-4], norm2(self.x + self.z) <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Test multiplying by constant atoms. def test_mult_constant_atoms(self): p = Problem(Minimize(norm2([3,4])*self.a), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertAlmostEqual(self.a.value, 2) # Test recovery of dual variables. def test_dual_variables(self): p = Problem(Minimize( norm1(self.x + self.z) ), [self.x >= [2,3], [[1,2],[3,4]]*self.z == [-1,-4], norm2(self.x + self.z) <= 100]) result = p.solve() self.assertAlmostEqual(result, 4) self.assertItemsAlmostEqual(self.x.value, [4,3]) self.assertItemsAlmostEqual(self.z.value, [-4,1]) # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, [0, 1]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, [-1, 0.5]) self.assertAlmostEqual(p.constraints[2].dual_value, 0) T = matrix(2, (2, 3)) c = matrix([3,4]) p = Problem(Minimize(1), [self.A >= T*self.C, self.A == self.B, self.C == T.T]) result = p.solve() # Dual values self.assertItemsAlmostEqual(p.constraints[0].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[1].dual_value, 4*[0]) self.assertItemsAlmostEqual(p.constraints[2].dual_value, 6*[0]) # Test problems with indexing. def test_indexing(self): # Vector variables p = Problem(Maximize(self.x[0,0]), [self.x[0,0] <= 2, self.x[1,0] == 3]) result = p.solve() self.assertAlmostEqual(result, 2) self.assertItemsAlmostEqual(self.x.value, [2,3]) n = 10 A = matrix(range(n*n), (n,n)) x = Variable(n,n) p = Problem(Minimize(sum_entries(x)), [x == A]) result = p.solve() answer = n*n*(n*n+1)/2 - n*n self.assertAlmostEqual(result, answer) # Matrix variables p = Problem(Maximize( sum(self.A[i,i] + self.A[i,1-i] for i in range(2)) ), [self.A <= [[1,-2],[-3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) self.assertItemsAlmostEqual(self.A.value, [1,-2,-3,4]) # Indexing arithmetic expressions. exp = [[1,2],[3,4]]*self.z + self.x p = Problem(Minimize(exp[1,0]), [self.x == self.z, self.z == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.x.value, self.z.value) # Test problems with slicing. def test_slicing(self): p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) p = Problem(Maximize(sum_entries(self.C[0:3:2,1])), [self.C[1:3,:] <= 2, self.C[0,:] == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:3:2,1], [1,2]) p = Problem(Maximize(sum_entries( (self.C[0:2,:] + self.A)[:,0:2] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,:], [1,2,1,2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) p = Problem(Maximize( [[3],[4]]*(self.C[0:2,:] + self.A)[:,0] ), [self.C[1:3,:] <= 2, self.C[0,:] == 1, [[1],[2]]*(self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1, 3*self.A[:,0] <= 3]) result = p.solve() self.assertAlmostEqual(result, 12) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,2]) self.assertItemsAlmostEqual(self.A.value, [1,-.5,1,1]) p = Problem(Minimize(norm2((self.C[0:2,:] + self.A)[:,0] )), [self.C[1:3,:] <= 2, self.C[0,:] == 1, (self.A + self.B)[:,0] == 3, (self.A + self.B)[:,1] == 2, self.B == 1]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.C.value[0:2,0], [1,-2]) self.assertItemsAlmostEqual(self.A.value, [2,2,1,1]) # Transpose of slice. p = Problem(Maximize(sum_entries(self.C)), [self.C[1:3,:].T <= 2, self.C[0,:].T == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test the vstack atom. def test_vstack(self): c = matrix(1, (1,5)) p = Problem(Minimize(c * vstack(self.x, self.y)), [self.x == [1,2], self.y == [3,4,5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1,4)) p = Problem(Minimize(c * vstack(self.x, self.x)), [self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2,2)) p = Problem( Minimize( sum_entries(vstack(self.A, self.C)) ), [self.A >= 2*c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) c = matrix(1, (1,2)) p = Problem( Minimize( sum_entries(vstack(c*self.A, c*self.B)) ), [self.A >= 2, self.B == -2]) result = p.solve() self.assertAlmostEqual(result, 0) c = matrix([1,-1]) p = Problem( Minimize( c.T * vstack(square(self.a), sqrt(self.b))), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") # Test the hstack atom. def test_hstack(self): c = matrix(1, (1,5)) p = Problem(Minimize(c * hstack(self.x.T, self.y.T).T), [self.x == [1,2], self.y == [3,4,5]]) result = p.solve() self.assertAlmostEqual(result, 15) c = matrix(1, (1,4)) p = Problem(Minimize(c * hstack(self.x.T, self.x.T).T), [self.x == [1,2]]) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix(1, (2,2)) p = Problem( Minimize( sum_entries(hstack(self.A.T, self.C.T)) ), [self.A >= 2*c, self.C == -2]) result = p.solve() self.assertAlmostEqual(result, -4) D = Variable(3, 3) expr = hstack(self.C, D) p = Problem( Minimize( expr[0,1] + sum_entries(hstack(expr, expr)) ), [self.C >= 0, D >= 0, D[0,0] == 2, self.C[0,1] == 3]) result = p.solve() self.assertAlmostEqual(result, 13) c = matrix([1,-1]) p = Problem( Minimize( c.T * hstack(square(self.a).T, sqrt(self.b).T).T), [self.a == 2, self.b == 16]) with self.assertRaises(Exception) as cm: p.solve() self.assertEqual(str(cm.exception), "Problem does not follow DCP rules.") def test_bad_objective(self): """Test using a cvxpy expression as an objective. """ with self.assertRaises(Exception) as cm: Problem(self.x+2) self.assertEqual(str(cm.exception), "Problem objective must be Minimize or Maximize.") # Test variable transpose. def test_transpose(self): p = Problem(Minimize(sum_entries(self.x)), [self.x.T >= matrix([1,2]).T]) result = p.solve() self.assertAlmostEqual(result, 3) self.assertItemsAlmostEqual(self.x.value, [1,2]) p = Problem(Minimize(sum_entries(self.C)), [matrix([1,1]).T*self.C.T >= matrix([0,1,2]).T]) result = p.solve() value = self.C.value constraints = [1*self.C[i,0] + 1*self.C[i,1] >= i for i in range(3)] p = Problem(Minimize(sum_entries(self.C)), constraints) result2 = p.solve() self.assertAlmostEqual(result, result2) self.assertItemsAlmostEqual(self.C.value, value) p = Problem(Minimize(self.A[0,1] - self.A.T[1,0]), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertAlmostEqual(result, 0) exp = (-self.x).T p = Problem(Minimize(sum_entries(self.x)), [(-self.x).T <= 1]) result = p.solve() self.assertAlmostEqual(result, -2) c = matrix([1,-1]) p = Problem(Minimize(max_elemwise(c.T, 2, 2 + c.T)[1])) result = p.solve() self.assertAlmostEqual(result, 2) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum_entries(max_elemwise(c, 2, 2 + c).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) c = matrix([[1,-1,2],[1,-1,2]]) p = Problem(Minimize(sum_entries(square(c.T).T[:,0]))) result = p.solve() self.assertAlmostEqual(result, 6) # Slice of transpose. p = Problem(Maximize(sum_entries(self.C)), [self.C.T[:,1:3] <= 2, self.C.T[:,0] == 1]) result = p.solve() self.assertAlmostEqual(result, 10) self.assertItemsAlmostEqual(self.C.value, 2*[1,2,2]) # Test multiplication on the left by a non-constant. def test_multiplication_on_left(self): c = matrix([1,2]) p = Problem(Minimize(c.T*self.A*c), [self.A >= 2]) result = p.solve() self.assertAlmostEqual(result, 18) p = Problem(Minimize(self.a*2), [self.a >= 2]) result = p.solve() self.assertAlmostEqual(result, 4) p = Problem(Minimize(self.x.T*c), [self.x >= 2]) result = p.solve() self.assertAlmostEqual(result, 6) p = Problem(Minimize((self.x.T + self.z.T)*c), [self.x >= 2, self.z >= 1]) result = p.solve() self.assertAlmostEqual(result, 9) # Test redundant constraints in cvxopt. def test_redundant_constraints(self): obj = Minimize(sum_entries(self.x)) constraints = [self.x == 2, self.x == 2, self.x.T == 2, self.x[0] == 2] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 4) obj = Minimize(sum_entries(square(self.x))) constraints = [self.x == self.x] p = Problem(obj, constraints) result = p.solve(solver=s.CVXOPT) self.assertAlmostEqual(result, 0) # Test that symmetry is enforced. def test_sdp_symmetry(self): # TODO should these raise exceptions? # with self.assertRaises(Exception) as cm: # lambda_max([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_max called on non-symmetric matrix.") # with self.assertRaises(Exception) as cm: # lambda_min([[1,2],[3,4]]) # self.assertEqual(str(cm.exception), "lambda_min called on non-symmetric matrix.") p = Problem(Minimize(lambda_max(self.A)), [self.A >= 2]) result = p.solve() self.assertItemsAlmostEqual(self.A.value, self.A.value.T) p = Problem(Minimize(lambda_max(self.A)), [self.A == [[1,2],[3,4]]]) result = p.solve() self.assertEqual(p.status, s.INFEASIBLE) # Test SDP def test_sdp(self): # Ensure sdp constraints enforce transpose. obj = Maximize(self.A[1,0] - self.A[0,1]) p = Problem(obj, [lambda_max(self.A) <= 100, self.A[0,0] == 2, self.A[1,1] == 2, self.A[1,0] == 2]) result = p.solve() self.assertAlmostEqual(result, 0) # Test getting values for expressions. def test_expression_values(self): diff_exp = self.x - self.z inf_exp = normInf(diff_exp) sum_entries_exp = 5 + norm1(self.z) + norm1(self.x) + inf_exp constr_exp = norm2(self.x + self.z) obj = norm2(sum_entries_exp) p = Problem(Minimize(obj), [self.x >= [2,3], self.z <= [-1,-4], constr_exp <= 2]) result = p.solve() self.assertAlmostEqual(result, 22) self.assertItemsAlmostEqual(self.x.value, [2,3]) self.assertItemsAlmostEqual(self.z.value, [-1,-4]) # Expression values. self.assertItemsAlmostEqual(diff_exp.value, self.x.value - self.z.value) self.assertAlmostEqual(inf_exp.value, LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(sum_entries_exp.value, 5 + LA.norm(self.z.value, 1) + LA.norm(self.x.value, 1) + \ LA.norm(self.x.value - self.z.value, numpy.inf)) self.assertAlmostEqual(constr_exp.value, LA.norm(self.x.value + self.z.value, 2)) self.assertAlmostEqual(obj.value, result) def test_mult_by_zero(self): """Test multiplication by zero. """ exp = 0*self.a self.assertEqual(exp.value, 0) obj = Minimize(exp) p = Problem(obj) result = p.solve() self.assertAlmostEqual(result, 0) assert self.a.value is not None def test_div(self): """Tests a problem with division. """ obj = Minimize(normInf(self.A/5)) p = Problem(obj, [self.A >= 5]) result = p.solve() self.assertAlmostEqual(result, 1) 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]) def test_invalid_solvers(self): """Tests that errors occur when you use an invalid solver. """ with self.assertRaises(Exception) as cm: Problem(Minimize(-log(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(lambda_max(self.a))).solve(solver=s.ECOS) self.assertEqual(str(cm.exception), "The solver ECOS cannot solve the problem.") with self.assertRaises(Exception) as cm: Problem(Minimize(self.a)).solve(solver=s.SCS) self.assertEqual(str(cm.exception), "The solver SCS cannot solve the problem.") def test_reshape(self): """Tests problems with reshape. """ # Test on scalars. self.assertEqual(reshape(1, 1, 1).value, 1) # Test vector to matrix. x = Variable(4) mat = matrix([[1,-1], [2, -2]]) vec = matrix([1, 2, 3, 4]) vec_mat = matrix([[1, 2], [3, 4]]) expr = reshape(x, 2, 2) obj = Minimize(sum_entries(mat*expr)) prob = Problem(obj, [x == vec]) result = prob.solve() self.assertAlmostEqual(result, sum(mat*vec_mat)) # Test on matrix to vector. c = [1, 2, 3, 4] expr = reshape(self.A, 4, 1) obj = Minimize(expr.T*c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) self.assertItemsAlmostEqual(reshape(expr, 2, 2).value, [-1, -2, 3, 4]) # Test matrix to matrix. expr = reshape(self.C, 2, 3) mat = numpy.matrix([[1,-1], [2, -2]]) C_mat = numpy.matrix([[1, 4], [2, 5], [3, 6]]) obj = Minimize(sum_entries(mat*expr)) prob = Problem(obj, [self.C == C_mat]) result = prob.solve() reshaped = numpy.reshape(C_mat, (2, 3), 'F') self.assertAlmostEqual(result, (mat.dot(reshaped)).sum()) self.assertItemsAlmostEqual(expr.value, C_mat) # Test promoted expressions. c = matrix([[1,-1], [2, -2]]) expr = reshape(c*self.a, 1, 4) obj = Minimize(expr*[1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2*c) expr = reshape(c*self.a, 4, 1) obj = Minimize(expr.T*[1, 2, 3, 4]) prob = Problem(obj, [self.a == 2]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(expr.value, 2*c) def test_vec(self): """Tests problems with vec. """ c = [1, 2, 3, 4] expr = vec(self.A) obj = Minimize(expr.T*c) constraints = [self.A == [[-1, -2], [3, 4]]] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 20) self.assertItemsAlmostEqual(expr.value, [-1, -2, 3, 4]) def test_diag_prob(self): """Test a problem with diag. """ C = Variable(3, 3) obj = Maximize(C[0, 2]) constraints = [diag(C) == 1, C[0, 1] == 0.6, C[1, 2] == -0.3, C == Semidef(3)] prob = Problem(obj, constraints) result = prob.solve() self.assertAlmostEqual(result, 0.583151) def test_presolve_constant_constraints(self): """Test that the presolver removes constraints with no variables. """ x = Variable() obj = Maximize(sqrt(x)) prob = Problem(obj) c, G, h, dims, A, b = prob.get_problem_data(s.ECOS) for row in range(A.shape[0]): assert A[row, :].nnz > 0 for row in range(G.shape[0]): assert G[row, :].nnz > 0
class TestExpressions(unittest.TestCase): """ 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_INTERFACE # 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.size, (2,1)) self.assertEqual(y.size, (1,1)) self.assertEqual(x.curvature, u.Curvature.AFFINE) self.assertEqual(x.canonicalize()[0].size, (2,1)) self.assertEqual(x.canonicalize()[1], []) # Scalar variable coeff = self.a.coefficients(self.intf) self.assertEqual(coeff[self.a], 1) # Vector variable. coeffs = x.coefficients(self.intf) self.assertItemsEqual(coeffs.keys(), [x]) vec = coeffs[x] self.assertEqual(vec.size, (2,2)) self.assertEqual(list(vec), [1,0,0,1]) # Matrix variable. coeffs = self.A.coefficients(self.intf) self.assertItemsEqual(coeffs.keys(), [self.A]) mat = coeffs[self.A] self.assertEqual(mat.size, (2,2)) self.assertEqual(list(mat), [1,0,0,1]) # Test the TransposeVariable class. def test_transpose_variable(self): var = self.a.T self.assertEquals(var.name(), "a") self.assertEquals(var.size, (1,1)) self.a.save_value(2) self.assertEquals(var.value, 2) var = self.x.T self.assertEquals(var.name(), "x.T") self.assertEquals(var.size, (1,2)) self.x.save_value( matrix([1,2]) ) self.assertEquals(var.value[0,0], 1) self.assertEquals(var.value[0,1], 2) var = self.C.T self.assertEquals(var.name(), "C.T") self.assertEquals(var.size, (2,3)) coeffs = var.coefficients(self.intf) self.assertItemsEqual(coeffs.keys(), [var]) mat = coeffs[var] self.assertEqual(mat.size, (2,2)) self.assertEqual(list(mat), [1,0,0,1]) index = var[1,0] self.assertEquals(index.name(), "C[0,1]") self.assertEquals(index.size, (1,1)) var = self.x.T.T self.assertEquals(var.name(), "x") self.assertEquals(var.size, (2,1)) # Test the Constant class. def test_constants(self): c = Constant(2) self.assertEqual(c.name(), str(2)) c = Constant(2, name="c") self.assertEqual(c.name(), "c") self.assertEqual(c.value, 2) self.assertEqual(c.size, (1,1)) self.assertEqual(c.curvature, u.Curvature.CONSTANT) self.assertEqual(c.sign, u.Sign.POSITIVE) self.assertEqual(Constant(-2).sign, u.Sign.NEGATIVE) self.assertEqual(Constant(0).sign, u.Sign.ZERO) self.assertEqual(c.canonicalize()[0].size, (1,1)) self.assertEqual(c.canonicalize()[1], []) coeffs = c.coefficients(self.intf) self.assertEqual(coeffs.keys(), [s.CONSTANT]) self.assertEqual(coeffs[s.CONSTANT], 2) # Test the sign. c = Constant([[2],[2]]) self.assertEqual(c.size, (1,2)) self.assertEqual(c.sign.neg_mat.value.shape, (1,2)) # Test the Parameter class. def test_parameters(self): p = Parameter(name='p') self.assertEqual(p.name(), "p") self.assertEqual(p.size, (1,1)) # Test the AddExpresion class. def test_add_expression(self): # Vectors c = Constant([2,2]) exp = self.x + c self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.sign, u.Sign.UNKNOWN) self.assertEqual(exp.canonicalize()[0].size, (2,1)) self.assertEqual(exp.canonicalize()[1], []) self.assertEqual(exp.name(), self.x.name() + " + " + c.name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp + z + self.x with self.assertRaises(Exception) as cm: (self.x + self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices exp = self.A + self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A + self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Test the SubExpresion class. def test_sub_expression(self): # Vectors c = Constant([2,2]) exp = self.x - c self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.sign, u.Sign.UNKNOWN) self.assertEqual(exp.canonicalize()[0].size, (2,1)) self.assertEqual(exp.canonicalize()[1], []) self.assertEqual(exp.name(), self.x.name() + " - " + Constant([2,2]).name()) self.assertEqual(exp.size, (2,1)) z = Variable(2, name='z') exp = exp - z - self.x with self.assertRaises(Exception) as cm: (self.x - self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices exp = self.A - self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.size, (2,2)) with self.assertRaises(Exception) as cm: (self.A - self.C) self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Test the MulExpresion class. def test_mul_expression(self): # Vectors c = Constant([[2],[2]]) exp = c*self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual((c[0]*self.x).sign, u.Sign.UNKNOWN) self.assertEqual(exp.canonicalize()[0].size, (1,1)) self.assertEqual(exp.canonicalize()[1], []) self.assertEqual(exp.name(), c.name() + " * " + self.x.name()) self.assertEqual(exp.size, (1,1)) with self.assertRaises(Exception) as cm: ([2,2,3]*self.x) const_name = Constant([2,2,3]).name() self.assertEqual(str(cm.exception), "Incompatible dimensions.") # Matrices with self.assertRaises(Exception) as cm: Constant([[2, 1],[2, 2]]) * self.C self.assertEqual(str(cm.exception), "Incompatible dimensions.") with self.assertRaises(Exception) as cm: (self.A * self.B) self.assertEqual(str(cm.exception), "Cannot multiply two non-constants.") # Constant expressions T = Constant([[1,2,3],[3,5,5]]) exp = (T + T) * self.B self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.size, (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.pos_mat.value.shape, (1,2)) # Test the NegExpression class. def test_neg_expression(self): # Vectors exp = -self.x self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.sign, u.Sign.UNKNOWN) self.assertEqual(exp.canonicalize()[0].size, (2,1)) self.assertEqual(exp.canonicalize()[1], []) self.assertEqual(exp.name(), "-%s" % self.x.name()) self.assertEqual(exp.size, self.x.size) # Matrices exp = -self.C self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.size, (3,2)) # Test promotion of scalar constants. def test_scalar_const_promotion(self): # Vectors exp = self.x + 2 self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.sign, u.Sign.UNKNOWN) self.assertEqual(exp.canonicalize()[0].size, (2,1)) self.assertEqual(exp.canonicalize()[1], []) self.assertEqual(exp.name(), self.x.name() + " + " + Constant(2).name()) self.assertEqual(exp.size, (2,1)) self.assertEqual((4 - self.x).size, (2,1)) self.assertEqual((4 * self.x).size, (2,1)) self.assertEqual((4 <= self.x).size, (2,1)) self.assertEqual((4 == self.x).size, (2,1)) self.assertEqual((self.x >= 4).size, (2,1)) # Matrices exp = (self.A + 2) + 4 self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual((3 * self.A).size, (2,2)) self.assertEqual(exp.size, (2,2)) # Test indexing expression. def test_index_expression(self): # Tuple of integers as key. exp = self.x[1,0] self.assertEqual(exp.name(), "x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) coeff = exp.coefficients(self.intf) self.assertEqual(coeff[exp], 1) self.assertEqual(exp.value, None) with self.assertRaises(Exception) as cm: (self.x[2,0]) self.assertEqual(str(cm.exception), "Invalid indices 2,0 for 'x'.") # Slicing exp = self.C[0:2,1] self.assertEquals(exp.name(), "C[0:2,1]") self.assertEquals(exp.size, (2,1)) exp = self.C[0:,0:2] self.assertEquals(exp.name(), "C[0:,0:2]") self.assertEquals(exp.size, (3,2)) exp = self.C[0::2,0::2] self.assertEquals(exp.name(), "C[0::2,0::2]") self.assertEquals(exp.size, (2,1)) exp = self.C[:3,:1:2] self.assertEquals(exp.name(), "C[0:3,0]") self.assertEquals(exp.size, (3,1)) c = Constant([[1,-2],[0,4]]) exp = c[1,1] print exp self.assertEqual(exp.curvature, u.Curvature.CONSTANT) self.assertEqual(exp.sign, u.Sign.POSITIVE) self.assertEqual(c[0,1].sign, u.Sign.ZERO) self.assertEqual(c[1,0].sign, u.Sign.NEGATIVE) self.assertEquals(exp.size, (1,1)) 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, u.Curvature.CONSTANT) self.assertEquals(exp.size, (3,2)) self.assertEqual(exp[0,1].value, 7) # Arithmetic expression indexing exp = (self.x + self.z)[1,0] self.assertEqual(exp.name(), "x[1,0] + z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEqual(exp.sign, u.Sign.UNKNOWN) self.assertEquals(exp.size, (1,1)) exp = (self.x + self.a)[1,0] self.assertEqual(exp.name(), "x[1,0] + a") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.z)[1,0] self.assertEqual(exp.name(), "x[1,0] - z[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) exp = (self.x - self.a)[1,0] self.assertEqual(exp.name(), "x[1,0] - a") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) exp = (-self.x)[1,0] self.assertEqual(exp.name(), "-x[1,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.x)[1,0] self.assertEqual(exp.name(), "[[2], [4]] * x[0:,0]") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1)) c = Constant([[1,2],[3,4]]) exp = (c*self.a)[1,0] self.assertEqual(exp.name(), "2 * a") self.assertEqual(exp.curvature, u.Curvature.AFFINE) self.assertEquals(exp.size, (1,1))
class TestConstraints(unittest.TestCase): """ 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), "LeqConstraint(%s, %s)" % (repr(self.x), repr(self.x))) constr = self.x <= 2 * self.x self.assertEqual( repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2 * self.x))) constr = 2 * self.x >= self.x self.assertEqual( repr(constr), "LeqConstraint(%s, %s)" % (repr(self.x), repr(2 * self.x))) # Test the EqConstraint class. def test_eq_constraint(self): constr = self.x == self.z self.assertEqual(constr.name(), "x == z") self.assertEqual(constr.size, (2, 1)) # self.assertItemsEqual(constr.variables().keys(), [self.x.id, self.z.id]) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None self.x.save_value(2) self.z.save_value(2) assert constr.value self.x.save_value(3) assert not constr.value with self.assertRaises(Exception) as cm: (self.x == self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # Test the LeqConstraint class. def test_leq_constraint(self): constr = self.x <= self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) # Test value and dual_value. assert constr.dual_value is None assert constr.value is None 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]) with self.assertRaises(Exception) as cm: (self.x <= self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_lt(self): """Test the < operator. """ constr = self.x < self.z self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.x < self.y) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_geq(self): """Test the >= operator. """ constr = self.z >= self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y >= self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") def test_gt(self): """Test the > operator. """ constr = self.z > self.x self.assertEqual(constr.name(), "x <= z") self.assertEqual(constr.size, (2, 1)) with self.assertRaises(Exception) as cm: (self.y > self.x) self.assertEqual(str(cm.exception), "Incompatible dimensions (2, 1) (3, 1)") # 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, 1)) def test_chained_constraints(self): """Tests that chaining constraints raises an error. """ with self.assertRaises(Exception) as cm: (self.z <= self.x <= 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.") with self.assertRaises(Exception) as cm: (self.x == self.z == 1) self.assertEqual(str(cm.exception), "Cannot evaluate the truth value of a constraint.")