Source code for quara.interface.cvxpy.qtomography.standard.minimization_algorithm

from typing import List
import time

import numpy as np
import cvxpy as cp

from quara.minimization_algorithm.minimization_algorithm import (
    MinimizationAlgorithm,
    MinimizationAlgorithmOption,
    MinimizationResult,
)
from quara.protocol.qtomography.standard.preprocessing import (
    type_standard_qtomography,
)
from quara.interface.cvxpy.conversion import (
    generate_cvxpy_variable,
    generate_cvxpy_constraints_from_cvxpy_variable,
    generate_cvxpy_constraints_from_cvxpy_variable_with_sparsity,
)
from quara.interface.cvxpy.qtomography.standard.loss_function import (
    CvxpyLossFunction,
    CvxpyLossFunctionOption,
    CvxpyRelativeEntropy,
)


[docs]def get_valid_names_solver() -> List[str]: """returns valid names of solver. Returns ------- List[str] valid names of solver. """ l = ["scs"] l.append("cvxopt") l.append("mosek") return l
[docs]def get_valid_modes_constraints() -> List[str]: """returns valid modes of constraints. Returns ------- List[str] valid modes of constraints. """ l = ["physical"] l.append("unconstraint") return l
[docs]class CvxpyMinimizationResult(MinimizationResult): def __init__( self, variable_value: np.ndarray, loss_value: np.float64 = None, comp_time: float = None, ): """Constructor Parameters ---------- variable_value : np.ndarray the result of the minimization. loss_value : np.float64, optional loss for the minimization, by default None comp_time : float, optional computation time for the minimization, by default None """ super().__init__(value=variable_value, computation_time=comp_time) self._loss_value = loss_value @property def variable_value(self) -> np.ndarray: """returns the result of the minimization. Returns ------- np.ndarray the result of the minimization. """ return self.value @property def loss_value(self) -> np.float64: """returns loss for the minimization. Returns ------- np.float64 loss for the minimization. """ return self._loss_value
[docs]class CvxpyMinimizationAlgorithmOption(MinimizationAlgorithmOption): def __init__( self, name_solver: str, verbose: bool = False, eps_tol: np.float64 = 1e-8, mode_constraint: str = "physical", ): """Constructor Parameters ---------- name_solver : str name of solver verbose : bool, optional verbose option of solver, by default False eps_tol : np.float64, optional eps option of solver, by default 1e-8 mode_constraint : str, optional mode of constraint, by default "physical" Raises ------ ValueError Unsupported name_solver is specified. ValueError eps_tol is negative. ValueError Unsupported mode_constraint is specified. """ if name_solver not in get_valid_names_solver(): raise ValueError( f"Unsupported name_solver is specified. name_solver={name_solver}" ) self._name_solver: str = name_solver self._verbose: bool = verbose if eps_tol < 0: raise ValueError(f"eps_tol must be non-negative. eps_tol={eps_tol}") self._eps_tol: np.float64 = eps_tol if mode_constraint not in get_valid_modes_constraints(): raise ValueError( f"Unsupported mode_constraint is specified. mode_constraint={mode_constraint}" ) self._mode_constraint: str = mode_constraint @property def name_solver(self) -> str: """returns name of solver. Returns ------- str name of solver. """ return self._name_solver @property def verbose(self) -> bool: """returns verbose option of solver. Returns ------- bool verbose option of solver. """ return self._verbose @property def eps_tol(self) -> np.float64: """returns eps option of solver. Returns ------- np.float64 eps option of solver. """ return self._eps_tol @property def mode_constraint(self) -> str: """returns mode of constraint. Returns ------- str mode of constraint. """ return self._mode_constraint
[docs]class CvxpyMinimizationAlgorithm(MinimizationAlgorithm): def __init__(self): super().__init__()
[docs] def is_loss_sufficient(self) -> bool: res = False if self.loss != None and self.loss.on_value: res = True return res
[docs] def is_loss_and_option_sufficient(self) -> bool: res = True if ( type(self.loss) == CvxpyRelativeEntropy and self.option != None and self.option.name_solver == "cvxopt" ): res = False elif ( type(self.loss) == CvxpyRelativeEntropy and self.option != None and self.option.mode_constraint == "unconstraint" ): res = False return res
[docs] def optimize( self, ) -> MinimizationResult: if self.loss == None: raise ValueError(f"loss is not set.") if self.option == None: raise ValueError(f"algorithm option is not set.") time_start = time.time() # CVXPY variable t = self.loss.type_estimate dim = self.loss.dim_system() c_sys = self.loss.composite_system if t == "state" or t == "gate": num_outcomes = None var = generate_cvxpy_variable(t, dim) elif t == "povm" or t == "mprocess": num_outcomes = self.loss.num_outcomes_estimate() var = generate_cvxpy_variable(t, dim, num_outcomes) else: raise ValueError(f"type of estimate is invalid! type of estimate={t}") # CVXPY constraints if self.option.mode_constraint == "unconstraint": constraints = [] elif self.option.mode_constraint == "physical": constraints = generate_cvxpy_constraints_from_cvxpy_variable_with_sparsity( c_sys, t, var, num_outcomes ) else: raise ValueError( f"mode_constraint is invalid! mode_constraint={self.option.mode_constraint}" ) objective = cp.Minimize(self.loss.value_cvxpy(var)) problem = cp.Problem(objective, constraints) # Execute Numerical Optimization name_solver = self.option.name_solver verbose = self.option.verbose eps_tol = self.option.eps_tol if name_solver == "scs": problem.solve(solver=cp.SCS, verbose=verbose, eps=eps_tol) elif name_solver == "mosek": params = {"MSK_DPAR_INTPNT_CO_TOL_DFEAS": eps_tol} problem.solve( solver=cp.MOSEK, verbose=verbose, mosek_params=params, ) elif name_solver == "cvxopt": problem.solve(solver=cp.CVXOPT, verbose=verbose) else: raise ValueError(f"name_solver is invalid! name_solver={name_solver}") time_elapsed = time.time() - time_start result = CvxpyMinimizationResult( variable_value=var.value, loss_value=problem.value, comp_time=time_elapsed ) return result