From e9de12355694c7b9cb5a084844fe617e0c933a63 Mon Sep 17 00:00:00 2001 From: Purva Thakre Date: Sat, 28 Oct 2023 12:25:09 -0500 Subject: [PATCH] quick fixes --- .../completely_bounded_trace_norm.py | 2 +- .../fidelity_of_separability.py | 39 +++++++++---------- toqito/channels/partial_trace.py | 9 +++-- toqito/channels/partial_transpose.py | 6 +-- toqito/helper/npa_hierarchy.py | 2 +- toqito/matrix_ops/inner_product.py | 3 +- toqito/matrix_ops/outer_product.py | 3 +- toqito/matrix_ops/vectors_from_gram_matrix.py | 8 +++- toqito/matrix_props/sk_norm.py | 15 +++---- toqito/perms/perfect_matchings.py | 4 +- toqito/random/random_povm.py | 5 +-- .../state_metrics/fidelity_of_separability.py | 6 +-- toqito/state_opt/state_exclusion.py | 11 ++++-- toqito/state_props/is_product.py | 3 +- toqito/state_props/is_separable.py | 16 ++++---- toqito/states/basis.py | 2 +- 16 files changed, 67 insertions(+), 67 deletions(-) diff --git a/toqito/channel_metrics/completely_bounded_trace_norm.py b/toqito/channel_metrics/completely_bounded_trace_norm.py index 642367b95..c583a832d 100644 --- a/toqito/channel_metrics/completely_bounded_trace_norm.py +++ b/toqito/channel_metrics/completely_bounded_trace_norm.py @@ -44,7 +44,7 @@ def completely_bounded_trace_norm(phi: np.ndarray) -> float: if is_quantum_channel(phi): return 1 - elif is_completely_positive(phi): + if is_completely_positive(phi): v = apply_channel(np.eye(dim_ly), dual_channel(phi)) return trace_norm(v) diff --git a/toqito/channel_metrics/fidelity_of_separability.py b/toqito/channel_metrics/fidelity_of_separability.py index 61de99a44..efd48c697 100644 --- a/toqito/channel_metrics/fidelity_of_separability.py +++ b/toqito/channel_metrics/fidelity_of_separability.py @@ -14,7 +14,7 @@ def fidelity_of_separability( - psi: np.ndarray, psi_dims: list[int], k: int = 1, verbosity_option=2, solver_option="cvxopt" + psi: np.ndarray, psi_dims: list[int], k: int = 1, verbosity_option: int=2, solver_option: str="cvxopt" ) -> float: r""" Define the first benchmark introduced in Appendix I of [Phil23.1]_ . @@ -113,28 +113,25 @@ def fidelity_of_separability( “The Theory of Quantum Information” Cambridge University Press, 2018 - Args: - psi: the density matrix for the tripartite state of interest psi_{BAR} - psi_dims: the dimensions of System A, B, & R in + + :param psi: the density matrix for the tripartite state of interest psi_{BAR} + :param psi_dims: the dimensions of System A, B, & R in the input state density matrix. It is assumed that the first quantity in this list is the dimension of System B. - k: value for k-extendibility. - verbosity_option: Parameter option for `picos`. Default value is `verbosity = 2`. - For more info, visit https://picos-api.gitlab.io/picos/api/picos.modeling.options.html#option-verbosity - solver_option: Optimization option for `picos` solver. Default option is `solver_option="cvxopt"` - For more info, visit https://picos-api.gitlab.io/picos/api/picos.modeling.options.html#option-solver - Raises: - AssertionError: - * If the provided dimensions are not for a tripartite density - matrix. - TypeError: - * If the matrix is not a density matrix (square matrix that is \n - PSD with trace 1). - TypeError: - * If the input state is a mixed state. - Returns: - Optimized value of the SDP when maximized over a set of linear - operators subject to some constraints. + :param k: value for k-extendibility. + :param verbosity_option: Parameter option for `picos`. Default value is + `verbosity = 2`. For more info, visit + https://picos-api.gitlab.io/picos/api/picos.modeling.options.html#option-verbosity. + :param solver_option: Optimization option for `picos` solver. Default option is + `solver_option="cvxopt"`. For more info, visit + https://picos-api.gitlab.io/picos/api/picos.modeling.options.html#option-solver. + :raises AssertionError: If the provided dimensions are not for a tripartite density matrix. + :raises ValueError: If the matrix is not a density matrix (square matrix that + is PSD with trace 1). + :raises ValueError: the input state is entangled. + :raises ValueError: the input state is a mixed state. + :return: Optimized value of the SDP when maximized over a set of linear + operators subject to some constraints. """ if not is_density(psi): raise ValueError("Provided input state is not a density matrix.") diff --git a/toqito/channels/partial_trace.py b/toqito/channels/partial_trace.py index 365035bd8..392b12008 100644 --- a/toqito/channels/partial_trace.py +++ b/toqito/channels/partial_trace.py @@ -12,7 +12,7 @@ def partial_trace( input_mat: np.ndarray | Variable, - sys: int | list[int] = [1], + sys: int | list[int] = None, dim: int | list[int] = None, ) -> np.ndarray | Expression: r""" @@ -130,6 +130,9 @@ def partial_trace( equal. :return: The partial trace of matrix :code:`input_mat`. """ + if not isinstance(sys, int): + if sys is None: + sys = [1] # If the input matrix is a CVX variable for an SDP, we convert it to a numpy array, # perform the partial trace, and convert it back to a CVX variable. if isinstance(input_mat, Variable): @@ -145,10 +148,10 @@ def partial_trace( if isinstance(dim, list): dim = np.array(dim) - num_sys = len(dim) + # Allow the user to enter a single number for dim. - if num_sys == 1: + if (num_sys := len(dim)) == 1: dim = np.array([dim[0], len(input_mat) / dim[0]]) if np.abs(dim[1] - np.round(dim[1])) >= 2 * len(input_mat) * np.finfo(float).eps: raise ValueError( diff --git a/toqito/channels/partial_transpose.py b/toqito/channels/partial_transpose.py index 6386f79d7..c3b6e991e 100644 --- a/toqito/channels/partial_transpose.py +++ b/toqito/channels/partial_transpose.py @@ -12,7 +12,7 @@ def partial_transpose( rho: np.ndarray | Variable, - sys: list[int] | np.ndarray | int = [1], + sys: list[int] | np.ndarray | int = 1, dim: list[int] | np.ndarray = None, ) -> np.ndarray | Expression: r"""Compute the partial transpose of a matrix [WikPtrans]_. @@ -135,9 +135,9 @@ def partial_transpose( if isinstance(sys, int): sys = np.array([sys]) - num_sys = max(dim.shape) + # Allow the user to enter a single number for dim. - if num_sys == 1: + if (num_sys := max(dim.shape)) == 1: dim = np.array([dim, list(rho.shape)[0] / dim]) if np.abs(dim[1] - np.round(dim[1]))[0] >= 2 * list(rho.shape)[0] * np.finfo(float).eps: raise ValueError( diff --git a/toqito/helper/npa_hierarchy.py b/toqito/helper/npa_hierarchy.py index 509817549..aa8f43d30 100644 --- a/toqito/helper/npa_hierarchy.py +++ b/toqito/helper/npa_hierarchy.py @@ -124,7 +124,7 @@ def _get_nonlocal_game_params( return a_out, a_in, b_out, b_in -def npa_constraints( +def npa_constraints(# pylint: disable=too-many-locals assemblage: dict[tuple[int, int], cvxpy.Variable], k: int | str = 1, referee_dim: int = 1 ) -> list[cvxpy.constraints.constraint.Constraint]: r""" diff --git a/toqito/matrix_ops/inner_product.py b/toqito/matrix_ops/inner_product.py index 7929e84a7..dde9210ac 100644 --- a/toqito/matrix_ops/inner_product.py +++ b/toqito/matrix_ops/inner_product.py @@ -33,7 +33,8 @@ def inner_product(v1: np.ndarray, v2: np.ndarray) -> float: https://en.wikipedia.org/wiki/Inner_product_space :raises ValueError: Vector dimensions are mismatched. - :param args: v1 and v2, both vectors of dimenstions :math:`(n,1)` where :math:`n>1`. + :param v1: v1 and v2, both vectors of dimenstions :math:`(n,1)` where :math:`n>1`. + :param v2: v1 and v2, both vectors of dimenstions :math:`(n,1)` where :math:`n>1`. :return: The computed inner product. """ # Check for dimensional validity diff --git a/toqito/matrix_ops/outer_product.py b/toqito/matrix_ops/outer_product.py index 65521f0fe..f73554f42 100644 --- a/toqito/matrix_ops/outer_product.py +++ b/toqito/matrix_ops/outer_product.py @@ -35,7 +35,8 @@ def outer_product(v1: np.ndarray, v2: np.ndarray) -> np.ndarray: https://en.wikipedia.org/wiki/Outer_product :raises ValueError: Vector dimensions are mismatched. - :param args: v1 and v2, both vectors of dimensions :math:`(n,1)` where :math:`n>1`. + :param v1: v1 and v2, both vectors of dimenstions :math:`(n,1)` where :math:`n>1`. + :param v2: v1 and v2, both vectors of dimenstions :math:`(n,1)` where :math:`n>1`. :return: The computed outer product. """ # Check for dimensional validity diff --git a/toqito/matrix_ops/vectors_from_gram_matrix.py b/toqito/matrix_ops/vectors_from_gram_matrix.py index 47c7013a5..bf296cb94 100644 --- a/toqito/matrix_ops/vectors_from_gram_matrix.py +++ b/toqito/matrix_ops/vectors_from_gram_matrix.py @@ -3,14 +3,18 @@ def vectors_from_gram_matrix(gram: np.ndarray) -> list[np.ndarray]: - """Obtain the corresponding ensemble of states from the Gram matrix.""" + """Obtain the corresponding ensemble of states from the Gram matrix. + + :param gram: Input Gram matrix. + :return: list of ensemble states + """ dim = gram.shape[0] # If matrix is PD, can do Cholesky decomposition: try: decomp = np.linalg.cholesky(gram) return [decomp[i][:] for i in range(dim)] # Otherwise, need to do eigendecomposition: - except Exception: + except Exception: # pylint: disable=broad-except print("Matrix is not positive semidefinite. Using eigendecomposition as alternative.") d, v = np.linalg.eig(gram) return [np.sqrt(np.diag(d)) @ v[i].conj().T for i in range(dim)] diff --git a/toqito/matrix_props/sk_norm.py b/toqito/matrix_props/sk_norm.py index 074e243ba..909143d3c 100644 --- a/toqito/matrix_props/sk_norm.py +++ b/toqito/matrix_props/sk_norm.py @@ -17,7 +17,7 @@ from toqito.states import max_entangled -def sk_operator_norm( +def sk_operator_norm( # pylint: disable=too-many-locals mat: np.ndarray, k: int = 1, dim: int | list[int] = None, @@ -227,7 +227,7 @@ def sk_operator_norm( return op_norm * lower_bound, op_norm * upper_bound # Start the semidefinite programming approach for getting upper bounds. - if effort >= 1 and ( + if effort >= 1 and (# pylint: disable=too-many-boolean-expressions (lower_bound + tol < upper_bound and is_positive) or (is_positive and is_trans_exact and k == 1) ): @@ -328,7 +328,7 @@ def __target_is_proved( # Schmidt vectors of the other sub-system. This optimization is equivalent to # a generalized eigenvalue problem. The algorithm terminates when an iteration # cannot improve the lower bound by more than tol. -def __lower_bound_sk_norm_randomized( +def __lower_bound_sk_norm_randomized(# pylint: disable=too-many-locals mat: np.ndarray, k: int = 1, dim: int | list[int] = None, @@ -350,8 +350,7 @@ def __lower_bound_sk_norm_randomized( opt_vec = None if start_vec is not None: singular_vals, vt_mat, u_mat = schmidt_decomposition(start_vec, dim) - s_rank = len(singular_vals) - if s_rank > k: + if (s_rank := len(singular_vals)) > k: warnings.warn( f"The Schmidt rank of the initial vector is {s_rank}, which is larger than k={k}. \ Using a randomly-generated intial vector instead." @@ -383,8 +382,7 @@ def __lower_bound_sk_norm_randomized( for p in range(2): # If Schmidt rank is not full, we will have numerical problems; go to # lower Schmidt rank iteration. - s_rank = schmidt_rank(opt_vec, dim) - if s_rank < k: + if (s_rank := schmidt_rank(opt_vec, dim)) < k: return __lower_bound_sk_norm_randomized(mat, s_rank, dim, tol, opt_vec) # Fix one of the parties and optimize over the other party. @@ -399,9 +397,8 @@ def __lower_bound_sk_norm_randomized( largest_eigval, largest_eigvec = scipy.linalg.eigh( a_mat, b=b_mat, subset_by_index=[a_mat.shape[0] - 1, a_mat.shape[0] - 1] ) - new_sk_lower_bound = np.real(largest_eigval[0]) - if new_sk_lower_bound >= sk_lower_bound + tol: + if (new_sk_lower_bound := np.real(largest_eigval[0])) >= sk_lower_bound + tol: it_lower_bound_improved = True sk_lower_bound = new_sk_lower_bound diff --git a/toqito/perms/perfect_matchings.py b/toqito/perms/perfect_matchings.py index 9c6638d7b..ffef0e9a0 100644 --- a/toqito/perms/perfect_matchings.py +++ b/toqito/perms/perfect_matchings.py @@ -37,10 +37,8 @@ def perfect_matchings(num: list[int] | int | np.ndarray) -> np.ndarray: if isinstance(num, list): num = np.array(num) - len_num = len(num) - # Base case, `num = 2`: only one perfect matching. - if len_num == 2: + if (len_num := len(num)) == 2: return num # There are no perfect matchings of an odd number of objects. diff --git a/toqito/random/random_povm.py b/toqito/random/random_povm.py index 85d8a62d3..97a83272d 100644 --- a/toqito/random/random_povm.py +++ b/toqito/random/random_povm.py @@ -49,10 +49,7 @@ def random_povm(dim: int, num_inputs: int, num_outputs: int) -> np.ndarray: povms = [] gram_vectors = np.random.normal(size=(num_inputs, num_outputs, dim, dim)) for input_block in gram_vectors: - normalizer = sum( - [np.array(output_block).T.conj() @ output_block for output_block in input_block] - ) - + normalizer = sum(np.array(output_block).T.conj() @ output_block for output_block in input_block) u_mat, d_mat, _ = np.linalg.svd(normalizer) output_povms = [] diff --git a/toqito/state_metrics/fidelity_of_separability.py b/toqito/state_metrics/fidelity_of_separability.py index 47169f0f9..66e4fb334 100644 --- a/toqito/state_metrics/fidelity_of_separability.py +++ b/toqito/state_metrics/fidelity_of_separability.py @@ -12,12 +12,12 @@ from toqito.state_props import is_pure, is_separable -def fidelity_of_separability(# pylint: disable = missing-type-doc +def fidelity_of_separability( input_state_rho: np.ndarray, input_state_rho_dims: list[int], k: int = 1, - verbosity_option=2, - solver_option="cvxopt", + verbosity_option: int=2, + solver_option: str ="cvxopt", ) -> float: r""" Define the first benchmark introduced in Appendix H of [Phil23]_. diff --git a/toqito/state_opt/state_exclusion.py b/toqito/state_opt/state_exclusion.py index 8f9b865c5..648c6b333 100644 --- a/toqito/state_opt/state_exclusion.py +++ b/toqito/state_opt/state_exclusion.py @@ -6,7 +6,7 @@ def state_exclusion( - vectors: list[np.ndarray], probs: list[float] = None, solver: str = "cvxopt", primal_dual="dual" + vectors: list[np.ndarray], probs: list[float] = None, solver: str = "cvxopt", primal_dual: str ="dual" ) -> tuple[float, list[picos.HermitianVariable]]: r""" Compute probability of single state exclusion. @@ -86,16 +86,19 @@ def state_exclusion( Physical Review A 89.2 (2014): 022336. arXiv:1306.4683 - :param states: A list of states provided as vectors. + :param vectors: A list of states provided as vectors. :param probs: Respective list of probabilities each state is selected. If no probabilities are provided, a uniform probability distribution is assumed. + :param solver: Optimization option for `picos` solver. Default option is + `solver_option="cvxopt"`. + :param primal_dual: Option for the optimization problem :return: The optimal probability with which Bob can guess the state he was not given from `states` along with the optimal set of measurements. """ if primal_dual == "primal": return _min_error_primal(vectors, probs, solver) - else: - return _min_error_dual(vectors, probs, solver) + + return _min_error_dual(vectors, probs, solver) def _min_error_primal(vectors: list[np.ndarray], probs: list[float] = None, solver: str = "cvxopt"): diff --git a/toqito/state_props/is_product.py b/toqito/state_props/is_product.py index 5c9b6f251..0aeb2257d 100644 --- a/toqito/state_props/is_product.py +++ b/toqito/state_props/is_product.py @@ -88,11 +88,10 @@ def _is_product(rho: np.ndarray, dim: int | list[int] = None) -> list[int, bool] # If there are only two subsystems, just use the Schmidt decomposition. if num_sys == 2: singular_vals, u_mat, vt_mat = schmidt_decomposition(rho, dim, 2) - ipv = singular_vals[1] <= np.prod(dim) * np.spacing(singular_vals[0]) # Provide this even if not requested, since it is needed if this # function was called as part of its recursive algorithm (see below) - if ipv: + if (ipv := singular_vals[1] <= np.prod(dim) * np.spacing(singular_vals[0])): u_mat = u_mat * np.sqrt(singular_vals[0]) vt_mat = vt_mat * np.sqrt(singular_vals[0]) dec = [u_mat[:, 0], vt_mat[:, 0]] diff --git a/toqito/state_props/is_separable.py b/toqito/state_props/is_separable.py index b1a439396..2c554390d 100644 --- a/toqito/state_props/is_separable.py +++ b/toqito/state_props/is_separable.py @@ -7,7 +7,7 @@ from toqito.channels import realignment from toqito.matrix_props import is_positive_semidefinite, trace_norm -from toqito.perms import swap + from toqito.state_props import in_separable_ball, is_ppt from toqito.state_props.has_symmetric_extension import has_symmetric_extension @@ -161,10 +161,11 @@ def is_separable( # For the rest of the block-matrix tests, we need the 2-dimensional subsystem to be the # first subsystem, so swap accordingly. - if dim[0] > 2: - Xt = swap(state, [1, 2], dim) - else: - Xt = state + #if dim[0] > 2: + # Xt = swap(state, [1, 2], dim) + #else: + # Xt = state + # commented out because pylint flagged this as an unused variable # Check the proximity of X with the maximally mixed state. if in_separable_ball(state): @@ -182,7 +183,6 @@ def is_separable( return True # The search for symmetric extensions. - for _ in range(2, level): - if has_symmetric_extension(state, level): - return True + if any(has_symmetric_extension(state, level) for _ in range(2, level)): + return True return False diff --git a/toqito/states/basis.py b/toqito/states/basis.py index 4d1c6ada5..43ec6b27d 100644 --- a/toqito/states/basis.py +++ b/toqito/states/basis.py @@ -48,7 +48,7 @@ def basis(dim: int, pos: int) -> np.ndarray: "Invalid: The `pos` variable needs to be less than `dim` for ket function." ) - ret = np.array(list(map(int, list(f"{0:0{dim}}")))) + ret = np.array(list(int(x) for x in list(f"{0:0{dim}}"))) ret[pos] = 1 ret = ret.conj().T.reshape(-1, 1) return ret