def test_constant_atoms(atom_info, objective_type) -> None: atom, size, args, obj_val = atom_info for indexer in get_indices(size): for solver in SOLVERS_TO_TRY: # Atoms with Constant arguments. prob_val = obj_val[indexer].value const_args = [Constant(arg) for arg in args] problem = Problem(objective_type(atom(*const_args)[indexer])) run_atom(atom, problem, prob_val, solver) # Atoms with Variable arguments. variables = [] constraints = [] for idx, expr in enumerate(args): variables.append(Variable(intf.shape(expr))) constraints.append(variables[-1] == expr) objective = objective_type(atom(*variables)[indexer]) new_obj_val = prob_val if objective_type == cp.Maximize: objective = -objective new_obj_val = -new_obj_val problem = Problem(objective, constraints) run_atom(atom, problem, new_obj_val, solver) # Atoms with Parameter arguments. parameters = [] for expr in args: parameters.append(Parameter(intf.shape(expr))) parameters[-1].value = intf.DEFAULT_INTF.const_to_matrix(expr) objective = objective_type(atom(*parameters)[indexer]) run_atom(atom, Problem(objective), prob_val, solver)
def _validate_value(self, val): """Check that the value satisfies the leaf's symbolic attributes. Parameters ---------- val : numeric type The value assigned. Returns ------- numeric type The value converted to the proper matrix type. """ if val is not None: # Convert val to ndarray or sparse matrix. val = intf.convert(val) if intf.shape(val) != self.shape: raise ValueError( "Invalid dimensions %s for %s value." % (intf.shape(val), self.__class__.__name__) ) projection = self.project(val) # ^ might be a numpy array, or sparse scipy matrix. delta = np.abs(val - projection) # ^ might be a numpy array, scipy matrix, or sparse scipy matrix. if intf.is_sparse(delta): # is a scipy sparse matrix is_close_enough = np.allclose(delta.data, 0) # ^ only check for near-equality on nonzero values. else: delta = np.array(delta) # make sure we have a numpy array. is_close_enough = np.allclose(delta, 0, atol=1e-8) if not is_close_enough: if self.attributes['nonneg']: attr_str = 'nonnegative' elif self.attributes['nonpos']: attr_str = 'nonpositive' elif self.attributes['diag']: attr_str = 'diagonal' elif self.attributes['PSD']: attr_str = 'positive semidefinite' elif self.attributes['NSD']: attr_str = 'negative semidefinite' elif self.attributes['imag']: attr_str = 'imaginary' else: attr_str = ([k for (k, v) in self.attributes.items() if v] + ['real'])[0] raise ValueError( "%s value must be %s." % (self.__class__.__name__, attr_str) ) return val
def test_numpy_scalars(self) -> None: n = 6 eps = 1e-6 np.random.seed(10) P0 = np.random.randn(n, n) eye = np.eye(n) P0 = P0.T.dot(P0) + eps * eye print(P0) P1 = np.random.randn(n, n) P1 = P1.T.dot(P1) P2 = np.random.randn(n, n) P2 = P2.T.dot(P2) P3 = np.random.randn(n, n) P3 = P3.T.dot(P3) q0 = np.random.randn(n, 1) q1 = np.random.randn(n, 1) q2 = np.random.randn(n, 1) q3 = np.random.randn(n, 1) r0 = np.random.randn(1, 1) r1 = np.random.randn(1, 1) r2 = np.random.randn(1, 1) r3 = np.random.randn(1, 1) slack = cvx.Variable() # Form the problem x = cvx.Variable(n) objective = cvx.Minimize(0.5 * cvx.quad_form(x, P0) + q0.T @ x + r0 + slack) constraints = [ 0.5 * cvx.quad_form(x, P1) + q1.T @ x + r1 <= slack, 0.5 * cvx.quad_form(x, P2) + q2.T @ x + r2 <= slack, 0.5 * cvx.quad_form(x, P3) + q3.T @ x + r3 <= slack, ] # We now find the primal result and compare it to the dual result # to check if strong duality holds i.e. the duality gap is effectively zero p = cvx.Problem(objective, constraints) p.solve(solver=cvx.SCS) # Note that since our data is random, # we may need to run this program multiple times to get a feasible primal # When feasible, we can print out the following values print(x.value) # solution lam1 = constraints[0].dual_value lam2 = constraints[1].dual_value lam3 = constraints[2].dual_value print(type(lam1)) P_lam = P0 + lam1 * P1 + lam2 * P2 + lam3 * P3 q_lam = q0 + lam1 * q1 + lam2 * q2 + lam3 * q3 r_lam = r0 + lam1 * r1 + lam2 * r2 + lam3 * r3 dual_result = -0.5 * q_lam.T.dot(P_lam).dot(q_lam) + r_lam print(dual_result.shape) self.assertEqual(intf.shape(dual_result), (1, 1))
def __init__(self, value): # Keep sparse matrices sparse. if intf.is_sparse(value): self._value = intf.DEFAULT_SPARSE_INTF.const_to_matrix( value, convert_scalars=True) self._sparse = True else: self._value = intf.DEFAULT_INTF.const_to_matrix(value) self._sparse = False self._imag = None self._nonneg = self._nonpos = None self._symm = None self._herm = None self._eigvals = None super(Constant, self).__init__(intf.shape(self.value))
def __init__(self, value) -> None: # Keep sparse matrices sparse. if intf.is_sparse(value): self._value = intf.DEFAULT_SPARSE_INTF.const_to_matrix( value, convert_scalars=True) self._sparse = True else: self._value = intf.DEFAULT_INTF.const_to_matrix(value) self._sparse = False self._imag: Optional[bool] = None self._nonneg: Optional[bool] = None self._nonpos: Optional[bool] = None self._symm: Optional[bool] = None self._herm: Optional[bool] = None self._top_eig: Optional[float] = None self._bottom_eig: Optional[float] = None self._cached_is_pos = None super(Constant, self).__init__(intf.shape(self.value))
def _validate_value(self, val): """Check that the value satisfies the leaf's symbolic attributes. Parameters ---------- val : numeric type The value assigned. Returns ------- numeric type The value converted to the proper matrix type. """ if val is not None: # Convert val to ndarray or sparse matrix. val = intf.convert(val) if intf.shape(val) != self.shape: raise ValueError("Invalid dimensions %s for %s value." % (intf.shape(val), self.__class__.__name__)) projection = self.project(val) # ^ might be a numpy array, or sparse scipy matrix. delta = np.abs(val - projection) # ^ might be a numpy array, scipy matrix, or sparse scipy matrix. if intf.is_sparse(delta): # ^ based on current implementation of project(...), # is is not possible for this Leaf to be PSD/NSD *and* # a sparse matrix. close_enough = np.allclose(delta.data, 0, atol=SPARSE_PROJECTION_TOL) # ^ only check for near-equality on nonzero values. else: # the data could be a scipy matrix, or a numpy array. # First we convert to a numpy array. delta = np.array(delta) # Now that we have the residual, we need to measure it # in some canonical way. if self.attributes['PSD'] or self.attributes['NSD']: # For PSD/NSD Leafs, we use the largest-singular-value norm. close_enough = LA.norm(delta, ord=2) <= PSD_NSD_PROJECTION_TOL else: # For all other Leafs we use the infinity norm on # the vectorized Leaf. close_enough = np.allclose(delta, 0, atol=GENERAL_PROJECTION_TOL) if not close_enough: if self.attributes['nonneg']: attr_str = 'nonnegative' elif self.attributes['pos']: attr_str = 'positive' elif self.attributes['nonpos']: attr_str = 'nonpositive' elif self.attributes['neg']: attr_str = 'negative' elif self.attributes['diag']: attr_str = 'diagonal' elif self.attributes['PSD']: attr_str = 'positive semidefinite' elif self.attributes['NSD']: attr_str = 'negative semidefinite' elif self.attributes['imag']: attr_str = 'imaginary' else: attr_str = ([k for (k, v) in self.attributes.items() if v] + ['real'])[0] raise ValueError("%s value must be %s." % (self.__class__.__name__, attr_str)) return val
def validate_matrix(self, matrix): if self.size != intf.shape(matrix): raise Exception(("The argument's dimensions must match " "the variable's dimensions."))