diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 32a0423556..39aa6f3421 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -1879,6 +1879,12 @@ Trace distance raised when using `cupy` backend. +Hilbert-Schmidt inner product +""""""""""""""""""""""""""""" + +.. autofunction:: qibo.quantum_info.hilbert_schmidt_inner_product + + Hilbert-Schmidt distance """""""""""""""""""""""" diff --git a/src/qibo/backends/__init__.py b/src/qibo/backends/__init__.py index 77aaab3442..136db6e7c5 100644 --- a/src/qibo/backends/__init__.py +++ b/src/qibo/backends/__init__.py @@ -231,8 +231,7 @@ def construct_backend(backend, **kwargs) -> Backend: # pylint: disable=unsupported-membership-test if provider not in e.msg: raise e - raise_error( - MissingBackend, + raise MissingBackend( f"The '{backend}' backends' provider is not available. Check that a Python " f"package named '{provider}' is installed, and it is exposing valid Qibo " "backends.", diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 63b3dc1da3..0429bbfed4 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -666,7 +666,7 @@ def mutual_information( of state :math:`\\rho` is given by .. math:: - I(\\rho}) \\equiv S(\\rho_{A}) + S(\\rho_{B}) - S(\\rho) \\, , + I(\\rho) \\equiv S(\\rho_{A}) + S(\\rho_{B}) - S(\\rho) \\, , where :math:`B` is the remaining qubits that are not in partition :math:`A`, and :math:`S(\\cdot)` is the :func:`qibo.quantum_info.von_neumann_entropy`. diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index b40789831a..6fb1073234 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -134,12 +134,41 @@ def trace_distance(state, target, check_hermitian: bool = False, backend=None): return distance +def hilbert_schmidt_inner_product(operator_A, operator_B, backend=None): + """Calculate the Hilbert-Schmidt inner product between two operators. + + Given two operators :math:`A, \\, B \\in \\mathcal{H}`, the Hilbert-Schmidt + inner product between the two is given by + + .. math:: + \\braket{A, \\, B}_{\\text{HS}} = \\text{tr}\\left(A^{\\dagger} \\, B\\right) \\, . + + Args: + operator_A (ndarray): operator :math:`A`. + operator_B (ndarray): operator :math:`B`. + backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used + in the execution. If ``None``, it uses :class:`qibo.backends.GlobalBackend`. + Defaults to ``None``. + + Returns: + float: Hilbert-Schmidt inner product :math:`\\braket{A, \\, B}_{\\text{HS}}`. + """ + backend = _check_backend(backend) + + inner_product = backend.np.trace(backend.np.conj(operator_A.T) @ operator_B) + + return backend.np.real(inner_product) + + def hilbert_schmidt_distance(state, target, backend=None): - """Hilbert-Schmidt distance between two quantum states: + """Calculate the Hilbert-Schmidt distance between two quantum states: .. math:: - \\langle \\rho \\, , \\, \\sigma \\rangle_{\\text{HS}} = - \\text{tr}\\left((\\rho - \\sigma)^{2}\\right) + \\braket{\\rho - \\sigma, \\, \\rho - \\sigma}_{\\text{HS}} = + \\text{tr}\\left((\\rho - \\sigma)^{2}\\right) \\, , + + where :math:`\\braket{\\cdot, \\, \\cdot}_{\\text{HS}}` is the + :func:`qibo.quantum_info.hilbert_schmidt_inner_product`. Args: state (ndarray): statevector or density matrix. @@ -151,6 +180,11 @@ def hilbert_schmidt_distance(state, target, backend=None): Returns: float: Hilbert-Schmidt distance between ``state`` :math:`\\rho` and ``target`` :math:`\\sigma`. + + References: + 1. P. J. Coles, M. Cerezo, and L. Cincio, *Strong bound between trace distance + and Hilbert-Schmidt distance for low-rank states*, `Phys. Rev. A 100, 022103 + `_ (2019). """ backend = _check_backend(backend) @@ -171,10 +205,9 @@ def hilbert_schmidt_distance(state, target, backend=None): state = backend.np.outer(backend.np.conj(state), state) target = backend.np.outer(backend.np.conj(target), target) - distance = backend.np.real(backend.np.trace((state - target) ** 2)) - distance = float(distance) + difference = state - target - return distance + return hilbert_schmidt_inner_product(difference, difference, backend=backend) def fidelity(state, target, check_hermitian: bool = False, backend=None): diff --git a/src/qibo/quantum_info/superoperator_transformations.py b/src/qibo/quantum_info/superoperator_transformations.py index dc52bc00be..bae06d6402 100644 --- a/src/qibo/quantum_info/superoperator_transformations.py +++ b/src/qibo/quantum_info/superoperator_transformations.py @@ -20,16 +20,18 @@ def vectorization(state, order: str = "row", backend=None): If ``order="row"``, then: .. math:: - |\\rho) = \\sum_{k, l} \\, \\rho_{kl} \\, \\ket{k} \\otimes \\ket{l} + |\\rho) = \\sum_{k, l} \\, \\rho_{kl} \\, \\ket{k} \\otimes \\ket{l} \\, . If ``order="column"``, then: .. math:: - |\\rho) = \\sum_{k, l} \\, \\rho_{kl} \\, \\ket{l} \\otimes \\ket{k} + |\\rho) = \\sum_{k, l} \\, \\rho_{kl} \\, \\ket{l} \\otimes \\ket{k} \\, . + + If ``state`` is a 3-dimensional tensor, it is interpreted as a batch of states. - If ``state`` is a 3-dimensional tensor it is interpreted as a batch of states. Args: - state: statevector, density matrix, an array of statevectors, or an array of density matrices. + state (ndarray): statevector, density matrix, an array of statevectors, + or an array of density matrices. order (str, optional): If ``"row"``, vectorization is performed row-wise. If ``"column"``, vectorization is performed column-wise. If ``"system"``, a block-vectorization is