def numeric(self, values): """Matrix multiplication. """ if self.args[0].shape == () or self.args[1].shape == () or \ intf.is_sparse(values[0]) or intf.is_sparse(values[1]): return values[0] * values[1] else: return np.matmul(values[0], values[1])
def test_cvxopt_sparse(self): interface = intf.get_matrix_interface(cvxopt.spmatrix) # const_to_matrix mat = interface.const_to_matrix([1, 2, 3]) self.assertEquals(interface.size(mat), (3, 1)) # identity mat = interface.identity(4) cmp_mat = interface.const_to_matrix(np.eye(4)) self.assertEquals(interface.size(mat), interface.size(cmp_mat)) assert not mat - cmp_mat assert intf.is_sparse(mat) # scalar_matrix mat = interface.scalar_matrix(2, 4, 3) self.assertEquals(interface.size(mat), (4, 3)) self.assertEquals(interface.index(mat, (1, 2)), 2) # reshape mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]]) mat = interface.reshape(mat, (6, 1)) self.assertEquals(interface.index(mat, (4, 0)), 4) # Test scalars. scalar = interface.scalar_matrix(1, 1, 1) self.assertEquals(type(scalar), cvxopt.spmatrix) scalar = interface.scalar_matrix(1, 1, 3) self.assertEquals(scalar.size, (1, 3)) # index mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]]) self.assertEquals(interface.index(mat, (0, 1)), 3) mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None))) self.assertEquals(list(mat), [2, 4, 4, 6]) # Sign self.sign_for_intf(interface)
def test_cvxopt_sparse(self): interface = intf.get_matrix_interface(cvxopt.spmatrix) # const_to_matrix mat = interface.const_to_matrix([1, 2, 3]) self.assertEquals(interface.size(mat), (3, 1)) # identity mat = interface.identity(4) cmp_mat = interface.const_to_matrix(np.eye(4)) self.assertEquals(interface.size(mat), interface.size(cmp_mat)) assert not mat - cmp_mat assert intf.is_sparse(mat) # scalar_matrix mat = interface.scalar_matrix(2, 4, 3) self.assertEquals(interface.size(mat), (4, 3)) self.assertEquals(interface.index(mat, (1, 2)), 2) # reshape mat = interface.const_to_matrix([[1, 2, 3], [3, 4, 5]]) mat = interface.reshape(mat, (6, 1)) self.assertEquals(interface.index(mat, (4, 0)), 4) mat = interface.const_to_matrix(1, convert_scalars=True) self.assertEquals(type(interface.reshape(mat, (1, 1))), type(mat)) # Test scalars. scalar = interface.scalar_matrix(1, 1, 1) self.assertEquals(type(scalar), cvxopt.spmatrix) scalar = interface.scalar_matrix(1, 1, 3) self.assertEquals(scalar.size, (1, 3)) # index mat = interface.const_to_matrix([[1, 2, 3, 4], [3, 4, 5, 6]]) self.assertEquals(interface.index(mat, (0, 1)), 3) mat = interface.index(mat, (slice(1, 4, 2), slice(0, 2, None))) self.assertEquals(list(mat), [2, 4, 4, 6]) # Sign self.sign_for_intf(interface)
def project(self, val): """Project value onto the attribute set of the leaf. A sensible idiom is ``leaf.value = leaf.project(val)``. Parameters ---------- val : numeric type The value assigned. Returns ------- numeric type The value rounded to the attribute type. """ # Only one attribute can be active at once (besides real, # nonpos/nonneg, and bool/int). if not self.is_complex(): val = np.real(val) if self.attributes['nonpos'] and self.attributes['nonneg']: return 0 * val elif self.attributes['nonpos']: return np.minimum(val, 0.) elif self.attributes['nonneg']: return np.maximum(val, 0.) elif self.attributes['imag']: return np.imag(val) elif self.attributes['complex']: return val.astype(np.complex) elif self.attributes['boolean']: # TODO(akshayka): respect the boolean indices. return np.round(np.clip(val, 0., 1.)) elif self.attributes['integer']: # TODO(akshayka): respect the integer indices. # also, a variable may be integer in some indices and # boolean in others. return np.round(val) elif self.attributes['diag']: if intf.is_sparse(val): val = val.diagonal() else: val = np.diag(val) return sp.diags([val], [0]) elif self.attributes['hermitian']: return (val + np.conj(val).T) / 2 elif any([self.attributes[key] for key in ['symmetric', 'PSD', 'NSD']]): val = (val + val.T) / 2 if self.attributes['symmetric']: return val w, V = LA.eigh(val) if self.attributes['PSD']: w = np.maximum(w, 0) else: # NSD w = np.minimum(w, 0) return V.dot(np.diag(w)).dot(V.T) else: return val
def numeric(self, values): """Sums the entries of value. """ if intf.is_sparse(values[0]): result = np.sum(values[0], axis=self.axis) if not self.keepdims and self.axis is not None: result = result.A.flatten() else: result = np.sum(values[0], axis=self.axis, keepdims=self.keepdims) return result
def __init__(self, value): # Keep sparse matrices sparse. if intf.is_sparse(value): self._value = intf.DEFAULT_SPARSE_INTERFACE.const_to_matrix(value) self._sparse = True else: self._value = intf.DEFAULT_INTERFACE.const_to_matrix(value) self._sparse = False # Set DCP attributes. self.init_dcp_attr()
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 __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): # TODO HACK. # A fix for c.T*x where c is a 1D array. self.is_1D_array = False # Keep sparse matrices sparse. if intf.is_sparse(value): self._value = intf.DEFAULT_SPARSE_INTF.const_to_matrix(value) self._sparse = True else: if isinstance(value, np.ndarray) and len(value.shape) == 1: self.is_1D_array = True self._value = intf.DEFAULT_INTF.const_to_matrix(value) self._sparse = False # Set DCP attributes. self.init_dcp_attr() super(Constant, self).__init__()
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 _process_constr(self, constr, mat_cache, vert_offset): """Extract the coefficients from a constraint. Parameters ---------- constr : LinConstr The linear constraint to process. mat_cache : MatrixCache The cached version of the matrix-vector pair. vert_offset : int The row offset of the constraint. """ V, I, J = mat_cache.coo_tup coeffs = op2mat.get_coefficients(constr.expr) for id_, block in coeffs: vert_start = vert_offset vert_end = vert_start + constr.size[0]*constr.size[1] if id_ is lo.CONSTANT_ID: # Flatten the block. block = self.vec_intf.const_to_matrix(block) block_size = intf.size(block) block = self.vec_intf.reshape( block, (block_size[0]*block_size[1], 1) ) mat_cache.const_vec[vert_start:vert_end, :] += block else: horiz_offset = self.sym_data.var_offsets[id_] if intf.is_scalar(block): block = intf.scalar_value(block) V.append(block) I.append(vert_start) J.append(horiz_offset) else: # Block is a numpy matrix or # scipy CSC sparse matrix. if not intf.is_sparse(block): block = intf.DEFAULT_SPARSE_INTF.const_to_matrix( block ) block = block.tocoo() V.extend(block.data) I.extend(block.row + vert_start) J.extend(block.col + horiz_offset)
def _process_constr(self, constr, mat_cache, vert_offset): """Extract the coefficients from a constraint. Parameters ---------- constr : LinConstr The linear constraint to process. mat_cache : MatrixCache The cached version of the matrix-vector pair. vert_offset : int The row offset of the constraint. """ V, I, J = mat_cache.coo_tup coeffs = op2mat.get_coefficients(constr.expr) for id_, block in coeffs: vert_start = vert_offset vert_end = vert_start + constr.size[0] * constr.size[1] if id_ is lo.CONSTANT_ID: # Flatten the block. block = self.vec_intf.const_to_matrix(block) block_size = intf.size(block) block = self.vec_intf.reshape( block, (block_size[0] * block_size[1], 1)) mat_cache.const_vec[vert_start:vert_end, :] += block else: horiz_offset = self.sym_data.var_offsets[id_] if intf.is_scalar(block): block = intf.scalar_value(block) V.append(block) I.append(vert_start) J.append(horiz_offset) else: # Block is a numpy matrix or # scipy CSC sparse matrix. if not intf.is_sparse(block): block = intf.DEFAULT_SPARSE_INTERFACE.const_to_matrix( block) block = block.tocoo() V.extend(block.data) I.extend(block.row + vert_start) J.extend(block.col + horiz_offset)
def _constr_matrix(self, constraints, var_offsets, x_length, matrix_intf, vec_intf): """Returns a matrix and vector representing a list of constraints. In the matrix, each constraint is given a block of rows. Each variable coefficient is inserted as a block with upper left corner at matrix[variable offset, constraint offset]. The constant term in the constraint is added to the vector. Parameters ---------- constraints : list A list of constraints. var_offsets : dict A dict of variable id to horizontal offset. x_length : int The length of the x vector. matrix_intf : interface The matrix interface to use for creating the constraints matrix. vec_intf : interface The matrix interface to use for creating the constant vector. Returns ------- tuple A (matrix, vector) tuple. """ rows = sum([c.size[0] * c.size[1] for c in constraints]) cols = x_length V, I, J = [], [], [] const_vec = vec_intf.zeros(rows, 1) vert_offset = 0 for constr in constraints: coeffs = op2mat.get_coefficients(constr.expr) for id_, block in coeffs: vert_start = vert_offset vert_end = vert_start + constr.size[0]*constr.size[1] if id_ is lo.CONSTANT_ID: # Flatten the block. block = self._DENSE_INTF.const_to_matrix(block) block_size = intf.size(block) block = self._DENSE_INTF.reshape( block, (block_size[0]*block_size[1], 1) ) const_vec[vert_start:vert_end, :] += block else: horiz_offset = var_offsets[id_] if intf.is_scalar(block): block = intf.scalar_value(block) V.append(block) I.append(vert_start) J.append(horiz_offset) else: # Block is a numpy matrix or # scipy CSC sparse matrix. if not intf.is_sparse(block): block = self._SPARSE_INTF.const_to_matrix(block) block = block.tocoo() V.extend(block.data) I.extend(block.row + vert_start) J.extend(block.col + horiz_offset) vert_offset += constr.size[0]*constr.size[1] # Create the constraints matrix. if len(V) > 0: matrix = sp.coo_matrix((V, (I, J)), (rows, cols)) # Convert the constraints matrix to the correct type. matrix = matrix_intf.const_to_matrix(matrix, convert_scalars=True) else: # Empty matrix. matrix = matrix_intf.zeros(rows, cols) return (matrix, -const_vec)
def _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 _constr_matrix(self, constraints, var_offsets, x_length, matrix_intf, vec_intf): """Returns a matrix and vector representing a list of constraints. In the matrix, each constraint is given a block of rows. Each variable coefficient is inserted as a block with upper left corner at matrix[variable offset, constraint offset]. The constant term in the constraint is added to the vector. Parameters ---------- constraints : list A list of constraints. var_offsets : dict A dict of variable id to horizontal offset. x_length : int The length of the x vector. matrix_intf : interface The matrix interface to use for creating the constraints matrix. vec_intf : interface The matrix interface to use for creating the constant vector. Returns ------- tuple A (matrix, vector) tuple. """ rows = sum([c.size[0] * c.size[1] for c in constraints]) cols = x_length V, I, J = [], [], [] const_vec = vec_intf.zeros(rows, 1) vert_offset = 0 for constr in constraints: coeffs = op2mat.get_coefficients(constr.expr) for id_, size, block in coeffs: vert_start = vert_offset vert_end = vert_start + constr.size[0] * constr.size[1] if id_ is lo.CONSTANT_ID: # Flatten the block. block = self._DENSE_INTF.const_to_matrix(block) block_size = intf.size(block) block = self._DENSE_INTF.reshape( block, (block_size[0] * block_size[1], 1)) const_vec[vert_start:vert_end, :] += block else: horiz_offset = var_offsets[id_] if intf.is_scalar(block): block = intf.scalar_value(block) V.append(block) I.append(vert_start) J.append(horiz_offset) else: # Block is a numpy matrix or # scipy CSC sparse matrix. if not intf.is_sparse(block): block = self._SPARSE_INTF.const_to_matrix(block) block = block.tocoo() V.extend(block.data) I.extend(block.row + vert_start) J.extend(block.col + horiz_offset) vert_offset += constr.size[0] * constr.size[1] # Create the constraints matrix. if len(V) > 0: matrix = sp.coo_matrix((V, (I, J)), (rows, cols)) # Convert the constraints matrix to the correct type. matrix = matrix_intf.const_to_matrix(matrix, convert_scalars=True) else: # Empty matrix. matrix = matrix_intf.zeros(rows, cols) return (matrix, -const_vec)