Source code for PEPit.psd_matrix

import numpy as np

from PEPit.expression import Expression


[docs] class PSDMatrix(object): """ A :class:`PSDMatrix` encodes a square matrix of :class:`Expression` objects that is constrained to be symmetric PSD. Attributes: name (str): A name set through the set_name method. None is no name is given. matrix_of_expressions (Iterable of Iterable of Expression): a square matrix of :class:`Expression` objects. shape (tuple of ints): the shape of the underlying matrix of :class:`Expression` objects. _value (2D ndarray of floats): numerical values of :class:`Expression` objects obtained after solving the PEP via SDP solver. Set to None before the call to the method `PEP.solve` from the :class:`PEP`. _dual_variable_value (2D ndarray of floats): the associated dual matrix from the numerical solution to the corresponding PEP. Set to None before the call to `PEP.solve` from the :class:`PEP`. entries_dual_variable_value (2D ndarray of floats): the dual of each correspondence between entries of the matrix and the underlying :class:`Expression` objects. counter (int): counts the number of :class:`PSDMatrix` objects. Example: >>> # Defining t <= sqrt(expr) for a given expression expr. >>> from PEPit import Expression >>> from PEPit import PSDMatrix >>> expr = Expression() >>> t = Expression() >>> psd_matrix = PSDMatrix(matrix_of_expressions=[[expr, t], [t, 1]]) >>> # The last line means that the matrix [[expr, t], [t, 1]] is constrained to be PSD. >>> # This is equivalent to det([[expr, t], [t, 1]]) >= 0, i.e. expr - t^2 >= 0. """ # Class counter. # It counts the number of generated constraints counter = 0 def __init__(self, matrix_of_expressions, name=None, ): """ :class:`PSDMatrix` objects are instantiated via the following argument. Args: matrix_of_expressions (Iterable of Iterable of Expression): a square matrix of :class:`Expression`. name (str): name of the object. None by default. Can be updated later through the method `set_name`. Instantiating the :class:`PSDMatrix` objects of the first example can be done by Example: >>> import numpy as np >>> from PEPit import Expression >>> from PEPit import PSDMatrix >>> matrix_of_expressions = np.array([Expression() for i in range(4)]).reshape(2, 2) >>> psd_matrix = PSDMatrix(matrix_of_expressions=matrix_of_expressions) Raises: AssertionError: if provided matrix is not a square matrix. TypeError: if provided matrix does not contain only Expressions and / or scalar values. """ # Initialize name of the psd matrix self.name = name # Update the counter self.counter = PSDMatrix.counter PSDMatrix.counter += 1 # Store the underlying matrix of expressions self.matrix_of_expressions = self._store(matrix_of_expressions) self.shape = self.matrix_of_expressions.shape # The value of the underlying expression must be stored in self._value. self._value = None # Moreover, the associated dual variable value must be stored in self._dual_variable_value. self._dual_variable_value = None self.entries_dual_variable_value = None
[docs] def set_name(self, name): """ Assign a name to self for easier identification purpose. Args: name (str): a name to be given to self. """ self.name = name
[docs] def get_name(self): """ Returns (str): the attribute name. """ return self.name
@staticmethod def _store(matrix_of_expressions): """ Store a new matrix of :class:`Expression`\s that we enforce to be positive semi-definite. Args: matrix_of_expressions (Iterable of Iterables of Expressions): a square matrix of :class:`Expression`. Raises: AssertionError: if provided matrix is not a square matrix. TypeError: if provided matrix does not contain only Expressions and / or scalar values. """ # Change iterable into ndarray matrix = np.array(matrix_of_expressions) # Verify constraint is a square matrix size = matrix.shape[0] assert matrix.shape == (size, size) # Transform scalars into Expressions for i in range(size): for j in range(size): # The entry must be an Expression ... if isinstance(matrix[i, j], Expression): pass # ... or a python scalar. If so, store it as an Expression elif isinstance(matrix[i, j], int) or isinstance(matrix[i, j], float): matrix[i, j] = Expression(is_leaf=False, decomposition_dict={1: matrix[i, j]}) # Raise an Exception in any other scenario else: raise TypeError("PSD matrices contains only Expressions and / or scalar values!" "Got {}".format(type(matrix[i, j]))) # Return processed matrix return matrix def __getitem__(self, item): """ Access to an element of the underlying matrix of expressions using subscript of self. Args: item (2 ints): line and column coordinates access to element of the matrix of expressions Returns: self.matrix_of_expression[item] (Expression): the expression placed at `item` in the matrix of expressions. """ return self.matrix_of_expressions[item]
[docs] def eval(self): """ Compute, store and return the value of the underlying matrix of :class:`Expression` objects. Returns: self._value (np.array): The value of the underlying matrix of :class:`Expression` objects after the corresponding PEP was solved numerically. Raises: ValueError("The PEP must be solved to evaluate PSDMatrix!") if the PEP has not been solved yet. """ # If the attribute value is not None, then simply return it. # Otherwise, compute it and return it. if self._value is None: try: self._value = np.array([[expression.eval() for expression in line] for line in self.matrix_of_expressions]) except ValueError: raise ValueError("The PEP must be solved to evaluate PSDMatrix!") # Return the value return self._value
[docs] def eval_dual(self): """ Compute, store and return the value of the dual variable of this :class:`PSDMatrix`. Returns: self._dual_variable_value (ndarray of floats): The value of the dual variable of this :class:`PSDMatrix` after the corresponding PEP was solved numerically. Raises: ValueError("The PEP must be solved to evaluate PSDMatrix dual variables!") if the PEP has not been solved yet. """ # If the attribute _dual_variable_value is not None, then simply return it. # Otherwise, raise a ValueError. if self._dual_variable_value is None: # The PEP would have filled the attribute after solving the problem. raise ValueError("The PEP must be solved to evaluate PSDMatrix dual variables!") return self._dual_variable_value