Source code for PEPit.wrapper

from PEPit.expression import Expression
from PEPit.point import Point
from PEPit.constraint import Constraint
from PEPit.psd_matrix import PSDMatrix


[docs] class Wrapper(object): """ A :class:`Wrapper` object interfaces PEPit with an SDP solver (or modelling language). Warnings: This class must be overwritten by a child class that encodes all particularities of the solver. In particular, the methods send_constraint_to_solver, send_lmi_constraint_to_solver, generate_problem, get_dual_variables, get_primal_variables, eval_constraint_dual_values, solve, prepare_heuristic, and heuristic must be overwritten. Attributes: _list_of_constraints_sent_to_solver (list): list of :class:`Constraint` and :class:`PSDMatrix` objects associated to the PEP. This list does not contain constraints due to internal representation of the problem by the solver. optimal_F (numpy.array): Elements of F after solving. optimal_G (numpy.array): Gram matrix of the PEP after solving. objective (Expression): The objective expression that must be maximized. This is an additional :class:`Expression` created by the PEP to deal with cases where the user wants to maximize a minimum of several expressions. dual_values (list): Optimal dual variables after solving (same ordering as that of _list_of_constraints_sent_to_solver). residual (Iterable of Iterables of floats): The residual of the problem, i.e. the dual variable of the Gram. prob: instance of the problem (whose type depends on the solver). solver_name (str): The name of the solver the wrapper interact with. verbose (int): Level of information details to print (Override the solver verbose parameter). - 0: No verbose at all - 1: PEPit information is printed but not solver's - 2: Both PEPit and solver details are printed """ def __init__(self, verbose=1): """ :class:`Wrapper` object should not be instantiated as such: only children class should. This function initializes all internal variables of the class. Args: verbose (int): Level of information details to print (Override the solver verbose parameter). - 0: No verbose at all - 1: PEPit information is printed but not solver's - 2: Both PEPit and solver details are printed """ # Initialize lists of constraints that are used to solve the SDP. # Those lists should not be updated by hand, only the solve method does update them. self._list_of_constraints_sent_to_solver = list() self.optimal_F = None self.optimal_G = None self.objective = None # PEPit leaf expression self.dual_values = list() self.residual = None self.prob = None self.solver_name = None self.verbose = verbose
[docs] def check_license(self): """ Check that there is a valid available license for the solver. Returns: license (bool): is there a valid license? """ raise NotImplementedError("This method must be overwritten in children classes")
[docs] def send_constraint_to_solver(self, constraint): """ Transfer a PEPit :class:`Constraint` to the solver and add the :class:`Constraint` into the tracking lists. Args: constraint (Constraint): a :class:`Constraint` object to be sent to the solver. Raises: ValueError if the attribute `equality_or_inequality` of the :class:`Constraint` is neither `equality`, nor `inequality`. """ raise NotImplementedError("This method must be overwritten in children classes")
[docs] def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix): """ Transfer a PEPit :class:`PSDMatrix` (LMI constraint) to the solver and add it the tracking lists. Args: psd_counter (int): a counter useful for the verbose mode. psd_matrix (PSDMatrix): a matrix of expressions that is constrained to be PSD. """ raise NotImplementedError("This method must be overwritten in children classes")
[docs] def get_dual_variables(self): """ Output the list of dual variables. Returns: optimal_dual (list): numerical values of the dual variables (same ordering as that of _list_of_constraints_sent_to_solver). dual_residual (ndarray): dual variable corresponding to the main (primal) Gram matrix. """ return self.dual_values, self.residual
[docs] def get_primal_variables(self): """ Output the optimal value of primal variables. Returns: optimal_G (ndarray): numerical Gram matrix of the PEP after solving. optimal_F (ndarray): numerical elements of F after solving. """ return self.optimal_G, self.optimal_F
[docs] def assign_dual_values(self): """ Recover all dual variables and store them in associated :class:`Constraint` and :class:`PSDMatrix` objects. Returns: residual (ndarray): main dual PSD matrix (dual to the PSD constraint on the Gram matrix). Raises: TypeError if the attribute `_list_of_constraints_sent_to_solver` of this object is neither a :class:`Constraint` object, nor a :class:`PSDMatrix` one. """ dual_values, residual = self._recover_dual_values() self.dual_values = dual_values self.residual = residual assert residual.shape == (Point.counter, Point.counter) for constraint_or_psd, dual_value in zip(self._list_of_constraints_sent_to_solver, dual_values[1:]): if isinstance(constraint_or_psd, Constraint): constraint_or_psd._dual_variable_value = dual_value elif isinstance(constraint_or_psd, PSDMatrix): assert dual_value.shape == constraint_or_psd.shape constraint_or_psd._dual_variable_value = dual_value else: raise TypeError("The list of constraints that are sent to CVXPY should contain only" "\'Constraint\' objects of \'PSDMatrix\' objects." "Got {}".format(type(constraint_or_psd))) return residual
def _recover_dual_values(self): """ Post-process the output of the solver and associate each constraint of the list _list_of_constraints_sent_to_solver to their corresponding numerical dual variables. Returns: dual_values (list) residual (np.array) """ # Return dual_values, residual raise NotImplementedError("This method must be overwritten in children classes")
[docs] def generate_problem(self, objective): """ Instantiate an optimization model using the solver format, whose objective corresponds to a PEPit :class:`Expression` object. Args: objective (Expression): the objective function of the PEP (to be maximized). Returns: prob: the PEP in the solver's format. """ raise NotImplementedError("This method must be overwritten in children classes")
[docs] def solve(self, **kwargs): """ Solve the PEP with solver options. Args: kwargs (keywords, optional): solver specific arguments. Returns: status (string): status of the solution / problem. name (string): name of the solver. value (float): value of the performance metric after solving. problem (): solver-specific model of the PEP. """ raise NotImplementedError("This method must be overwritten in children classes")
[docs] def prepare_heuristic(self, wc_value, tol_dimension_reduction): """ Add the constraint that the objective stay close to its actual value before using dimension-reduction heuristics. That is, we constrain .. math:: \\tau \\leqslant \\text{wc value} + \\text{tol dimension reduction} Args: wc_value (float): the optimal value of the original PEP. tol_dimension_reduction (float): tolerance on the objective for finding low-dimensional examples. """ # Add constraint that tau <= wc_value + tol_dimension raise NotImplementedError("This method must be overwritten in children classes")
[docs] def heuristic(self, weight): """ Change the objective of the PEP, specifically for finding low-dimensional examples. We specify a matrix :math:`W` (weight), which will allow minimizing :math:`\\mathrm{Tr}(G\\,W)`. Args: weight (np.array): weights that will be used in the heuristic. """ raise NotImplementedError("This method must be overwritten in children classes")