def test_log_det(self): """Test log det. """ P = np.arange(9) - 2j*np.arange(9) P = np.reshape(P, (3, 3)) P = np.conj(P.T).dot(P)/100 + np.eye(3)*.1 value = cvx.log_det(P).value X = Variable((3, 3), complex=True) prob = Problem(cvx.Maximize(cvx.log_det(X)), [X == P]) result = prob.solve(solver=cvx.SCS, eps=1e-6) self.assertAlmostEqual(result, value, places=2)
def stuffed_objective(self, problem, extractor): # Extract to c.T * x + r C, R = extractor.affine(problem.objective.expr) c = np.asarray(C.todense()).flatten() boolean, integer = extract_mip_idx(problem.variables()) x = Variable(extractor.N, boolean=boolean, integer=integer) new_obj = c.T * x + 0 return new_obj, x, R[0]
def test_max(self) -> None: """Test max. """ # One arg, test sign. self.assertEqual(cp.max(1).sign, s.NONNEG) self.assertEqual(cp.max(-2).sign, s.NONPOS) self.assertEqual(cp.max(Variable()).sign, s.UNKNOWN) self.assertEqual(cp.max(0).sign, s.ZERO) # Test with axis argument. self.assertEqual(cp.max(Variable(2), axis=0, keepdims=True).shape, (1,)) self.assertEqual(cp.max(Variable(2), axis=1).shape, (2,)) self.assertEqual(cp.max(Variable((2, 3)), axis=0, keepdims=True).shape, (1, 3)) self.assertEqual(cp.max(Variable((2, 3)), axis=1).shape, (2,)) # Invalid axis. with self.assertRaises(Exception) as cm: cp.max(self.x, axis=4) self.assertEqual(str(cm.exception), "Invalid argument for axis.")
def matrix_frac_canon(expr, args): X = args[0] # n by m matrix. P = args[1] # n by n matrix. if len(X.shape) == 1: X = reshape(X, (X.shape[0], 1)) n, m = X.shape # Create a matrix with Schur complement T - X.T*P^-1*X. M = Variable((n + m, n + m), PSD=True) T = Variable((m, m)) constraints = [] # Fix M using the fact that P must be affine by the DCP rules. # M[0:n, 0:n] == P. constraints.append(M[0:n, 0:n] == P) # M[0:n, n:n+m] == X constraints.append(M[0:n, n:n + m] == X) # M[n:n+m, n:n+m] == T constraints.append(M[n:n + m, n:n + m] == T) return trace(T), constraints
def exprval_in_vec_eq(expr, vec): # Reference: https://docs.mosek.com/modeling-cookbook/mio.html#fixed-set-of-values assert len(expr.shape) == 1 n_entries = expr.shape[0] repeated_vec = np.broadcast_to(vec, (n_entries, len(vec))) z = Variable(repeated_vec.shape, boolean=True) main_con = cp.sum(cp.multiply(repeated_vec, z), axis=1) == expr aux_cons = [cp.sum(z, axis=1) == 1] return main_con, aux_cons
def test_params(self): """Test with parameters. """ p = cvx.Parameter(imag=True, value=1j) x = Variable(2, complex=True) prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))), [cvx.abs(p * x) <= 2]) result = prob.solve() self.assertAlmostEqual(result, 4 * np.sqrt(2)) val = np.ones(2) * np.sqrt(2) self.assertItemsAlmostEqual(x.value, val + 1j * val)
def test_quadratic_form(self) -> None: x = Variable(5) P = np.eye(5) - 2 * np.ones((5, 5)) q = np.ones((5, 1)) with warnings.catch_warnings(): warnings.simplefilter("ignore") s = x.T @ P @ x + q.T @ x self.assertFalse(s.is_constant()) self.assertFalse(s.is_affine()) self.assertTrue(s.is_quadratic()) self.assertFalse(s.is_dcp())
def sigma_max_canon(expr, args): A = args[0] n, m = A.shape X = Variable((n+m, n+m), PSD=True) shape = expr.shape t = Variable(shape) constraints = [] # Fix X using the fact that A must be affine by the DCP rules. # X[0:n, 0:n] == I_n*t constraints.append(X[0:n, 0:n] == Constant(sp.eye(n)) * t) # X[0:n, n:n+m] == A constraints.append(X[0:n, n:n+m] == A) # X[n:n+m, n:n+m] == I_m*t constraints.append(X[n:n+m, n:n+m] == Constant(sp.eye(m)) * t) return t, constraints
def test_objective(self): """Test objectives. """ x = Variable(complex=True) with self.assertRaises(Exception) as cm: Minimize(x) self.assertEqual(str(cm.exception), "The 'minimize' objective must be real valued.") with self.assertRaises(Exception) as cm: cvx.Maximize(x) self.assertEqual(str(cm.exception), "The 'maximize' objective must be real valued.")
def test_min(self): """Test min. """ # One arg, test sign. self.assertEqual(cp.min(1).sign, s.NONNEG) self.assertEqual(cp.min(-2).sign, s.NONPOS) self.assertEqual(cp.min(Variable()).sign, s.UNKNOWN) self.assertEqual(cp.min(0).sign, s.ZERO) # Test with axis argument. self.assertEqual(cp.min(Variable(2), axis=0).shape, tuple()) self.assertEqual(cp.min(Variable(2), axis=1).shape, (2,)) self.assertEqual(cp.min(Variable((2, 3)), axis=0).shape, (3,)) self.assertEqual(cp.min(Variable((2, 3)), axis=1).shape, (2,)) # Invalid axis. with self.assertRaises(Exception) as cm: cp.min(self.x, axis=4) self.assertEqual(str(cm.exception), "Invalid argument for axis.")
def test_affine(self) -> None: """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 = cp.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 = cp.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 = cp.cumsum(self.x[:, None], axis=1) self.x.value = [1, 2] val = np.eye(2) self.assertItemsAlmostEqual(expr.grad[self.x].toarray(), val)
def stuffed_objective(self, problem, extractor): # extract to x.T * P * x + q.T * x + r # TODO need to copy objective? P, q, r = extractor.quad_form(problem.objective.expr) # concatenate all variables in one vector boolean, integer = extract_mip_idx(problem.variables()) x = Variable(extractor.N, boolean=boolean, integer=integer) new_obj = QuadForm(x, P) + q.T * x return new_obj, x, r
def test_large_square(self): """Test large number of variables squared. """ self.skipTest("Too slow.") for n in [10, 20, 30, 40, 50]: A = np.arange(n * n) A = np.reshape(A, (n, n)) x = Variable((n, n)) p = Problem(Minimize(at.square(x[0, 0])), [x >= A]) result = p.solve() self.assertAlmostEqual(result, 0)
def huber_canon(expr, args): M = expr.M x = args[0] shape = expr.shape n = Variable(shape) s = Variable(shape) # n**2 + 2*M*|s| # TODO(akshayka): Make use of recursion inherent to canonicalization # process and just return a power / abs expressions for readability sake power_expr = power(n, 2) n2, constr_sq = power_canon(power_expr, power_expr.args) abs_expr = abs(s) abs_s, constr_abs = abs_canon(abs_expr, abs_expr.args) obj = n2 + 2 * M * abs_s constraints = constr_sq + constr_abs constraints.append(x == s + n) return obj, constraints
def test_index(self): """Test the copy function for index. """ # Test copy with args=None shape = (5, 4) A = Variable(shape) atom = A[0:2, 0:1] copy = atom.copy() self.assertTrue(type(copy) is type(atom)) # A new object is constructed, so copy.args == atom.args but copy.args # is not atom.args. self.assertEqual(copy.args, atom.args) self.assertFalse(copy.args is atom.args) self.assertEqual(copy.get_data(), atom.get_data()) # Test copy with new args B = Variable((4, 5)) copy = atom.copy(args=[B]) self.assertTrue(type(copy) is type(atom)) self.assertTrue(copy.args[0] is B) self.assertEqual(copy.get_data(), atom.get_data())
def test_sum_largest(self): """Test the sum_largest atom and related atoms. """ with self.assertRaises(Exception) as cm: cp.sum_largest(self.x, -1) self.assertEqual(str(cm.exception), "Second argument must be a positive integer.") with self.assertRaises(Exception) as cm: cp.lambda_sum_largest(self.x, 2.4) self.assertEqual(str(cm.exception), "First argument must be a square matrix.") with self.assertRaises(Exception) as cm: cp.lambda_sum_largest(Variable((2, 2)), 2.4) self.assertEqual(str(cm.exception), "Second argument must be a positive integer.") with self.assertRaises(ValueError) as cm: cp.lambda_sum_largest([[1, 2], [3, 4]], 2).value self.assertEqual(str(cm.exception), "Input matrix was not Hermitian/symmetric.") # Test copy with args=None atom = cp.sum_largest(self.x, 2) copy = atom.copy() self.assertTrue(type(copy) is type(atom)) # A new object is constructed, so copy.args == atom.args but copy.args # is not atom.args. self.assertEqual(copy.args, atom.args) self.assertFalse(copy.args is atom.args) self.assertEqual(copy.get_data(), atom.get_data()) # Test copy with new args copy = atom.copy(args=[self.y]) self.assertTrue(type(copy) is type(atom)) self.assertTrue(copy.args[0] is self.y) self.assertEqual(copy.get_data(), atom.get_data()) # Test copy with lambda_sum_largest, which is in fact an AddExpression atom = cp.lambda_sum_largest(Variable((2, 2)), 2) copy = atom.copy() self.assertTrue(type(copy) is type(atom))
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') # TODO(akshayka): Why are these solvers commented out? If it is # because their interfaces are not yet implemented, then a comment # along those lines should exist. self.solvers = [ECOS()] #, GUROBI(), MOSEK(), SCS(), CVXOPT(), GLPK()]
def test_affine_atoms_canon(self): """Test canonicalization for affine atoms. """ # Scalar. x = Variable() expr = cvx.imag(x + 1j * x) prob = Problem(Minimize(expr), [x >= 0]) result = prob.solve() self.assertAlmostEqual(result, 0) self.assertAlmostEqual(x.value, 0) x = Variable(imag=True) expr = 1j * x prob = Problem(Minimize(expr), [cvx.imag(x) <= 1]) result = prob.solve() self.assertAlmostEqual(result, -1) self.assertAlmostEqual(x.value, 1j) x = Variable(2) expr = x / 1j prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j), [cvx.real(x + 1j) >= 1]) result = prob.solve() self.assertAlmostEqual(result, -np.inf) prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j), [cvx.real(x + 1j) <= 1]) result = prob.solve() self.assertAlmostEqual(result, -2) self.assertItemsAlmostEqual(x.value, [1, 1]) prob = Problem( Minimize(expr[0] * 1j + expr[1] * 1j), [cvx.real(x + 1j) >= 1, cvx.conj(x) <= 0]) result = prob.solve() self.assertAlmostEqual(result, np.inf) x = Variable((2, 2)) y = Variable((3, 2), complex=True) expr = cvx.vstack([x, y]) prob = Problem(Minimize(cvx.sum(cvx.imag(cvx.conj(expr)))), [x == 0, cvx.real(y) == 0, cvx.imag(y) <= 1]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2))) self.assertItemsAlmostEqual(x.value, np.zeros((2, 2))) x = Variable((2, 2)) y = Variable((3, 2), complex=True) expr = cvx.vstack([x, y]) prob = Problem(Minimize(cvx.sum(cvx.imag(expr.H))), [x == 0, cvx.real(y) == 0, cvx.imag(y) <= 1]) result = prob.solve() self.assertAlmostEqual(result, -6) self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2))) self.assertItemsAlmostEqual(x.value, np.zeros((2, 2)))
def test_variable(self): x = Variable(2) y = Variable(2) assert y.name() != x.name() x = Variable(2, name='x') y = Variable() self.assertEqual(x.name(), 'x') self.assertEqual(x.shape, (2,)) self.assertEqual(y.shape, tuple()) self.assertEqual(x.curvature, s.AFFINE) # self.assertEqual(x.canonical_form[0].shape, (2, 1)) # self.assertEqual(x.canonical_form[1], []) self.assertEqual(repr(self.x), "Variable((2,))") self.assertEqual(repr(self.A), "Variable((2, 2))") # # Scalar variable # coeff = self.a.coefficients() # self.assertEqual(coeff[self.a.id], [1]) # # Vector variable. # coeffs = x.coefficients() # self.assertItemsEqual(coeffs.keys(), [x.id]) # vec = coeffs[x.id][0] # self.assertEqual(vec.shape, (2,2)) # self.assertEqual(vec[0,0], 1) # # Matrix variable. # coeffs = self.A.coefficients() # self.assertItemsEqual(coeffs.keys(), [self.A.id]) # self.assertEqual(len(coeffs[self.A.id]), 2) or 0 in self.shape # mat = coeffs[self.A.id][1] # self.assertEqual(mat.shape, (2,4)) # self.assertEqual(mat[0,2], 1) with self.assertRaises(Exception) as cm: p = Variable((2, 2), diag=True, symmetric=True) self.assertEqual(str(cm.exception), "Cannot set more than one special attribute in Variable.") with self.assertRaises(Exception) as cm: p = Variable((2, 0)) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0).") with self.assertRaises(Exception) as cm: p = Variable((2, .5)) self.assertEqual(str(cm.exception), "Invalid dimensions (2, 0.5).") with self.assertRaises(Exception) as cm: p = Variable(2, 1) self.assertEqual(str(cm.exception), "Variable name 1 must be a string.")
def setUp(self): self.a = Variable(name='a') self.x = Variable(2, name='x') self.y = Variable(2, name='y') self.z = Variable(3, 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_large_sum(self): """Test large number of variables summed. """ for n in [10, 20, 30, 40, 50]: A = np.arange(n * n) A = np.reshape(A, (n, n)) x = Variable((n, n)) p = Problem(Minimize(at.sum(x)), [x >= A]) result = p.solve() answer = n * n * (n * n + 1) / 2 - n * n print(result - answer) self.assertAlmostEqual(result, answer)
def _value_impl(self): from cvxpy.problems.problem import Problem from cvxpy.problems.objective import Maximize y_val = self.args[0].value.round(decimals=9).ravel(order='F') x_flat = self._parent.x.flatten() cons = self._parent.constraints if len(cons) == 0: dummy = Variable() cons = [dummy == 1] prob = Problem(Maximize(y_val @ x_flat), cons) val = prob.solve(solver='SCS', eps=1e-6) return val
def stuffed_objective(self, problem, extractor): # extract to 0.5 * x.T * P * x + q.T * x + r expr = problem.objective.expr.copy() params_to_P, params_to_q = extractor.quad_form(expr) # Handle 0.5 factor. params_to_P = 2 * params_to_P # concatenate all variables in one vector boolean, integer = extract_mip_idx(problem.variables()) x = Variable(extractor.x_length, boolean=boolean, integer=integer) return params_to_P, params_to_q, x
def test_sum_smallest(self): """Test the sum_smallest atom and related atoms. """ with self.assertRaises(Exception) as cm: cp.sum_smallest(self.x, -1) self.assertEqual(str(cm.exception), "Second argument must be a positive integer.") with self.assertRaises(Exception) as cm: cp.lambda_sum_smallest(Variable((2, 2)), 2.4) self.assertEqual(str(cm.exception), "Second argument must be a positive integer.")
def test_log_log_curvature(self) -> None: """Test that the curvature string is populated for log-log expressions. """ x = Variable(pos=True) monomial = x * x * x assert monomial.curvature == s.LOG_LOG_AFFINE posynomial = x * x * x + x assert posynomial.curvature == s.LOG_LOG_CONVEX llcv = 1 / (x * x * x + x) assert llcv.curvature == s.LOG_LOG_CONCAVE
def test_vec_to_upper_tri(self): 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 _compute_conic_repr_of_set(self): if len(self.constraints) == 0: dummy = Variable() constrs = [dummy == 1] else: constrs = self.constraints A, b, K = scs_coniclift(self.x, constrs) K_sels = scs_cone_selectors(K) self._A = A self._b = b self._K_sels = K_sels pass
def test_neg_indices(self): """Test negative indices. """ c = Constant([[1, 2], [3, 4]]) exp = c[-1, -1] self.assertEqual(exp.value, 4) self.assertEqual(exp.shape, tuple()) self.assertEqual(exp.curvature, s.CONSTANT) c = Constant([1, 2, 3, 4]) exp = c[1:-1] self.assertItemsAlmostEqual(exp.value, [2, 3]) self.assertEqual(exp.shape, (2, )) self.assertEqual(exp.curvature, s.CONSTANT) c = Constant([1, 2, 3, 4]) exp = c[::-1] self.assertItemsAlmostEqual(exp.value, [4, 3, 2, 1]) self.assertEqual(exp.shape, (4, )) self.assertEqual(exp.curvature, s.CONSTANT) x = Variable(4) self.assertEqual(x[::-1].shape, (4, )) Problem(Minimize(0), [x[::-1] == c]).solve() self.assertItemsAlmostEqual(x.value, [4, 3, 2, 1]) x = Variable(2) self.assertEqual(x[::-1].shape, (2, )) x = Variable(100, name="x") self.assertEqual("x[0:99]", str(x[:-1])) c = Constant([[1, 2], [3, 4]]) expr = c[0, 2:0:-1] self.assertEqual(expr.shape, (1, )) self.assertAlmostEqual(expr.value, 3) expr = c[0, 2::-1] self.assertEqual(expr.shape, (2, )) self.assertItemsAlmostEqual(expr.value, [3, 1])
def stuffed_objective(self, problem, inverse_data): extractor = CoeffExtractor(inverse_data) # Extract to c.T * x, store r C, R = extractor.get_coeffs(problem.objective.expr) c = np.asarray(C.todense()).flatten() boolean, integer = extract_mip_idx(problem.variables()) x = Variable(inverse_data.x_length, boolean=boolean, integer=integer) new_obj = c.T * x + 0 inverse_data.r = R[0] return new_obj, x
def geo_mean_canon(expr, args): x = args[0] w = expr.w shape = expr.shape t = Variable(shape) x_list = [x[i] for i in range(len(w))] # todo: catch cases where we have (0, 0, 1)? # todo: what about curvature case (should be affine) in trivial # case of (0, 0 , 1)? # should this behavior match with what we do in power? return t, gm_constrs(t, x_list, w)