def test_partial_optimize_numeric_fn(self): x, y = Variable(), Variable() xval = 4 # Solve the (simple) two-stage problem by "combining" the two stages # (i.e., by solving a single linear program) p1 = Problem(Minimize(y), [xval + y >= 3]) p1.solve() # Solve the two-stage problem via partial_optimize constr = [y >= -100] p2 = Problem(Minimize(y), [x + y >= 3] + constr) g = partial_optimize(p2, [y], [x]) x.value = xval y.value = 42 constr[0].dual_variables[0].value = 42 result = g.value self.assertAlmostEqual(result, p1.value) self.assertAlmostEqual(y.value, 42) self.assertAlmostEqual(constr[0].dual_value, 42) # No variables optimized over. p2 = Problem(Minimize(y), [x + y >= 3]) g = partial_optimize(p2, [], [x, y]) x.value = xval y.value = 42 p2.constraints[0].dual_variables[0].value = 42 result = g.value self.assertAlmostEqual(result, y.value) self.assertAlmostEqual(y.value, 42) self.assertAlmostEqual(p2.constraints[0].dual_value, 42)
def test_variable(self): """Test the Variable class. """ x = Variable(2, complex=False) y = Variable(2, complex=True) z = Variable(2, imag=True) assert not x.is_complex() assert not x.is_imag() assert y.is_complex() assert not y.is_imag() assert z.is_complex() assert z.is_imag() with self.assertRaises(Exception) as cm: x.value = np.array([1j, 0.]) self.assertEqual(str(cm.exception), "Variable value must be real.") y.value = np.array([1., 0.]) y.value = np.array([1j, 0.]) with self.assertRaises(Exception) as cm: z.value = np.array([1., 0.]) self.assertEqual(str(cm.exception), "Variable value must be imaginary.")
def test_maximum(self) -> None: """Test domain for maximum. """ b = Variable() expr = cp.maximum(self.a, b) self.a.value = 2 b.value = 4 self.assertAlmostEqual(expr.grad[self.a], 0) self.assertAlmostEqual(expr.grad[b], 1) self.a.value = 3 b.value = 0 self.assertAlmostEqual(expr.grad[self.a], 1) self.assertAlmostEqual(expr.grad[b], 0) self.a.value = -1 b.value = 2 self.assertAlmostEqual(expr.grad[self.a], 0) self.assertAlmostEqual(expr.grad[b], 1) y = Variable(2) expr = cp.maximum(self.x, y) self.x.value = [3, 4] y.value = [5, -5] val = np.zeros((2, 2)) + np.diag([0, 1]) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((2, 2)) + np.diag([1, 0]) self.assertItemsAlmostEqual(expr.grad[y].toarray(), val) expr = cp.maximum(self.x, y) self.x.value = [-1e-9, 4] y.value = [1, 4] val = np.zeros((2, 2)) + np.diag([0, 1]) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((2, 2)) + np.diag([1, 0]) self.assertItemsAlmostEqual(expr.grad[y].toarray(), val) expr = cp.maximum(self.A, self.B) self.A.value = [[1, 2], [3, 4]] self.B.value = [[5, 1], [3, 2.3]] val = np.zeros((4, 4)) + np.diag([0, 1, 1, 1]) self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) val = np.zeros((4, 4)) + np.diag([1, 0, 0, 0]) self.assertItemsAlmostEqual(expr.grad[self.B].toarray(), val) # cummax expr = cp.cummax(self.x) self.x.value = [2, 1] val = np.zeros((2, 2)) val[0, 0] = 1 self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) expr = cp.cummax(self.x[:, None], axis=1) self.x.value = [2, 1] val = np.eye(2) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)
def test_log_det(self): """Test gradient for log_det """ expr = log_det(self.A) self.A.value = 2 * np.eye(2) self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), 1.0 / 2 * np.eye(2)) mat = np.array([[1, 2], [3, 5]]) self.A.value = mat.T.dot(mat) val = np.linalg.inv(self.A.value).T self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) self.A.value = np.zeros((2, 2)) self.assertAlmostEqual(expr.grad[self.A], None) self.A.value = -np.array([[1, 2], [3, 4]]) self.assertAlmostEqual(expr.grad[self.A], None) K = Variable((8, 8)) expr = log_det(K[[1, 2]][:, [1, 2]]) K.value = np.eye(8) val = np.zeros((8, 8)) val[[1, 2], [1, 2]] = 1 self.assertItemsAlmostEqual(expr.grad[K].toarray(), val)
def test_nonneg_constraints_end_user(self): x = Variable(shape=(2,), name='x') objective = Minimize(-4 * x[0] - 5 * x[1]) constr_expr = hstack([3 - (2 * x[0] + x[1]), 3 - (x[0] + 2 * x[1]), x[0], x[1]]) constraints = [NonNeg(constr_expr)] expect_dual_var = np.array([1, 2, 0, 0]) con_pairs = [(constraints[0], expect_dual_var)] var_pairs = [(x, np.array([1, 1]))] obj_pair = (objective, -9) # Check that the problem compiles correctly, and that # dual variables are recovered correctly. sth = SolverTestHelper(obj_pair, var_pairs, con_pairs) sth.solve(solver='ECOS') sth.verify_primal_values(places=4) sth.verify_dual_values(places=4) # Check that violations are computed properly expr_val = constr_expr.value # want >= 0 expr_val[expr_val >= 0] = 0 manual_viol = np.linalg.norm(expr_val, ord=2) reported_viol = constraints[0].violation() self.assertAlmostEqual(manual_viol, reported_viol, places=4) # Check that residuals are computed properly x.value = np.array([-1, -2]) expr_val = constraints[0].residual self.assertItemsAlmostEqual( expr_val, # first two constraints are feasible. np.array([0, 0, 1, 2]) ) # Run a second check for violations reported_viol = constraints[0].violation() expected_viol = np.sqrt(5.0) self.assertAlmostEqual(reported_viol, expected_viol)
def test_pownd_constraint(self) -> None: n = 4 W, z = Variable(n), Variable() np.random.seed(0) alpha = 0.5 + np.random.rand(n) alpha /= np.sum(alpha) with self.assertRaises(ValueError): # entries don't sum to one con = PowConeND(W, z, alpha + 0.01) with self.assertRaises(ValueError): # shapes don't match exactly con = PowConeND(W, z, alpha.reshape((n, 1))) with self.assertRaises(ValueError): # wrong axis con = PowConeND(reshape_atom(W, (n, 1)), z, alpha.reshape((n, 1)), axis=1) # Compute a violation con = PowConeND(W, z, alpha) W0 = 0.1 + np.random.rand(n) z0 = np.prod(np.power(W0, alpha)) + 0.05 W.value, z.value = W0, z0 viol = con.violation() self.assertGreaterEqual(viol, 0.01) self.assertLessEqual(viol, 0.06)
def test_broadcast_add(self): """Test addition broadcasting. """ y = Parameter((3, 1)) z = Variable((1, 3)) y.value = np.arange(3)[:, None] z.value = (np.arange(3) - 1)[None, :] expr = y + z self.assertItemsAlmostEqual(expr.value, y.value + z.value) prob = cp.Problem(cp.Minimize(cp.sum(expr)), [z == z.value]) prob.solve() self.assertItemsAlmostEqual(expr.value, y.value + z.value) np.random.seed(0) m, n = 3, 4 A = np.random.rand(m, n) col_scale = Variable(n) with self.assertRaises(ValueError) as cm: A + col_scale self.assertEqual(str(cm.exception), "Cannot broadcast dimensions (3, 4) (4,)") col_scale = Variable([1, n]) C = A + col_scale self.assertEqual(C.shape, (m, n)) row_scale = Variable([m, 1]) R = A + row_scale self.assertEqual(R.shape, (m, n))
def test_broadcast_mul(self) -> None: """Test multiply broadcasting. """ y = Parameter((3, 1)) z = Variable((1, 3)) y.value = np.arange(3)[:, None] z.value = (np.arange(3) - 1)[None, :] expr = cp.multiply(y, z) self.assertItemsAlmostEqual(expr.value, y.value * z.value) prob = cp.Problem(cp.Minimize(cp.sum(expr)), [z == z.value]) prob.solve(solver=cp.SCS) self.assertItemsAlmostEqual(expr.value, y.value * z.value) np.random.seed(0) m, n = 3, 4 A = np.random.rand(m, n) col_scale = Variable(n) with self.assertRaises(ValueError) as cm: cp.multiply(A, col_scale) self.assertEqual(str(cm.exception), "Cannot broadcast dimensions (3, 4) (4,)") col_scale = Variable([1, n]) C = cp.multiply(A, col_scale) self.assertEqual(C.shape, (m, n)) row_scale = Variable([m, 1]) R = cp.multiply(A, row_scale) self.assertEqual(R.shape, (m, n))
def test_minimum(self): """Test domain for minimum. """ b = Variable() expr = minimum(self.a, b) self.a.value = 2 b.value = 4 self.assertAlmostEqual(expr.grad[self.a], 1) self.assertAlmostEqual(expr.grad[b], 0) self.a.value = 3 b.value = 0 self.assertAlmostEqual(expr.grad[self.a], 0) self.assertAlmostEqual(expr.grad[b], 1) self.a.value = -1 b.value = 2 self.assertAlmostEqual(expr.grad[self.a], 1) self.assertAlmostEqual(expr.grad[b], 0) y = Variable(2) expr = minimum(self.x, y) self.x.value = [3, 4] y.value = [5, -5] val = np.zeros((2, 2)) + np.diag([1, 0]) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((2, 2)) + np.diag([0, 1]) self.assertItemsAlmostEqual(expr.grad[y].toarray(), val) expr = minimum(self.x, y) self.x.value = [-1e-9, 4] y.value = [1, 4] val = np.zeros((2, 2)) + np.diag([1, 1]) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((2, 2)) + np.diag([0, 0]) self.assertItemsAlmostEqual(expr.grad[y].toarray(), val) expr = minimum(self.A, self.B) self.A.value = [[1, 2], [3, 4]] self.B.value = [[5, 1], [3, 2.3]] div = (self.A.value / self.B.value).ravel(order='F') val = np.zeros((4, 4)) + np.diag([1, 0, 1, 0]) self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) val = np.zeros((4, 4)) + np.diag([0, 1, 0, 1]) self.assertItemsAlmostEqual(expr.grad[self.B].toarray(), val)
def test_vec_to_upper_tri(self) -> None: from cvxpy.atoms.affine.upper_tri import vec_to_upper_tri x = Variable(shape=(3, )) X = vec_to_upper_tri(x) x.value = np.array([1, 2, 3]) actual = X.value expect = np.array([[1, 2], [0, 3]]) assert np.allclose(actual, expect) y = Variable(shape=(1, )) y.value = np.array([4]) Y = vec_to_upper_tri(y, strict=True) actual = Y.value expect = np.array([[0, 4], [0, 0]]) assert np.allclose(actual, expect) A_expect = np.array([[0, 11, 12, 13], [0, 0, 16, 17], [0, 0, 0, 21], [0, 0, 0, 0]]) a = np.array([11, 12, 13, 16, 17, 21]) A_actual = vec_to_upper_tri(a, strict=True).value assert np.allclose(A_actual, A_expect)
def test_kl_div(self): """Test domain for kl_div. """ b = Variable() expr = kl_div(self.a, b) self.a.value = 2 b.value = 4 self.assertAlmostEqual(expr.grad[self.a], np.log(2 / 4)) self.assertAlmostEqual(expr.grad[b], 1 - (2 / 4)) self.a.value = 3 b.value = 0 self.assertAlmostEqual(expr.grad[self.a], None) self.assertAlmostEqual(expr.grad[b], None) self.a.value = -1 b.value = 2 self.assertAlmostEqual(expr.grad[self.a], None) self.assertAlmostEqual(expr.grad[b], None) y = Variable(2) expr = kl_div(self.x, y) self.x.value = [3, 4] y.value = [5, 8] val = np.zeros((2, 2)) + np.diag(np.log([3, 4]) - np.log([5, 8])) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((2, 2)) + np.diag([1 - 3 / 5, 1 - 4 / 8]) self.assertItemsAlmostEqual(expr.grad[y].toarray(), val) expr = kl_div(self.x, y) self.x.value = [-1e-9, 4] y.value = [1, 2] self.assertAlmostEqual(expr.grad[self.x], None) self.assertAlmostEqual(expr.grad[y], None) expr = kl_div(self.A, self.B) self.A.value = [[1, 2], [3, 4]] self.B.value = [[5, 1], [3.5, 2.3]] div = (self.A.value / self.B.value).ravel(order='F') val = np.zeros((4, 4)) + np.diag(np.log(div)) self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) val = np.zeros((4, 4)) + np.diag(1 - div) self.assertItemsAlmostEqual(expr.grad[self.B].toarray(), val)
def test_affine(self): """Test grad for affine atoms. """ expr = -self.a self.a.value = 2 self.assertAlmostEqual(expr.grad[self.a], -1) expr = 2 * self.a self.a.value = 2 self.assertAlmostEqual(expr.grad[self.a], 2) expr = self.a / 2 self.a.value = 2 self.assertAlmostEqual(expr.grad[self.a], 0.5) expr = -(self.x) self.x.value = [3, 4] val = np.zeros((2, 2)) - np.diag([1, 1]) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) expr = -(self.A) self.A.value = [[1, 2], [3, 4]] val = np.zeros((4, 4)) - np.diag([1, 1, 1, 1]) self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) expr = self.A[0, 1] self.A.value = [[1, 2], [3, 4]] val = np.zeros((4, 1)) val[2] = 1 self.assertItemsAlmostEqual(expr.grad[self.A].toarray(), val) z = Variable(3) expr = hstack([self.x, z]) self.x.value = [1, 2] z.value = [1, 2, 3] val = np.zeros((2, 5)) val[:, 0:2] = np.eye(2) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) val = np.zeros((3, 5)) val[:, 2:] = np.eye(3) self.assertItemsAlmostEqual(expr.grad[z].toarray(), val) # cumsum expr = cumsum(self.x) self.x.value = [1, 2] val = np.ones((2, 2)) val[1, 0] = 0 self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val) expr = cumsum(self.x[:, None], axis=1) self.x.value = [1, 2] val = np.eye(2) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)
def test_assign_var_value(self): """Test assigning a value to a variable. """ # Scalar variable. a = Variable() a.value = 1 self.assertEqual(a.value, 1) with self.assertRaises(Exception) as cm: a.value = [2, 1] self.assertEqual(str(cm.exception), "Invalid dimensions (2,) for Variable value.") # Test assigning None. a.value = 1 a.value = None assert a.value is None # Vector variable. x = Variable(2) x.value = [2, 1] self.assertItemsAlmostEqual(x.value, [2, 1]) # Matrix variable. A = Variable((3, 2)) A.value = np.ones((3, 2)) self.assertItemsAlmostEqual(A.value, np.ones((3, 2))) # Test assigning negative val to nonnegative variable. x = Variable(nonneg=True) with self.assertRaises(Exception) as cm: x.value = -2 self.assertEqual(str(cm.exception), "Variable value must be nonnegative.")
def test_pow3d_constraint(self) -> None: n = 3 np.random.seed(0) alpha = 0.275 x, y, z = Variable(n), Variable(n), Variable(n) con = PowCone3D(x, y, z, alpha) # check violation against feasible values x0, y0 = 0.1 + np.random.rand(n), 0.1 + np.random.rand(n) z0 = x0**alpha * y0**(1 - alpha) z0[1] *= -1 x.value, y.value, z.value = x0, y0, z0 viol = con.residual() self.assertLessEqual(viol, 1e-7) # check violation against infeasible values x1 = x0.copy() x1[0] *= -0.9 x.value = x1 viol = con.residual() self.assertGreaterEqual(viol, 0.99 * abs(x1[0])) # check invalid constraint data with self.assertRaises(ValueError): con = PowCone3D(x, y, z, 1.001) with self.assertRaises(ValueError): con = PowCone3D(x, y, z, -0.00001)