From 1e14fbff46f96105608c1922e2e48f3ab13ed057 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 31 Oct 2024 13:40:50 +0400 Subject: [PATCH 01/15] `basis` --- src/qibo/quantum_info/basis.py | 36 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/qibo/quantum_info/basis.py b/src/qibo/quantum_info/basis.py index 7efab48547..587cc0508b 100644 --- a/src/qibo/quantum_info/basis.py +++ b/src/qibo/quantum_info/basis.py @@ -18,18 +18,32 @@ def pauli_basis( pauli_order: str = "IXYZ", backend=None, ): - """Creates the ``nqubits``-qubit Pauli basis. + """Create the :math:`n`-qubit Pauli basis. + + For :math:`d = 2^{n}`, the returned Pauli basis is represented by the following array: + + .. math:: + \\mathcal{P} = \\mathcal{N} \\, \\left[ P_{0}, \\, P_{1}, \\, + \\cdots, P_{d^{2} - 1} \\right] \\, , + + where :math:`P_{k}` is the representation of the :math:`k`-th element of the Pauli basis, + and :math:`\\mathcal{N}` is a normalization factor that equals to :math:`1/\\sqrt{d}` if + ``normalize=True``, and :math:`1` otherwise. + If ``vectorize=False``, each :math:`P_{k}` is the :math:`d \\times d` matrix representing + the :math:`k`-th Pauli element. If ``vectorize=True``, then the Paulis are vectorized + according to ``order`` (see :func:`qibo.quantum_info.vectorization`). Args: - nqubits (int): number of qubits. - normalize (bool, optional): If ``True``, normalized basis is returned. - Defaults to False. - vectorize (bool, optional): If ``False``, returns a nested array with + nqubits (int): number of qubits :math:`n`. + normalize (bool, optional): if ``True``, :math:`\\mathcal{N} = 1/\\sqrt{d}`, + and the normalized Pauli basis is returned. Defaults to ``False``. + vectorize (bool, optional): if ``False``, returns a nested array with all Pauli matrices. If ``True``, retuns an array where every - row is a vectorized Pauli matrix. Defaults to ``False``. - sparse (bool, optional): If ``True``, retuns Pauli basis in a sparse + row is a vectorized Pauli matrix according to vectorization ``order``. + Defaults to ``False``. + sparse (bool, optional): if ``True``, retuns Pauli basis in a sparse representation. Defaults to ``False``. - order (str, optional): If ``"row"``, vectorization of Pauli basis is + order (str, optional): if ``"row"``, vectorization of Pauli basis is performed row-wise. If ``"column"``, vectorization is performed column-wise. If ``"system"``, system-wise vectorization is performed. If ``vectorization=False``, then ``order=None`` is @@ -41,9 +55,9 @@ def pauli_basis( the current backend. Defaults to ``None``. Returns: - ndarray or tuple: all Pauli matrices forming the basis. If ``sparse=True`` - and ``vectorize=True``, tuple is composed of an array of non-zero - elements and an array with their row-wise indexes. + ndarray or tuple: All Pauli matrices forming the basis. If ``sparse=True`` + and ``vectorize=True``, tuple is composed of an array of non-zero + elements and an array with their row-wise indexes. """ if nqubits <= 0: From 9f50ac0c9a9ebecaaa9d5c100ea7460df241f394 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 31 Oct 2024 14:05:03 +0400 Subject: [PATCH 02/15] `comp_basis_to_pauli` --- src/qibo/quantum_info/basis.py | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/qibo/quantum_info/basis.py b/src/qibo/quantum_info/basis.py index 587cc0508b..ee4287e7aa 100644 --- a/src/qibo/quantum_info/basis.py +++ b/src/qibo/quantum_info/basis.py @@ -48,7 +48,7 @@ def pauli_basis( column-wise. If ``"system"``, system-wise vectorization is performed. If ``vectorization=False``, then ``order=None`` is forced. Defaults to ``None``. - pauli_order (str, optional): corresponds to the order of 4 single-qubit + pauli_order (str, optional): corresponds to the order of :math:`4` single-qubit Pauli elements. Defaults to ``"IXYZ"``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses @@ -150,42 +150,48 @@ def comp_basis_to_pauli( pauli_order: str = "IXYZ", backend=None, ): - """Unitary matrix :math:`U` that converts operators from the Liouville - representation in the computational basis to the Pauli-Liouville - representation. + """Unitary matrix :math:`U` that converts operators to the Pauli-Liouville representation. - The unitary :math:`U` is given by + For :math:`d = 2^{n}`, the unitary matrix :math:`U` is given by .. math:: U = \\sum_{k = 0}^{d^{2} - 1} \\, |k)(P_{k}| \\,\\, , where :math:`|P_{k})` is the vectorization of the :math:`k`-th - Pauli operator :math:`P_{k}`, and :math:`|k)` is the vectorization - of the :math:`k`-th computational basis element. + Pauli operator :math:`P_{k}`, and :math:`|k)` is the :math:`k`-th + computational basis element in :math:`\\mathbb{C}^{d^{2}}`. For a definition of vectorization, see :func:`qibo.quantum_info.vectorization`. Example: .. code-block:: python - from qibo.quantum_info import random_density_matrix, vectorization, comp_basis_to_pauli + # Imports below are equivalent to the following: + # from qibo.quantum_info.basis import comp_basis_to_pauli + # from qibo.quantum_info.random_ensembles import random_density_matrix + # from qibo.quantum_info.superoperator_transformations import vectorization + from qibo.quantum_info import comp_basis_to_pauli, random_density_matrix, vectorization + nqubits = 2 - d = 2**nqubits - rho = random_density_matrix(d) - U_c2p = comp_basis_to_pauli(nqubits) + dims = 2**nqubits + + U_c2p = comp_basis_to_pauli(nqubits, order="system") + + rho = random_density_matrix(dims, pure=False) + rho_liouville = vectorization(rho, order="system") rho_pauli_liouville = U_c2p @ rho_liouville Args: - nqubits (int): number of qubits. - normalize (bool, optional): If ``True``, converts to the - Pauli basis. Defaults to ``False``. + nqubits (int): number of qubits :math:`n`. + normalize (bool, optional): If ``True``, returns unitary matrix that converts + to the normalized Pauli basis. Defaults to ``False``. sparse (bool, optional): If ``True``, returns unitary matrix in sparse representation. Defaults to ``False``. order (str, optional): If ``"row"``, vectorization of Pauli basis is performed row-wise. If ``"column"``, vectorization is performed column-wise. If ``"system"``, system-wise vectorization is performed. Defaults to ``"row"``. - pauli_order (str, optional): corresponds to the order of 4 single-qubit + pauli_order (str, optional): corresponds to the order of :math:`4` single-qubit Pauli elements. Defaults to ``"IXYZ"``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses @@ -193,8 +199,8 @@ def comp_basis_to_pauli( Returns: ndarray or tuple: Unitary matrix :math:`U`. If ``sparse=True``, - tuple is composed of array of non-zero elements and an - array with their row-wise indexes. + tuple is composed of array of non-zero elements and an + array with their row-wise indexes. """ backend = _check_backend(backend) From 1a2552b77c7373c57fa247996232b0864ad660a4 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 4 Nov 2024 15:58:04 +0400 Subject: [PATCH 03/15] ``entanglement`` --- src/qibo/quantum_info/entanglement.py | 89 ++++++++++++++++----------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 3851739e4c..e8843793e6 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -13,14 +13,18 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): - """Calculates concurrence of a pure bipartite quantum state - :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}` as + """Calculate concurrence of a pure bipartite quantum state. + + For a pure bipartite quantum state + :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, + the concurrence :math:`C(\\rho)` can be calculate as .. math:: - C(\\rho) = \\sqrt{2 \\, (\\text{tr}^{2}(\\rho) - \\text{tr}(\\rho_{A}^{2}))} \\, , + \\operatorname{C}(\\rho) = \\sqrt{2 \\, (\\operatorname{Tr}^{2}(\\rho) - + \\operatorname{Tr}(\\rho_{B}^{2}))} \\, , - where :math:`\\rho_{A} = \\text{tr}_{B}(\\rho)` is the reduced density operator - obtained by tracing out the qubits in the ``bipartition`` :math:`B`. + where :math:`\\rho_{B} = \\operatorname{Tr}_{A}(\\rho)` is the reduced density operator + obtained by tracing out the qubits in the ``bipartition`` :math:`A`. Args: state (ndarray): statevector or density matrix. @@ -76,19 +80,29 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): def entanglement_of_formation( state, bipartition, base: float = 2, check_purity: bool = True, backend=None ): - """Calculates the entanglement of formation :math:`E_{f}` of a pure bipartite - quantum state :math:`\\rho`, which is given by + """Calculate the entanglement of formation of a pure bipartite quantum state. - .. math:: - E_{f} = H([1 - x, x]) \\, , + For a pure bipartite quantumm state + :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, + the entanglement of formation :math:`E_{f}` can be calculated as + function of its :func:`qibo.quantum_info.concurrence`. + Given a random variable :math:`\\chi \\in \\{0, \\, 1\\}` with + a Bernoulli probability distribution :math:`[1 - x(\\rho), \\, x(\\rho)]`, where .. math:: - x = \\frac{1 + \\sqrt{1 - C^{2}(\\rho)}}{2} \\, , + x(\\rho) = \\frac{1 + \\sqrt{1 - \\operatorname{C}^{2}(\\rho)}}{2} \\, , - :math:`C(\\rho)` is the :func:`qibo.quantum_info.concurrence` of :math:`\\rho`, - and :math:`H` is the :func:`qibo.quantum_info.entropies.shannon_entropy`. + then the entanglement of formation :math:`\\operatorname{E}_{f}` of state :math:`\\rho` + is given by + + .. math:: + \\operatorname{E}_{f} = \\operatorname{H}_{2}(\\chi) \\, . + + :math:`\\operatorname{C}(\\rho)` is the :func:`qibo.quantum_info.concurrence` of :math:`\\rho`, + and :math:`\\operatorname{H}_{2}` is the base-:math:`2` + :func:`qibo.quantum_info.shannon_entropy`. Args: state (ndarray): statevector or density matrix. @@ -103,7 +117,7 @@ def entanglement_of_formation( Returns: - float: entanglement of formation of state :math:`\\rho`. + float: Entanglement of formation of state :math:`\\rho`. """ from qibo.quantum_info.entropies import shannon_entropy # pylint: disable=C0415 @@ -121,17 +135,16 @@ def entanglement_of_formation( def negativity(state, bipartition, backend=None): - """Calculates the negativity of a bipartite quantum state. + """Calculate the negativity of a bipartite quantum state. Given a bipartite state :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, the negativity :math:`\\operatorname{Neg}(\\rho)` is given by .. math:: - \\operatorname{Neg}(\\rho) = \\frac{1}{2} \\, - \\left( \\norm{\\rho_{B}}_{1} - 1 \\right) \\, , + \\operatorname{Neg}(\\rho) = \\frac{\\|\\rho_{B}\\|_{1} - 1}{2} \\, , where :math:`\\rho_{B}` is the reduced density matrix after tracing out qubits in - partition :math:`A`, and :math:`\\norm{\\cdot}_{1}` is the Schatten :math:`1`-norm + partition :math:`A`, and :math:`\\|\\cdot\\|_{1}` is the Schatten :math:`1`-norm (also known as nuclear norm or trace norm). Args: @@ -142,7 +155,7 @@ def negativity(state, bipartition, backend=None): Defaults to ``None``. Returns: - float: Negativity :math:`\\operatorname{Neg}(\\rho)` of state :math:`\\rho`. + float: Negativity :math:`\\operatorname{Neg}` of state :math:`\\rho`. """ backend = _check_backend(backend) @@ -156,16 +169,18 @@ def negativity(state, bipartition, backend=None): def entanglement_fidelity( channel, nqubits: int, state=None, check_hermitian: bool = False, backend=None ): - """Entanglement fidelity :math:`F_{\\mathcal{E}}` of a ``channel`` :math:`\\mathcal{E}` - on ``state`` :math:`\\rho` is given by + """Calculate entanglement fidelity of a quantum channel w.r.t. a quantum state. + + Given a quantum ``channel`` :math:`\\mathcal{E}` and a quantum ``state`` + :math:`\\rho`, the entanglement fidelity :math:`F_{\\mathcal{E}}` is given by .. math:: - F_{\\mathcal{E}}(\\rho) = F(\\rho_{f}, \\rho) + \\operatorname{F}_{\\mathcal{E}}(\\rho) = + \\operatorname{F}(\\mathcal{E}(\\rho), \\rho) \\, , - where :math:`F` is the :func:`qibo.quantum_info.fidelity` function for states, - and :math:`\\rho_{f} = \\mathcal{E}_{A} \\otimes I_{B}(\\rho)` - is the state after the channel :math:`\\mathcal{E}` was applied to - partition :math:`A`. + where :math:`\\operatorname{F}` is the state :func:`qibo.quantum_info.fidelity`, + and :math:`\\mathcal{E}(\\rho) \\equiv (\\mathcal{E}_{A} \\otimes I_{B})(\\rho)` + is the state after the channel :math:`\\mathcal{E}` was applied to partition :math:`A`. Args: channel (:class:`qibo.gates.channels.Channel`): quantum channel @@ -234,11 +249,12 @@ def meyer_wallach_entanglement(state, backend=None): """Compute the Meyer-Wallach entanglement :math:`Q` of a ``state``, .. math:: - Q(\\rho) = 2\\left(1 - \\frac{1}{N} \\, \\sum_{k} \\, - \\text{tr}\\left(\\rho_{k}^{2}\\right)\\right) \\, , + \\operatorname{Q}(\\rho) = 2\\left(1 - \\frac{1}{n} \\, \\sum_{k=0}^{n-1} \\, + \\text{Tr}\\left(\\rho_{k}^{2}\\right)\\right) \\, , + + where :math:`\\rho_{k}` is the reduced density matrix of the :math:`k`-th qubit, + and :math:`n` is the total number of qubits in ``state``. - where :math:`\\rho_{k}^{2}` is the reduced density matrix of qubit :math:`k`, - and :math:`N` is the total number of qubits in ``state``. We use the definition of the Meyer-Wallach entanglement as the average purity proposed in `Brennen (2003) `_, which is equivalent to the definition introduced in `Meyer and Wallach (2002) @@ -292,18 +308,19 @@ def meyer_wallach_entanglement(state, backend=None): def entangling_capability(circuit, samples: int, seed=None, backend=None): - """Return the entangling capability :math:`\\text{Ent}` of a parametrized circuit. + """Calculate the entangling capability :math:`\\text{Ent}` of a parametrized circuit. - It is defined as the average Meyer-Wallach entanglement :math:`Q` - (:func:`qibo.quantum_info.meyer_wallach_entanglement`) of the ``circuit``, i.e. + It is defined as the average Meyer-Wallach entanglement :math:`\\operatorname{Q}` + (:func:`qibo.quantum_info.meyer_wallach_entanglement`) of the ``circuit``, *i.e.* .. math:: - \\text{Ent} = \\frac{2}{|\\mathcal{S}|}\\sum_{\\theta_{k} \\in \\mathcal{S}} - \\, Q(\\rho_{k}) \\, , + \\operatorname{Ent}(\\rho) = \\frac{2}{|\\mathcal{S}|} \\, + \\sum_{\\theta_{k} \\in \\mathcal{S}} \\, + \\operatorname{Q}(\\rho(\\theta_{k})) \\, , where :math:`\\mathcal{S}` is the set of sampled circuit parameters, - and :math:`\\rho_{k}` is the state prepared by the circuit with uniformily-sampled - parameters :math:`\\theta_{k}`. + :math:`|\\mathcal{S}|` its cardinality, and :math:`\\rho_{k}` is the + state prepared by the circuit with uniformily-sampled parameters :math:`\\theta_{k}`. .. note:: Currently, function does not work with ``circuit`` that contains noisy channels. From f9a58e5babffc6db1b3bbc9857c6e58cde613d8c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Tue, 5 Nov 2024 16:46:30 +0400 Subject: [PATCH 04/15] entropies --- src/qibo/quantum_info/entropies.py | 97 +++++++++++++++++++----------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 48767896d0..2620ef01b3 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -11,24 +11,31 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): - """Calculate the Shannon entropy of a probability array :math:`\\mathbf{p}`, which is given by + """Calculate the Shannon entropy of a discrete random variable. + + + For a discrete random variable :math:`\\chi` that has values :math:`x` in the set + :math:`\\mathcal{X}` with probability distribution :math:`\\operatorname{p}(x)`, + the base-:math:`b` Shannon entropy is defined as .. math:: - H(\\mathbf{p}) = - \\sum_{k = 0}^{d^{2} - 1} \\, p_{k} \\, \\log_{b}(p_{k}) \\, , + \\operatorname{H}_{b}(\\chi) = - \\sum_{x \\in \\mathcal{X}} + \\, \\operatorname{p}(x) \\, \\log_{b}(\\operatorname{p}(x)) \\, , where :math:`d = \\text{dim}(\\mathcal{H})` is the dimension of the - Hilbert space :math:`\\mathcal{H}`, :math:`b` is the log base (default 2), - and :math:`0 \\log_{b}(0) \\equiv 0`. + Hilbert space :math:`\\mathcal{H}`, :math:`b` is the log base, + and :math:`0 \\log_{b}(0) \\equiv 0, \\,\\, \\forall \\, b`. Args: - prob_dist (ndarray or list): a probability array :math:`\\mathbf{p}`. + prob_dist (ndarray or list): probability array + :math:`\\{\\operatorname{p(x)}\\}_{x \\in \\mathcal{X}}`. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Shannon entropy :math:`H(\\mathcal{p})`. + float: Shannon entropy :math:`\\operatorname{H}_{b}(\\chi)`. """ backend = _check_backend(backend) @@ -72,27 +79,34 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backend=None): - """Calculates the relative entropy between two discrete probability distributions. + """Calculate the relative entropy between two discrete random variables. - For probabilities :math:`\\mathbf{p}` and :math:`\\mathbf{q}`, it is defined as + Given two random variables, :math:`\\chi` and :math:`\\upsilon`, + that admit values :math:`x` in the set :math:`\\mathcal{X}` with respective probabilities + :math:`\\operatorname{p}(x)` and :math:`\\operatorname{q}(x)`, then their base-:math:`b` + relative entropy is given by .. math:: - D(\\mathbf{p} \\, \\| \\, \\mathbf{q}) = \\sum_{x} \\, \\mathbf{p}(x) \\, - \\log\\left( \\frac{\\mathbf{p}(x)}{\\mathbf{q}(x)} \\right) \\, . + \\operatorname{D}_{b}(\\chi \\, \\| \\, \\upsilon) = + \\sum_{x \\in \\mathcal{X}} \\, \\operatorname{p}(x) \\, + \\log_{b}\\left( \\frac{\\operatorname{p}(x)}{\\operatorname{q}(x)} \\right) \\, . The classical relative entropy is also known as the - `Kullback-Leibler (KL) divergence `_. + `Kullback-Leibler (KL) divergence + `_. Args: - prob_dist_p (ndarray or list): discrete probability distribution :math:`p`. - prob_dist_q (ndarray or list): discrete probability distribution :math:`q`. + prob_dist_p (ndarray or list): discrete probability + :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. + prob_dist_q (ndarray or list): discrete probability + :math:`\\{\\operatorname{q}(x)\\}_{x\\in\\mathcal{X}}`. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Classical relative entropy between :math:`\\mathbf{p}` and :math:`\\mathbf{q}`. + float: Classical relative entropy between :math:`\\chi` and :math:`\\upsilon`. """ backend = _check_backend(backend) prob_dist_p = backend.cast(prob_dist_p, dtype=np.float64) @@ -144,29 +158,35 @@ def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backen def classical_mutual_information( prob_dist_joint, prob_dist_p, prob_dist_q, base: float = 2, backend=None ): - """Calculates the classical mutual information of two random variables. + """Calculate the classical mutual information of two random variables. - Given two random variables :math:`(X, \\, Y)`, their mutual information is given by + Let :math:`\\chi` and :math:`\\upsilon` be two discrete random variables that + have values :math:`x \\in \\mathcal{X}` and :math:`y \\in \\mathcal{Y}`, respectively. + Then, their mutual information is given by .. math:: - I(X, \\, Y) \\equiv H(p(x)) + H(q(y)) - H(p(x, \\, y)) \\, , + \\operatorname{I}_{b}(\\chi, \\, \\upsilon) = \\operatorname{H}_{b}(\\chi) + + \\operatorname{H}_{b}(\\upsilon) + - \\operatorname{H}_{b}(\\chi, \\, \\upsilon) \\, , - where :math:`p(x, \\, y)` is the joint probability distribution of :math:`(X, Y)`, - :math:`p(x)` is the marginal probability distribution of :math:`X`, - :math:`q(y)` is the marginal probability distribution of :math:`Y`, - and :math:`H(\\cdot)` is the :func:`qibo.quantum_info.entropies.shannon_entropy`. + where :math:`\\operatorname{H}_{b}(\\cdot)` is the :func:`qibo.quantum_info.shannon_entropy`, + and :math:`\\operatorname{H}_{b}(\\chi, \\, \\upsilon)` represents the joint Shannon entropy + of the two random variables. Args: - prob_dist_joint (ndarray): joint probability distribution :math:`p(x, \\, y)`. - prob_dist_p (ndarray): marginal probability distribution :math:`p(x)`. - prob_dist_q (ndarray): marginal probability distribution :math:`q(y)`. + prob_dist_joint (ndarray): joint probability + :math:`\\{\\operatorname{p}(x, \\, y)\\}_{x\\in\\mathcal{X},y\\in\\mathcal{Y}}`. + prob_dist_p (ndarray): marginal probability + :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. + prob_dist_q (ndarray): marginal probability distribution + :math:`\\{\\operatorname{q}(y)\\}_{y\\in\\mathcal{Y}}`. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Mutual information :math:`I(X, \\, Y)`. + float: Mutual information :math:`\\operatorname{I}(X, \\, Y)`. """ return ( shannon_entropy(prob_dist_p, base, backend) @@ -178,30 +198,35 @@ def classical_mutual_information( def classical_renyi_entropy( prob_dist, alpha: Union[float, int], base: float = 2, backend=None ): - """Calculates the classical Rényi entropy :math:`H_{\\alpha}` of a discrete probability distribution. + """Calculate the Rényi entropy of a discrete random variable. - For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)` and probability distribution - :math:`\\mathbf{p}`, the classical Rényi entropy is defined as + Let :math:`\\chi` be a discrete random variable that has values :math:`x` + in the set :math:`\\mathcal{X}` with probability :math:`\\operatorname{p}(x)`. + For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)`, + the (classical) base-:math:`b` Rényi entropy of :math:`\\chi` is defined as .. math:: - H_{\\alpha}(\\mathbf{p}) = \\frac{1}{1 - \\alpha} \\, \\log\\left( \\sum_{x} - \\, \\mathbf{p}^{\\alpha}(x) \\right) \\, . + \\operatorname{H}_{\\alpha}^{\\text{re}}(\\chi) = \\frac{1}{1 - \\alpha} \\, + \\log_{b}\\left( \\sum_{x} \\, \\operatorname{p}^{\\alpha}(x) \\right) \\, , + + where :math:`\\|\\cdot\\|_{\\alpha}` is the vector :math:`\\alpha`-norm. A special case is the limit :math:`\\alpha \\to 1`, in which the classical Rényi entropy - coincides with the :func:`qibo.quantum_info.entropies.shannon_entropy`. + coincides with the :func:`qibo.quantum_info.shannon_entropy`. Another special case is the limit :math:`\\alpha \\to 0`, where the function is - reduced to :math:`\\log\\left(|\\mathbf{p}|\\right)`, with :math:`|\\mathbf{p}|` - being the support of :math:`\\mathbf{p}`. + reduced to :math:`\\log_{b}\\left(|\\operatorname{p}|\\right)`, with :math:`|\\operatorname{p}|` + being the support of :math:`\\operatorname{p}`. This is known as the `Hartley entropy `_ (also known as *Hartley function* or *max-entropy*). In the limit :math:`\\alpha \\to \\infty`, the function reduces to - :math:`-\\log(\\max_{x}(\\mathbf{p}(x)))`, which is called the + :math:`-\\log_{b}(\\max_{x}(\\operatorname{p}(x)))`, which is called the `min-entropy `_. Args: - prob_dist (ndarray): discrete probability distribution. + prob_dist (ndarray): discrete probability + :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. alpha (float or int): order of the Rényi entropy. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be @@ -209,7 +234,7 @@ def classical_renyi_entropy( the current backend. Defaults to ``None``. Returns: - float: Classical Rényi entropy :math:`H_{\\alpha}`. + float: Classical Rényi entropy :math:`\\operatorname{H}_{\\alpha}^{\\text{re}}`. """ backend = _check_backend(backend) prob_dist = backend.cast(prob_dist, dtype=np.float64) From 75030d6eba28d67d229e62330b49a5eaf962b409 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 10:44:21 +0400 Subject: [PATCH 05/15] entropies --- src/qibo/quantum_info/entropies.py | 53 +++++++++++++++++------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 2620ef01b3..76156f4d47 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -79,7 +79,7 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backend=None): - """Calculate the relative entropy between two discrete random variables. + """Calculate the (classical) relative entropy between two discrete random variables. Given two random variables, :math:`\\chi` and :math:`\\upsilon`, that admit values :math:`x` in the set :math:`\\mathcal{X}` with respective probabilities @@ -158,11 +158,11 @@ def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backen def classical_mutual_information( prob_dist_joint, prob_dist_p, prob_dist_q, base: float = 2, backend=None ): - """Calculate the classical mutual information of two random variables. + """Calculate the (classical) mutual information between two random variables. Let :math:`\\chi` and :math:`\\upsilon` be two discrete random variables that have values :math:`x \\in \\mathcal{X}` and :math:`y \\in \\mathcal{Y}`, respectively. - Then, their mutual information is given by + Then, their base-:math:`b` mutual information is given by .. math:: \\operatorname{I}_{b}(\\chi, \\, \\upsilon) = \\operatorname{H}_{b}(\\chi) @@ -174,11 +174,11 @@ def classical_mutual_information( of the two random variables. Args: - prob_dist_joint (ndarray): joint probability + prob_dist_joint (ndarray): joint discrete probability :math:`\\{\\operatorname{p}(x, \\, y)\\}_{x\\in\\mathcal{X},y\\in\\mathcal{Y}}`. - prob_dist_p (ndarray): marginal probability + prob_dist_p (ndarray): marginal discrete probability :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. - prob_dist_q (ndarray): marginal probability distribution + prob_dist_q (ndarray): marginal discrete probability :math:`\\{\\operatorname{q}(y)\\}_{y\\in\\mathcal{Y}}`. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -198,7 +198,7 @@ def classical_mutual_information( def classical_renyi_entropy( prob_dist, alpha: Union[float, int], base: float = 2, backend=None ): - """Calculate the Rényi entropy of a discrete random variable. + """Calculate the (classical) Rényi entropy of a discrete random variable. Let :math:`\\chi` be a discrete random variable that has values :math:`x` in the set :math:`\\mathcal{X}` with probability :math:`\\operatorname{p}(x)`. @@ -289,33 +289,40 @@ def classical_renyi_entropy( def classical_relative_renyi_entropy( prob_dist_p, prob_dist_q, alpha: Union[float, int], base: float = 2, backend=None ): - """Calculates the classical relative Rényi entropy between two discrete probability distributions. + """Calculate the (classical) relative Rényi entropy between two discrete random variables. This function is also known as `Rényi divergence `_. - For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)` and probability distributions - :math:`\\mathbf{p}` and :math:`\\mathbf{q}`, the classical relative Rényi entropy is defined as + Let :math:`\\chi` and :math:`\\upsilon` be two discrete random variables + that admit values :math:`x` in the set :math:`\\mathcal{X}` with respective probabilities + :math:`\\operatorname{p}(x)` and :math:`\\operatorname{q}(x)`. + For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)`, the (classical) relative + Rényi entropy is defined as .. math:: - H_{\\alpha}(\\mathbf{p} \\, \\| \\, \\mathbf{q}) = \\frac{1}{\\alpha - 1} \\, - \\log\\left( \\sum_{x} \\, \\frac{\\mathbf{p}^{\\alpha}(x)} - {\\mathbf{q}^{\\alpha - 1}(x)} \\right) \\, . + \\operatorname{D}_{\\alpha,b}^{\\text{re}}(\\chi \\, \\| \\, \\upsilon) = + \\frac{1}{\\alpha - 1} \\, \\log_{b}\\left( \\sum_{x} \\, + \\frac{\\operatorname{p}^{\\alpha}(x)}{\\operatorname{q}^{\\alpha - 1}(x)} \\right) + \\, . A special case is the limit :math:`\\alpha \\to 1`, in which the classical Rényi divergence - coincides with the :func:`qibo.quantum_info.entropies.classical_relative_entropy`. + coincides with the :func:`qibo.quantum_info.classical_relative_entropy`. Another special case is the limit :math:`\\alpha \\to 1/2`, where the function is - reduced to :math:`-2 \\log\\left(\\sum_{x} \\, \\sqrt{\\mathbf{p}(x) \\, \\mathbf{q}(x)} \\right)`. - The sum inside the :math:`\\log` is known as the + reduced to :math:`-2 \\log_{b}\\left(\\sum_{x\\in\\mathcal{X}} \\, + \\sqrt{\\operatorname{p}(x) \\, \\operatorname{q}(x)} \\right)`. + The sum inside the :math:`\\log_{b}` is known as the `Bhattacharyya coefficient `_. In the limit :math:`\\alpha \\to \\infty`, the function reduces to - :math:`\\log(\\max_{x}(\\mathbf{p}(x) \\, \\mathbf{q}(x))`. + :math:`\\log_{b}\\,\\max_{x\\in\\mathcal{X}}\\,(\\operatorname{p}(x) \\, \\operatorname{q}(x))`. Args: - prob_dist_p (ndarray or list): discrete probability distribution :math:`p`. - prob_dist_q (ndarray or list): discrete probability distribution :math:`q`. + prob_dist_p (ndarray or list): discrete probability + :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. + prob_dist_q (ndarray or list): discrete probability + :math:`\\{\\operatorname{q}(x)\\}_{x\\in\\mathcal{X}}`. alpha (float or int): order of the Rényi entropy. base (float): the base of the log. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be @@ -323,7 +330,7 @@ def classical_relative_renyi_entropy( the current backend. Defaults to ``None``. Returns: - float: Classical relative Rényi entropy :math:`H_{\\alpha}(\\mathbf{p} \\, \\| \\, \\mathbf{q})`. + float: Classical relative Rényi entropy :math:`D_{\\alpha,b}^{\\text{re}}`. """ backend = _check_backend(backend) prob_dist_p = backend.cast(prob_dist_p, dtype=np.float64) @@ -395,8 +402,8 @@ def classical_tsallis_entropy(prob_dist, alpha: float, base: float = 2, backend= This is defined as .. math:: - S_{\\alpha}(\\mathbf{p}) = \\frac{1}{\\alpha - 1} \\, - \\left(1 - \\sum_{x} \\, \\mathbf{p}^{\\alpha}(x) \\right) + S_{\\alpha}(\\operatorname{p}) = \\frac{1}{\\alpha - 1} \\, + \\left(1 - \\sum_{x} \\, \\operatorname{p}^{\\alpha}(x) \\right) Args: prob_dist (ndarray): discrete probability distribution. @@ -408,7 +415,7 @@ def classical_tsallis_entropy(prob_dist, alpha: float, base: float = 2, backend= the current backend. Defaults to ``None``. Returns: - float: Classical Tsallis entropy :math:`S_{\\alpha}(\\mathbf{p})`. + float: Classical Tsallis entropy :math:`S_{\\alpha}(\\operatorname{p})`. """ backend = _check_backend(backend) From 5250fa7055e9229b981267589ceca5eb26177406 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 11:03:02 +0400 Subject: [PATCH 06/15] finish classical functions --- src/qibo/quantum_info/entropies.py | 68 ++++++++++++++++++------------ 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 76156f4d47..48ec99002d 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -29,7 +29,7 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): Args: prob_dist (ndarray or list): probability array :math:`\\{\\operatorname{p(x)}\\}_{x \\in \\mathcal{X}}`. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -100,7 +100,7 @@ def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backen :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. prob_dist_q (ndarray or list): discrete probability :math:`\\{\\operatorname{q}(x)\\}_{x\\in\\mathcal{X}}`. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -180,7 +180,7 @@ def classical_mutual_information( :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. prob_dist_q (ndarray): marginal discrete probability :math:`\\{\\operatorname{q}(y)\\}_{y\\in\\mathcal{Y}}`. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -228,7 +228,7 @@ def classical_renyi_entropy( prob_dist (ndarray): discrete probability :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. alpha (float or int): order of the Rényi entropy. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -324,7 +324,7 @@ def classical_relative_renyi_entropy( prob_dist_q (ndarray or list): discrete probability :math:`\\{\\operatorname{q}(x)\\}_{x\\in\\mathcal{X}}`. alpha (float or int): order of the Rényi entropy. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -397,25 +397,34 @@ def classical_relative_renyi_entropy( def classical_tsallis_entropy(prob_dist, alpha: float, base: float = 2, backend=None): - """Calculates the classical Tsallis entropy for a discrete probability distribution. + """Calculate the (classical) Tsallis entropy for a discrete random variable. This is defined as .. math:: - S_{\\alpha}(\\operatorname{p}) = \\frac{1}{\\alpha - 1} \\, - \\left(1 - \\sum_{x} \\, \\operatorname{p}^{\\alpha}(x) \\right) + \\begin{align} + \\operatorname{H}_{\\alpha}^{\\text{ts}}(\\chi) &= -\\sum_{x\\in\\mathcal{X}} \\, + \\operatorname{p}^{\\alpha}(x) \\, \\ln_{\\alpha}\\left(\\operatorname{p}(x)\\right) + \\\\ + &= \\frac{1}{\\alpha - 1} \\,\\left(1 - \\sum_{x} \\, \\operatorname{p}^{\\alpha}(x) + \\right) \\, , + \\end{align} + + where :math:`\\ln_{\\alpha}(x) \\equiv (x^{1-\\alpha} - 1) / (1 - \\alpha)` + is the so-called :math:`\\alpha`-logarithm. Args: - prob_dist (ndarray): discrete probability distribution. + prob_dist (ndarray): discrete probability + :math:`\\{\\operatorname{p}(x)\\}_{x\\in\\mathcal{X}}`. alpha (float or int): entropic index. - base (float): the base of the log. Used when ``alpha=1.0``. + base (float): the base of the :math:`\\log`. Used when :math:`\\alpha = 1`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Classical Tsallis entropy :math:`S_{\\alpha}(\\operatorname{p})`. + float: Classical Tsallis entropy :math:`\\operatorname{H}_{\\alpha}^{\\text{ts}}`. """ backend = _check_backend(backend) @@ -466,7 +475,7 @@ def classical_tsallis_entropy(prob_dist, alpha: float, base: float = 2, backend= def classical_relative_tsallis_entropy( prob_dist_p, prob_dist_q, alpha: float, base: float = 2, backend=None ): - """Calculate the classical relative Tsallis entropy between two discrete probability distributions. + """Calculate the (classical) relative Tsallis entropy between two discrete random variables. Given a discrete random variable :math:`\\chi` that has values :math:`x` in the set :math:`\\mathcal{X}` with probability :math:`\\mathrm{p}(x)` and a discrete random variable @@ -478,21 +487,26 @@ def classical_relative_tsallis_entropy( \\mathrm{p}^{\\alpha}(x) \\, \\ln_{\\alpha} \\left( \\frac{\\mathrm{p}(x)}{\\mathrm{q}(x)} \\right) \\, , - where :math:`\\ln_{\\alpha}(x) \\equiv \\frac{x^{1 - \\alpha} - 1}{1 - \\alpha}` - is the so-called :math:`\\alpha`-logarithm. When :math:`\\alpha = 1`, it reduces to - :class:`qibo.quantum_info.entropies.classical_relative_entropy`. + where :math:`\\ln_{\\alpha}(x) \\equiv (x^{1 - \\alpha} - 1) / (1 - \\alpha)` + is the so-called :math:`\\alpha`-logarithm. + + When :math:`\\alpha = 1`, this funciton reduces to reduces to + :class:`qibo.quantum_info.classical_relative_entropy`. Args: - prob_dist_p (ndarray or list): discrete probability distribution :math:`p`. - prob_dist_q (ndarray or list): discrete probability distribution :math:`q`. + prob_dist_p (ndarray or list): discrete probability + :math:`\\{\\operatorname{p}(x)\\}`. + prob_dist_q (ndarray or list): discrete probability + :math:`\\{\\operatorname{q}(x)\\}`. alpha (float): entropic index. - base (float): the base of the log used when :math:`\\alpha = 1`. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Used when :math:`\\alpha = 1`. + Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Tsallis relative entropy :math:`D_{\\alpha}^{\\text{ts}}`. + float: Classical Tsallis relative entropy :math:`D_{\\alpha}^{\\text{ts}}`. """ if alpha == 1.0: return classical_relative_entropy(prob_dist_p, prob_dist_q, base, backend) @@ -529,7 +543,7 @@ def von_neumann_entropy( Args: state (ndarray): statevector or density matrix. - base (float, optional): the base of the log. Defaults to :math:`2`. + base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Defaults to ``False``. @@ -613,7 +627,7 @@ def relative_von_neumann_entropy( Args: state (ndarray): statevector or density matrix :math:`\\rho`. target (ndarray): statevector or density matrix :math:`\\sigma`. - base (float, optional): the base of the log. Defaults to :math:`2`. + base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): If ``True``, checks if ``state`` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Defaults to ``False``. @@ -720,7 +734,7 @@ def mutual_information( Args: state (ndarray): statevector or density matrix. partition (Union[List[int], Tuple[int]]): indices of qubits in partition :math:`A`. - base (float, optional): the base of the log. Defaults to :math:`2`. + base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -772,7 +786,7 @@ def renyi_entropy(state, alpha: Union[float, int], base: float = 2, backend=None Args: state (ndarray): statevector or density matrix. alpha (float or int): order of the Rényi entropy. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -856,7 +870,7 @@ def relative_renyi_entropy( state (ndarray): statevector or density matrix :math:`\\rho`. target (ndarray): statevector or density matrix :math:`\\sigma`. alpha (float or int): order of the Rényi entropy. - base (float): the base of the log. Defaults to :math:`2`. + base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. @@ -947,7 +961,7 @@ def tsallis_entropy(state, alpha: float, base: float = 2, backend=None): Args: state (ndarray): statevector or density matrix. alpha (float or int): entropic index. - base (float, optional): the base of the log. Used when ``alpha=1.0``. + base (float, optional): the base of the :math:`\\log`. Used when ``alpha=1.0``. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses @@ -1014,7 +1028,7 @@ def relative_tsallis_entropy( state (ndarray): statevector or density matrix :math:`\\rho`. target (ndarray): statevector or density matrix :math:`\\sigma`. alpha (float or int): entropic index :math:`\\alpha \\in [0, \\, 2]`. - base (float, optional): the base of the log used when :math:`\\alpha = 1`. + base (float, optional): the base of the :math:`\\log` used when :math:`\\alpha = 1`. Defaults to :math:`2`. check_hermitian (bool, optional): Used when :math:`\\alpha = 1`. If ``True``, checks if ``state`` is Hermitian. @@ -1091,7 +1105,7 @@ def entanglement_entropy( Args: state (ndarray): statevector or density matrix. bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. - base (float, optional): the base of the log. Defaults to :math: `2`. + base (float, optional): the base of the :math:`\\log`. Defaults to :math: `2`. check_hermitian (bool, optional): if ``True``, checks if :math:`\\rho_{A}` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Default: ``False``. return_spectrum: if ``True``, returns ``entropy`` and eigenvalues of ``state``. From 01b7e8071cc5677a5908e6ae4db8993d75eca4bf Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 12:27:20 +0400 Subject: [PATCH 07/15] more changes --- src/qibo/quantum_info/entropies.py | 68 +++++++++++++++++------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 48ec99002d..0794c576fb 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -1,6 +1,6 @@ """Submodule with entropy measures.""" -from typing import Union +from typing import List, Tuple, Union import numpy as np @@ -35,7 +35,7 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): Defaults to ``None``. Returns: - float: Shannon entropy :math:`\\operatorname{H}_{b}(\\chi)`. + float: Shannon entropy :math:`\\operatorname{H}_{b}`. """ backend = _check_backend(backend) @@ -106,7 +106,7 @@ def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backen the current backend. Defaults to ``None``. Returns: - float: Classical relative entropy between :math:`\\chi` and :math:`\\upsilon`. + float: Classical relative entropy :math:`\\operatorname{D}_{b}`. """ backend = _check_backend(backend) prob_dist_p = backend.cast(prob_dist_p, dtype=np.float64) @@ -186,7 +186,7 @@ def classical_mutual_information( Defaults to ``None``. Returns: - float: Mutual information :math:`\\operatorname{I}(X, \\, Y)`. + float: Mutual information :math:`\\operatorname{I}_{b}`. """ return ( shannon_entropy(prob_dist_p, base, backend) @@ -534,28 +534,29 @@ def von_neumann_entropy( return_spectrum: bool = False, backend=None, ): - """Calculates the von-Neumann entropy :math:`S(\\rho)` of a quantum ``state`` :math:`\\rho`. + """Calculate the von Neumann entropy of a quantum state. - It is given by + Given a quantum ``state`` :math:`\\rho`, the base-:math:`b` von Neumann entropy is .. math:: - S(\\rho) = - \\text{tr}\\left[\\rho \\, \\log(\\rho)\\right] + S_{b}(\\rho) = -\\text{Tr}\\left(\\rho \\, \\log_{b}(\\rho)\\right) \\, . Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Defaults to ``False``. - return_spectrum: if ``True``, returns ``entropy`` and - :math:`-\\log_{\\textup{b}}(\\textup{eigenvalues})`, where :math:`b` is ``base``. - If ``False``, returns only ``entropy``. Default is ``False``. + return_spectrum: if ``True``, returns :math:`S_{b}(\\rho)` and + :math:`-\\log_{b}(\\text{eigenvalues}(\\rho))`. + If ``False``, returns only :math:`S_{b}(\\rho)`. + Default is ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: The von-Neumann entropy :math:`S` of ``state`` :math:`\\rho`. + float: von Neumann entropy :math:`\\operatorname{S}_{b}`. """ backend = _check_backend(backend) @@ -614,15 +615,16 @@ def relative_von_neumann_entropy( precision_tol: float = 1e-14, backend=None, ): - """Calculates the relative von Neumann entropy between two quantum states. + """Calculate the relative von Neumann entropy between two quantum states. - Also known as *quantum relative entropy*, :math:`S(\\rho \\, \\| \\, \\sigma)` is given by + Given two quantum states :math:`\\rho` and :math:`\\sigma`, it is defined as .. math:: - S(\\rho \\, \\| \\, \\sigma) = \\text{tr}\\left[\\rho \\, \\log(\\rho)\\right] - - \\text{tr}\\left[\\rho \\, \\log(\\sigma)\\right] + \\Delta_{b}(\\rho \\, \\| \\, \\sigma) = \\text{Tr}\\left(\\rho \\, + \\log_{b}(\\rho)\\right) - \\text{Tr}\\left(\\rho \\, + \\log_{b}(\\sigma)\\right) - where ``state`` :math:`\\rho` and ``target`` :math:`\\sigma` are two quantum states. + It is also known as the *quantum relative entropy*. Args: state (ndarray): statevector or density matrix :math:`\\rho`. @@ -639,7 +641,7 @@ def relative_von_neumann_entropy( the current backend. Defaults to ``None``. Returns: - float: Relative (von-Neumann) entropy :math:`S(\\rho \\, \\| \\, \\sigma)`. + float: Relative von Neumann entropy :math:`\\Delta_{b}`. """ backend = _check_backend(backend) state = backend.cast(state) @@ -718,22 +720,30 @@ def relative_von_neumann_entropy( def mutual_information( - state, partition, base: float = 2, check_hermitian: bool = False, backend=None + state, + partition: Union[List[int], Tuple[int, ...]], + base: float = 2, + check_hermitian: bool = False, + backend=None, ): - """Calculates the mutual information of a bipartite state. + """Calculate the mutual information over two partitions of a quantum state. - Given a qubit ``partition`` :math:`A`, the mutual information - of state :math:`\\rho` is given by + Given a bipartite quantum state :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, + its base-:math:`b` mutual information over those two partitions is given by .. math:: - I(\\rho) \\equiv S(\\rho_{A}) + S(\\rho_{B}) - S(\\rho) \\, , + \\mathcal{I}_{b}(\\rho) = \\operatorname{S}_{b}(\\rho_{A}) + + \\operatorname{S}_{b}(\\rho_{B}) - \\operatorname{S}_{b}(\\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`. + where :math:`\\rho_{A} = \\text{Tr}_{B}(\\rho)` is the reduced density matrix of qubits + in partition :math:`A`, :math:`\\rho_{B} = \\text{Tr}_{A}(\\rho)` is the reduced density + matrix of qubits in partition :math:`B`, and :math:`\\operatorname{S}_{b}(\\cdot)` + is the base-:math:`b` :func:`qibo.quantum_info.von_neumann_entropy`. Args: - state (ndarray): statevector or density matrix. - partition (Union[List[int], Tuple[int]]): indices of qubits in partition :math:`A`. + state (ndarray): statevector or density matrix :math:`\\rho`. + partition (list or tuple): indices of qubits in partition :math:`A`. + Partition :math:`B` is assumed to contain the remaining qubits. base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Defaults to ``False``. @@ -742,7 +752,7 @@ def mutual_information( the current backend. Defaults to ``None``. Returns: - float: Mutual information :math:`I(\\rho)` of ``state`` :math:`\\rho`. + float: Mutual information :math:`\\mathcal{I}_{b}`. """ nqubits = np.log2(len(state)) @@ -762,7 +772,7 @@ def mutual_information( def renyi_entropy(state, alpha: Union[float, int], base: float = 2, backend=None): - """Calculates the Rényi entropy :math:`H_{\\alpha}` of a quantum state :math:`\\rho`. + """Calculate the Rényi entropy :math:`H_{\\alpha}` of a quantum state :math:`\\rho`. For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)`, the Rényi entropy is defined as From acdab7fe40c4091e767ee2a303e2e55161bd8f00 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 13:17:50 +0400 Subject: [PATCH 08/15] rename partition --- tests/test_quantum_info_entropies.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_quantum_info_entropies.py b/tests/test_quantum_info_entropies.py index 7c289009f9..cbe1c6a786 100644 --- a/tests/test_quantum_info_entropies.py +++ b/tests/test_quantum_info_entropies.py @@ -822,14 +822,14 @@ def test_relative_tsallis_entropy( @pytest.mark.parametrize("check_hermitian", [False, True]) @pytest.mark.parametrize("base", [2, 10, np.e, 5]) -@pytest.mark.parametrize("bipartition", [[0], [1]]) -def test_entanglement_entropy(backend, bipartition, base, check_hermitian): +@pytest.mark.parametrize("partition", [[0], [1]]) +def test_entanglement_entropy(backend, partition, base, check_hermitian): with pytest.raises(TypeError): state = np.random.rand(2, 3) state = backend.cast(state, dtype=state.dtype) test = entanglement_entropy( state, - bipartition=bipartition, + partition=partition, base=base, check_hermitian=check_hermitian, backend=backend, @@ -839,7 +839,7 @@ def test_entanglement_entropy(backend, bipartition, base, check_hermitian): state = backend.cast(state, dtype=state.dtype) test = entanglement_entropy( state, - bipartition=bipartition, + partition=partition, base=0, check_hermitian=check_hermitian, backend=backend, @@ -849,7 +849,7 @@ def test_entanglement_entropy(backend, bipartition, base, check_hermitian): state = random_unitary(4, backend=backend) test = entanglement_entropy( state, - bipartition=bipartition, + partition=partition, base=base, check_hermitian=True, backend=backend, @@ -861,7 +861,7 @@ def test_entanglement_entropy(backend, bipartition, base, check_hermitian): entang_entrop = entanglement_entropy( state, - bipartition=bipartition, + partition=partition, base=base, check_hermitian=check_hermitian, backend=backend, @@ -885,7 +885,7 @@ def test_entanglement_entropy(backend, bipartition, base, check_hermitian): entang_entrop = entanglement_entropy( state, - bipartition=bipartition, + partition=partition, base=base, check_hermitian=check_hermitian, backend=backend, From 188e26ac6c1fac2ce8e6da0999635ac98138f49c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 13:20:31 +0400 Subject: [PATCH 09/15] entropies --- src/qibo/quantum_info/entropies.py | 84 +++++++++++++++++------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 0794c576fb..a632464fd8 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -772,29 +772,33 @@ def mutual_information( def renyi_entropy(state, alpha: Union[float, int], base: float = 2, backend=None): - """Calculate the Rényi entropy :math:`H_{\\alpha}` of a quantum state :math:`\\rho`. + """Calculate the Rényi entropy of a quantum state. - For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)`, the Rényi entropy is defined as + For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)`, the Rényi entropy + :math:`\\operatorname{H}_{\\alpha,b}` + is defined as .. math:: - H_{\\alpha}(\\rho) = \\frac{1}{1 - \\alpha} \\, \\log\\left( \\rho^{\\alpha} \\right) \\, . + \\operatorname{S}_{\\alpha,b}^{\\text{re}}(\\rho) = \\frac{1}{1 - \\alpha} \\, + \\log_{b}\\left(\\rho^{\\alpha} \\right) \\, . A special case is the limit :math:`\\alpha \\to 1`, in which the Rényi entropy - coincides with the :func:`qibo.quantum_info.entropies.entropy`. + coincides with the :func:`qibo.quantum_info.von_neumann_entropy`. Another special case is the limit :math:`\\alpha \\to 0`, where the function is - reduced to :math:`\\log\\left(d\\right)`, with :math:`d = 2^{n}` + reduced to :math:`\\log_{b}\\left(d\\right)`, with :math:`d = 2^{n}` being the dimension of the Hilbert space in which ``state`` :math:`\\rho` lives in. This is known as the `Hartley entropy `_ (also known as *Hartley function* or *max-entropy*). In the limit :math:`\\alpha \\to \\infty`, the function reduces to - :math:`-\\log(\\|\\rho\\|_{\\infty})`, with :math:`\\|\\cdot\\|_{\\infty}` - being the `spectral norm `_. + :math:`-\\log_{b}(\\|\\rho\\|_{\\infty})`, with :math:`\\|\\cdot\\|_{\\infty}` + being the `spectral norm + `_. This is known as the `min-entropy `_. Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. alpha (float or int): order of the Rényi entropy. base (float): the base of the :math:`\\log`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be @@ -802,7 +806,7 @@ def renyi_entropy(state, alpha: Union[float, int], base: float = 2, backend=None the current backend. Defaults to ``None``. Returns: - float: Rényi entropy :math:`H_{\\alpha}`. + float: Rényi entropy :math:`\\operatorname{S}_{\\alpha,b}^{\\text{re}}`. """ backend = _check_backend(backend) @@ -851,21 +855,22 @@ def renyi_entropy(state, alpha: Union[float, int], base: float = 2, backend=None def relative_renyi_entropy( state, target, alpha: Union[float, int], base: float = 2, backend=None ): - """Calculates the relative Rényi entropy between two quantum states. + """Calculate the relative Rényi entropy between two quantum states. - For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)` and quantum states - :math:`\\rho` and :math:`\\sigma`, the relative Rényi entropy is defined as + For :math:`\\alpha \\in (0, \\, 1) \\cup (1, \\, \\infty)` and two quantum states + :math:`\\rho` and :math:`\\sigma`, the base-:math:`b` relative Rényi entropy + is defined as .. math:: - H_{\\alpha}(\\rho \\, \\| \\, \\sigma) = \\frac{1}{\\alpha - 1} \\, - \\log\\left( \\textup{tr}\\left( \\rho^{\\alpha} \\, + \\Delta_{\\alpha,b}^{\\text{re}}(\\rho \\, \\| \\, \\sigma) = \\frac{1}{\\alpha - 1} + \\, \\log_{b}\\left( \\text{Tr}\\left( \\rho^{\\alpha} \\, \\sigma^{1 - \\alpha} \\right) \\right) \\, . A special case is the limit :math:`\\alpha \\to 1`, in which the Rényi entropy - coincides with the :func:`qibo.quantum_info.entropies.relative_von_neumann_entropy`. + coincides with the :func:`qibo.quantum_info.relative_von_neumann_entropy`. In the limit :math:`\\alpha \\to \\infty`, the function reduces to - :math:`-2 \\, \\log(\\|\\sqrt{\\rho} \\, \\sqrt{\\sigma}\\|_{1})`, + :math:`-2 \\, \\log_{b}(\\|\\sqrt{\\rho} \\, \\sqrt{\\sigma}\\|_{1})`, with :math:`\\|\\cdot\\|_{1}` being the `Schatten 1-norm `_. This is known as the `min-relative entropy `_. @@ -886,7 +891,7 @@ def relative_renyi_entropy( the current backend. Defaults to ``None``. Returns: - float: Relative Rényi entropy :math:`H_{\\alpha}(\\rho \\, \\| \\, \\sigma)`. + float: Relative Rényi entropy :math:`\\Delta_{\\alpha,,b}^{\\text{re}}`. """ backend = _check_backend(backend) state = backend.cast(state) @@ -959,26 +964,26 @@ def relative_renyi_entropy( def tsallis_entropy(state, alpha: float, base: float = 2, backend=None): - """Calculates the Tsallis entropy of a quantum state. + """Calculate the Tsallis entropy of a quantum state. .. math:: - S_{\\alpha}(\\rho) = \\frac{1}{1 - \\alpha} \\, - \\left( \\text{tr}(\\rho^{\\alpha}) - 1 \\right) + \\operatorname{S}_{\\alpha}^{\\text{ts}}(\\rho) = + \\frac{\\text{Tr}(\\rho^{\\alpha}) - 1}{1 - \\alpha} When :math:`\\alpha = 1`, the functions defaults to - :func:`qibo.quantum_info.entropies.entropy`. + :func:`qibo.quantum_info.von_neumann_entropy`. Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. alpha (float or int): entropic index. - base (float, optional): the base of the :math:`\\log`. Used when ``alpha=1.0``. + base (float, optional): the base of the :math:`\\log`. Used when :math:`\\alpha = 1`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Tsallis entropy :math:`S_{\\alpha}(\\rho)`. + float: Tsallis entropy :math:`\\operatorname{S}_{\\alpha}^{\\text{ts}}`. """ backend = _check_backend(backend) @@ -1029,16 +1034,17 @@ def relative_tsallis_entropy( .. math:: \\Delta_{\\alpha}^{\\text{ts}}(\\rho, \\, \\sigma) = \\frac{1 - - \\text{tr}\\left(\\rho^{\\alpha} \\, \\sigma^{1 - \\alpha}\\right)}{1 - \\alpha} \\, . + \\text{Tr}\\left(\\rho^{\\alpha} \\, \\sigma^{1 - \\alpha}\\right)}{1 - \\alpha} + \\, . A special case is the limit :math:`\\alpha \\to 1`, in which the Tsallis entropy - coincides with the :func:`qibo.quantum_info.entropies.relative_von_neumann_entropy`. + coincides with the :func:`qibo.quantum_info.relative_von_neumann_entropy`. Args: state (ndarray): statevector or density matrix :math:`\\rho`. target (ndarray): statevector or density matrix :math:`\\sigma`. alpha (float or int): entropic index :math:`\\alpha \\in [0, \\, 2]`. - base (float, optional): the base of the :math:`\\log` used when :math:`\\alpha = 1`. + base (float, optional): the base of the :math:`\\log`. Used when :math:`\\alpha = 1`. Defaults to :math:`2`. check_hermitian (bool, optional): Used when :math:`\\alpha = 1`. If ``True``, checks if ``state`` is Hermitian. @@ -1097,25 +1103,29 @@ def relative_tsallis_entropy( def entanglement_entropy( state, - bipartition, + partition: Union[List[int], Tuple[int, ...]], base: float = 2, check_hermitian: bool = False, return_spectrum: bool = False, backend=None, ): - """Calculates the entanglement entropy :math:`S` of bipartition :math:`A` - of ``state`` :math:`\\rho`. This is given by + """Calculate the entanglement entropy of a bipartite quantum state. + + + Given a bipartite quantum state :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, + its base-:math:`b` entanglement entropy is given by .. math:: - S(\\rho_{A}) = -\\text{tr}(\\rho_{A} \\, \\log(\\rho_{A})) \\, , + \\operatorname{S}_{b}^{\\text{ent}}(\\rho) \\equiv \\operatorname{S}_{b}(\\rho_{A}) = + -\\text{Tr}\\left(\\rho_{A} \\, \\log_{b}(\\rho_{A})\\right) \\, , - where :math:`\\rho_{A} = \\text{tr}_{B}(\\rho)` is the reduced density matrix calculated - by tracing out the ``bipartition`` :math:`B`. + where :math:`\\rho_{A} = \\text{Tr}_{B}(\\rho)` is the reduced density matrix calculated + by tracing out the ``partition`` :math:`B`. Args: state (ndarray): statevector or density matrix. - bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. - base (float, optional): the base of the :math:`\\log`. Defaults to :math: `2`. + partition (list or tuple): qubits in the partition :math:`B` to be traced out. + base (float, optional): the base of the :math:`\\log`. Defaults to :math:`2`. check_hermitian (bool, optional): if ``True``, checks if :math:`\\rho_{A}` is Hermitian. If ``False``, it assumes ``state`` is Hermitian . Default: ``False``. return_spectrum: if ``True``, returns ``entropy`` and eigenvalues of ``state``. @@ -1125,7 +1135,7 @@ def entanglement_entropy( the current backend. Defaults to ``None``. Returns: - float: Entanglement entropy :math:`S` of ``state`` :math:`\\rho`. + float: Entanglement entropy :math:`\\operatorname{S}_{b}^{\\text{ent}}`. """ backend = _check_backend(backend) @@ -1142,7 +1152,7 @@ def entanglement_entropy( f"state must have dims either (k,) or (k,k), but have dims {state.shape}.", ) - reduced_density_matrix = partial_trace(state, bipartition, backend=backend) + reduced_density_matrix = partial_trace(state, partition, backend=backend) entropy_entanglement = von_neumann_entropy( reduced_density_matrix, From 4cf1dc0024f2c6962cb8d6445eff820d36f5a580 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 13:27:55 +0400 Subject: [PATCH 10/15] more changes --- src/qibo/quantum_info/entanglement.py | 33 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index e8843793e6..38cc376dc5 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -1,5 +1,7 @@ """Submodules with entanglement measures.""" +from typing import List, Tuple, Union + import numpy as np from qibo.backends import _check_backend @@ -12,7 +14,12 @@ from qibo.quantum_info.metrics import fidelity, purity -def concurrence(state, bipartition, check_purity: bool = True, backend=None): +def concurrence( + state, + partition: Union[List[int], Tuple[int, ...]], + check_purity: bool = True, + backend=None, +): """Calculate concurrence of a pure bipartite quantum state. For a pure bipartite quantum state @@ -24,11 +31,11 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): \\operatorname{Tr}(\\rho_{B}^{2}))} \\, , where :math:`\\rho_{B} = \\operatorname{Tr}_{A}(\\rho)` is the reduced density operator - obtained by tracing out the qubits in the ``bipartition`` :math:`A`. + obtained by tracing out the qubits in the ``partition`` :math:`A`. Args: state (ndarray): statevector or density matrix. - bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. + partition (list or tuple): qubits in the partition :math:`A` to be traced out. check_purity (bool, optional): if ``True``, checks if ``state`` is pure. If ``False``, it assumes ``state`` is pure . Defaults to ``True``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -36,7 +43,7 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): Defaults to ``None``. Returns: - float: Concurrence of :math:`\\rho`. + float: Concurrence :math:`\\operatorname{C}`. """ backend = _check_backend(backend) @@ -66,7 +73,7 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): "concurrence only implemented for pure quantum states.", ) - reduced_density_matrix = partial_trace(state, bipartition, backend=backend) + reduced_density_matrix = partial_trace(state, partition, backend=backend) purity_reduced = purity(reduced_density_matrix, backend=backend) if purity_reduced - 1.0 > 0.0: @@ -78,7 +85,7 @@ def concurrence(state, bipartition, check_purity: bool = True, backend=None): def entanglement_of_formation( - state, bipartition, base: float = 2, check_purity: bool = True, backend=None + state, partition, base: float = 2, check_purity: bool = True, backend=None ): """Calculate the entanglement of formation of a pure bipartite quantum state. @@ -105,9 +112,9 @@ def entanglement_of_formation( :func:`qibo.quantum_info.shannon_entropy`. Args: - state (ndarray): statevector or density matrix. - bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. - base (float): the base of the log in :func:`qibo.quantum_info.entropies.shannon_entropy`. + state (ndarray): statevector or density matrix :math:`\\rho`. + partition (list or tuple or ndarray): qubits in the partition :math:`B` to be traced out. + base (float): the base of the :math:`\\log` in :func:`qibo.quantum_info.shannon_entropy`. Defaults to :math:`2`. check_purity (bool, optional): if ``True``, checks if ``state`` is pure. If ``False``, it assumes ``state`` is pure . Default: ``True``. @@ -117,14 +124,14 @@ def entanglement_of_formation( Returns: - float: Entanglement of formation of state :math:`\\rho`. + float: Entanglement of formation :math:`\\operatorname{E}_{f}`. """ from qibo.quantum_info.entropies import shannon_entropy # pylint: disable=C0415 backend = _check_backend(backend) concur = concurrence( - state, bipartition=bipartition, check_purity=check_purity, backend=backend + state, partition=partition, check_purity=check_purity, backend=backend ) concur = (1 + np.sqrt(1 - concur**2)) / 2 probabilities = [1 - concur, concur] @@ -148,14 +155,14 @@ def negativity(state, bipartition, backend=None): (also known as nuclear norm or trace norm). Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses it uses the current backend. Defaults to ``None``. Returns: - float: Negativity :math:`\\operatorname{Neg}` of state :math:`\\rho`. + float: Negativity :math:`\\operatorname{Neg}`. """ backend = _check_backend(backend) From 297b1785f44799d2ea15b9d3199b0c9f234558bd Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 13:43:16 +0400 Subject: [PATCH 11/15] metrics --- src/qibo/quantum_info/metrics.py | 47 ++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index 61503cb48c..22240a135a 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -10,15 +10,19 @@ def purity(state, backend=None): - """Purity of a quantum state :math:`\\rho`. + """Calculate the purity of a quantum state. This is given by .. math:: - \\text{purity}(\\rho) = \\text{tr}(\\rho^{2}) \\, . + \\text{Tr}(\\rho^{2}) \\, . Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + backend (:class:`qibo.backends.abstract.Backend`, optional): backend + to be used in the execution. If ``None``, it uses + it uses the current backend. Defaults to ``None``. + Returns: float: Purity of quantum ``state`` :math:`\\rho`. """ @@ -41,34 +45,43 @@ def purity(state, backend=None): def impurity(state, backend=None): - """Impurity of quantum state :math:`\\rho`. + """Calculate the impurity of a quantum state. - This is given by :math:`1 - \\text{purity}(\\rho)`, where :math:`\\text{purity}` - is defined in :func:`qibo.quantum_info.purity`. + This is given by + + .. math:: + 1 - \\text{Tr}(\\rho^{2}) \\, . Args: - state (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + backend (:class:`qibo.backends.abstract.Backend`, optional): backend + to be used in the execution. If ``None``, it uses + it uses the current backend. Defaults to ``None``. Returns: - float: impurity of ``state`` :math:`\\rho`. + float: Impurity of quantum ``state`` :math:`\\rho`. """ return 1 - purity(state, backend=backend) def trace_distance(state, target, check_hermitian: bool = False, backend=None): - """Trace distance between two quantum states, :math:`\\rho` and - :math:`\\sigma`: + """Calculate the trace distance between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, + their trace distance is defined as .. math:: - T(\\rho, \\sigma) = \\frac{1}{2} \\, \\|\\rho - \\sigma\\|_{1} = \\frac{1}{2} \\, - \\text{tr}\\left[ \\sqrt{(\\rho - \\sigma)^{\\dagger}(\\rho - \\sigma)} - \\right] \\, , + \\begin{align} + \\operatorname{T}(\\rho, \\, \\sigma) &= \\frac{1}{2} \\, \\|\\rho - \\sigma\\|_{1} \\\\ + &= \\frac{1}{2} \\, \\text{Tr}\\left( \\sqrt{(\\rho - \\sigma)^{\\dagger} + (\\rho - \\sigma)} \\right) \\, , + \\end{align} - where :math:`\\|\\cdot\\|_{1}` is the Schatten 1-norm. + where :math:`\\|\\cdot\\|_{1}` is the Schatten :math:`1`-norm. Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + target (ndarray): statevector or density matrix :math:`\\sigma`. check_hermitian (bool, optional): if ``True``, checks if :math:`\\rho - \\sigma` is Hermitian. If ``False``, it assumes the difference is Hermitian. @@ -78,7 +91,7 @@ def trace_distance(state, target, check_hermitian: bool = False, backend=None): Defaults to ``None``. Returns: - float: Trace distance between ``state`` :math:`\\rho` and ``target`` :math:`\\sigma`. + float: Trace distance :math:`\\operatorname{T}`. """ backend = _check_backend(backend) From 5b4e5c08d6fbbf53746452ccee9862f700788ee2 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 14:22:39 +0400 Subject: [PATCH 12/15] rename variables --- doc/source/api-reference/qibo.rst | 2 +- src/qibo/callbacks.py | 2 +- src/qibo/quantum_info/entanglement.py | 16 ++++++++++------ tests/test_quantum_info_entanglement.py | 18 +++++++++--------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 9ee57772ee..3e00857a90 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -1844,7 +1844,7 @@ Entanglement entropy .. note:: ``check_hermitian`` flag allows the user to choose if the function will check if - the reduced density matrix resulting from tracing out ``bipartition`` from input + the reduced density matrix resulting from tracing out ``partition`` from input ``state`` is Hermitian or not. Default option is ``check_hermitian=False``, i.e. the assumption of Hermiticity. This is faster and, more importantly, this function are intended to be used on Hermitian inputs. When ``check_hermitian=True`` diff --git a/src/qibo/callbacks.py b/src/qibo/callbacks.py index 2313e42e95..94d4cb437f 100644 --- a/src/qibo/callbacks.py +++ b/src/qibo/callbacks.py @@ -128,7 +128,7 @@ def apply(self, backend, state): entropy, spectrum = entanglement_entropy( state, - bipartition=self.partition, + partition=self.partition, base=self.base, check_hermitian=self.check_hermitian, return_spectrum=True, diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 38cc376dc5..d7b7b931ad 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -85,7 +85,11 @@ def concurrence( def entanglement_of_formation( - state, partition, base: float = 2, check_purity: bool = True, backend=None + state, + partition: Union[List[int], Tuple[int, ...]], + base: float = 2, + check_purity: bool = True, + backend=None, ): """Calculate the entanglement of formation of a pure bipartite quantum state. @@ -113,7 +117,7 @@ def entanglement_of_formation( Args: state (ndarray): statevector or density matrix :math:`\\rho`. - partition (list or tuple or ndarray): qubits in the partition :math:`B` to be traced out. + partition (list or tuple): qubits in the partition :math:`B` to be traced out. base (float): the base of the :math:`\\log` in :func:`qibo.quantum_info.shannon_entropy`. Defaults to :math:`2`. check_purity (bool, optional): if ``True``, checks if ``state`` is pure. If ``False``, @@ -141,7 +145,7 @@ def entanglement_of_formation( return ent_of_form -def negativity(state, bipartition, backend=None): +def negativity(state, partition: Union[List[int], Tuple[int, ...]], backend=None): """Calculate the negativity of a bipartite quantum state. Given a bipartite state :math:`\\rho \\in \\mathcal{H}_{A} \\otimes \\mathcal{H}_{B}`, @@ -155,8 +159,8 @@ def negativity(state, bipartition, backend=None): (also known as nuclear norm or trace norm). Args: - state (ndarray): statevector or density matrix :math:`\\rho`. - bipartition (list or tuple or ndarray): qubits in the subsystem to be traced out. + state (ndarray): statevector or density matrix :math:``. + partition (list or tuple): qubits in the partition :math:`A` to be traced out. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses it uses the current backend. Defaults to ``None``. @@ -166,7 +170,7 @@ def negativity(state, bipartition, backend=None): """ backend = _check_backend(backend) - reduced = partial_transpose(state, bipartition, backend) + reduced = partial_transpose(state, partition, backend) reduced = backend.np.conj(reduced.T) @ reduced norm = backend.np.trace(matrix_power(reduced, 1 / 2, backend=backend)) diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index ed7e98f300..375b5ca48c 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -16,34 +16,34 @@ @pytest.mark.parametrize("check_purity", [True, False]) @pytest.mark.parametrize("base", [2, 10, np.e, 5]) -@pytest.mark.parametrize("bipartition", [[0], [1]]) -def test_concurrence_and_formation(backend, bipartition, base, check_purity): +@pytest.mark.parametrize("partition", [[0], [1]]) +def test_concurrence_and_formation(backend, partition, base, check_purity): with pytest.raises(TypeError): state = np.random.rand(2, 3) state = backend.cast(state, dtype=state.dtype) test = concurrence( - state, bipartition=bipartition, check_purity=check_purity, backend=backend + state, partition=partition, check_purity=check_purity, backend=backend ) with pytest.raises(TypeError): state = random_statevector(4, backend=backend) test = concurrence( - state, bipartition=bipartition, check_purity="True", backend=backend + state, partition=partition, check_purity="True", backend=backend ) if check_purity is True: with pytest.raises(NotImplementedError): state = backend.identity_density_matrix(2, normalize=False) - test = concurrence(state, bipartition=bipartition, backend=backend) + test = concurrence(state, partition=partition, backend=backend) nqubits = 2 dim = 2**nqubits state = random_statevector(dim, backend=backend) concur = concurrence( - state, bipartition=bipartition, check_purity=check_purity, backend=backend + state, partition=partition, check_purity=check_purity, backend=backend ) ent_form = entanglement_of_formation( state, - bipartition=bipartition, + partition=partition, base=base, check_purity=check_purity, backend=backend, @@ -55,10 +55,10 @@ def test_concurrence_and_formation(backend, bipartition, base, check_purity): random_density_matrix(2, pure=True, backend=backend), random_density_matrix(2, pure=True, backend=backend), ) - concur = concurrence(state, bipartition, check_purity=check_purity, backend=backend) + concur = concurrence(state, partition, check_purity=check_purity, backend=backend) ent_form = entanglement_of_formation( state, - bipartition=bipartition, + partition=partition, base=base, check_purity=check_purity, backend=backend, From b1b26f5c8b8179217884971cc685f94d4fcf207e Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 7 Nov 2024 15:08:57 +0400 Subject: [PATCH 13/15] metrics --- src/qibo/quantum_info/entanglement.py | 2 +- src/qibo/quantum_info/metrics.py | 123 +++++++++++++++----------- 2 files changed, 74 insertions(+), 51 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index d7b7b931ad..2521264e86 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -159,7 +159,7 @@ def negativity(state, partition: Union[List[int], Tuple[int, ...]], backend=None (also known as nuclear norm or trace norm). Args: - state (ndarray): statevector or density matrix :math:``. + state (ndarray): statevector or density matrix :math:`\\rho`. partition (list or tuple): qubits in the partition :math:`A` to be traced out. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses it uses the current backend. diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index 22240a135a..ea432c2d89 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -154,7 +154,7 @@ def hilbert_schmidt_inner_product(operator_A, operator_B, backend=None): inner product between the two is given by .. math:: - \\braket{A, \\, B}_{\\text{HS}} = \\text{tr}\\left(A^{\\dagger} \\, B\\right) \\, . + \\braket{A, \\, B}_{\\text{HS}} = \\text{Tr}\\left(A^{\\dagger} \\, B\\right) \\, . Args: operator_A (ndarray): operator :math:`A`. @@ -174,25 +174,30 @@ def hilbert_schmidt_inner_product(operator_A, operator_B, backend=None): def hilbert_schmidt_distance(state, target, backend=None): - """Calculate the Hilbert-Schmidt distance between two quantum states: + """Calculate the Hilbert-Schmidt distance between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, their + Hilbert-Schmidt distance is given by .. math:: - \\braket{\\rho - \\sigma, \\, \\rho - \\sigma}_{\\text{HS}} = - \\text{tr}\\left((\\rho - \\sigma)^{2}\\right) \\, , + \\begin{align} + \\operatorname{HSD}(\\rho, \\, \\sigma) &\\equiv + \\braket{\\rho - \\sigma, \\, \\rho - \\sigma}_{\\text{HS}} \\\\ + &= \\text{Tr}\\left((\\rho - \\sigma)^{2}\\right) \\, , + \\end{align} where :math:`\\braket{\\cdot, \\, \\cdot}_{\\text{HS}}` is the :func:`qibo.quantum_info.hilbert_schmidt_inner_product`. Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`.. + target (ndarray): statevector or density matrix :math:`\\sigma`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. Returns: - float: Hilbert-Schmidt distance between ``state`` :math:`\\rho` - and ``target`` :math:`\\sigma`. + float: Hilbert-Schmidt distance :math:`\\operatorname{HSD}`. References: 1. P. J. Coles, M. Cerezo, and L. Cincio, *Strong bound between trace distance @@ -224,21 +229,23 @@ def hilbert_schmidt_distance(state, target, backend=None): def fidelity(state, target, check_hermitian: bool = False, backend=None): - """Fidelity :math:`F(\\rho, \\sigma)` between ``state`` :math:`\\rho` and - ``target`` state :math:`\\sigma`. In general, + """Calcualte the fidelity between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, In general, .. math:: - F(\\rho, \\sigma) = \\text{tr}^{2}\\left( \\sqrt{\\sqrt{\\sigma} \\, - \\rho^{\\dagger} \\, \\sqrt{\\sigma}} \\right) \\, . + \\operatorname{F}(\\rho, \\sigma) = \\text{Tr}\\left( \\sqrt{\\sqrt{\\sigma} \\, + \\rho^{\\dagger} \\, \\sqrt{\\sigma}} \\right) \\, . - However, when at least one of the states is pure, then + When at least one of the quantum states is pure, then :math:`\\operatorname{F}` + reduces to .. math:: - F(\\rho, \\sigma) = \\text{tr}(\\rho \\, \\sigma) + \\operatorname{F}(\\rho, \\sigma) = \\text{Tr}(\\rho \\, \\sigma) Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\operatorname{\\rho}`. + target (ndarray): statevector or density matrix :math:`\\operatorname{\\sigma}`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -246,7 +253,7 @@ def fidelity(state, target, check_hermitian: bool = False, backend=None): Defaults to ``None``. Returns: - float: Fidelity between ``state`` :math:`\\rho` and ``target`` :math:`\\sigma`. + float: Fidelity :math:`\\operatorname{F}`. """ backend = _check_backend(backend) state = backend.cast(state, dtype=state.dtype) @@ -333,18 +340,20 @@ def fidelity(state, target, check_hermitian: bool = False, backend=None): def infidelity(state, target, check_hermitian: bool = False, backend=None): - """Infidelity between ``state`` :math:`\\rho` and ``target`` state - :math:`\\sigma`, which is given by + """Calculate the infidelity between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, + their infidelity is given by .. math:: - 1 - F(\\rho, \\, \\sigma) \\, , + 1 - \\operatorname{F}(\\rho, \\, \\sigma) \\, , - where :math:`F(\\rho, \\, \\sigma)` is the :func:`qibo.quantum_info.fidelity` - between ``state`` and ``target``. + where :math:`\\operatorname{F}(\\rho, \\, \\sigma)` is the :func:`qibo.quantum_info.fidelity` + between ``state`` :math:`\\rho` and ``target`` :math:`\\sigma`. Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + target (ndarray): statevector or density matrix :math:`\\sigma`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -352,24 +361,28 @@ def infidelity(state, target, check_hermitian: bool = False, backend=None): Defaults to ``None``. Returns: - float: Infidelity between ``state`` :math:`\\rho` and ``target`` :math:`\\sigma`. + float: Infidelity :math:`1 - \\operatorname{F}`. """ return 1 - fidelity(state, target, check_hermitian=check_hermitian, backend=backend) def bures_angle(state, target, check_hermitian: bool = False, backend=None): - """Calculates the Bures angle :math:`D_{A}` between a ``state`` - :math:`\\rho` and a ``target`` state :math:`\\sigma`. This is given by + """Calculate the Bures angle between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, + the Bures angle between them is given by .. math:: - D_{A}(\\rho, \\, \\sigma) = \\text{arccos}\\left(\\sqrt{F(\\rho, \\, \\sigma)}\\right) \\, , + \\operatorname{B}_{\\text{ang}}(\\rho, \\, \\sigma) = + \\operatorname{arccos}\\left( + \\sqrt{\\operatorname{F}(\\rho, \\, \\sigma)}\\right) \\, , - where :math:`F(\\rho, \\sigma)` is the :func:`qibo.quantum_info.fidelity` + where :math:`\\operatorname{F}(\\rho, \\sigma)` is the :func:`qibo.quantum_info.fidelity` between `state` and `target`. Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + target (ndarray): statevector or density matrix :math:`\\sigma`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -377,7 +390,7 @@ def bures_angle(state, target, check_hermitian: bool = False, backend=None): Defaults to ``None``. Returns: - float: Bures angle between ``state`` and ``target``. + float: Bures angle :math:`\\operatorname{B}_{\\text{ang}}`. """ backend = _check_backend(backend) @@ -389,18 +402,21 @@ def bures_angle(state, target, check_hermitian: bool = False, backend=None): def bures_distance(state, target, check_hermitian: bool = False, backend=None): - """Calculates the Bures distance :math:`D_{B}` between a ``state`` - :math:`\\rho` and a ``target`` state :math:`\\sigma`. This is given by + """Calculate the Bures distance between two quantum states. + + Given two quantum states :math:`\\rho` and :math:`\\sigma`, + their Bures distance is given by .. math:: - D_{B}(\\rho, \\, \\sigma) = \\sqrt{2 \\, \\left(1 - \\sqrt{F(\\rho, \\, \\sigma)}\\right)} + \\operatorname{B}_{\\text{dist}}(\\rho, \\, \\sigma) = + \\sqrt{2 \\, \\left(1 - \\sqrt{F(\\rho, \\, \\sigma)}\\right)} \\, , - where :math:`F(\\rho, \\sigma)` is the :func:`qibo.quantum_info.fidelity` - between `state` and `target`. + where :math:`\\operatorname{F}(\\rho, \\sigma)` is the + :func:`qibo.quantum_info.fidelity` between ``state`` and ``target``. Args: - state (ndarray): statevector or density matrix. - target (ndarray): statevector or density matrix. + state (ndarray): statevector or density matrix :math:`\\rho`. + target (ndarray): statevector or density matrix :math:`\\sigma`. check_hermitian (bool, optional): if ``True``, checks if ``state`` is Hermitian. Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -408,7 +424,7 @@ def bures_distance(state, target, check_hermitian: bool = False, backend=None): Defaults to ``None``. Returns: - float: Bures distance between ``state`` and ``target``. + float: Bures distance :math:`\\operatorname{B}_{\\text{dist}}`. """ backend = _check_backend(backend) sqrt_fid = backend.np.sqrt( @@ -420,17 +436,24 @@ def bures_distance(state, target, check_hermitian: bool = False, backend=None): def process_fidelity(channel, target=None, check_unitary: bool = False, backend=None): - """Process fidelity between a quantum ``channel`` :math:`\\mathcal{E}` and - a ``target`` unitary channel :math:`U`. The process fidelity is defined as + """Calculate the process fidelity between a quantum channel and target unitary. + + Given a generic :math:`n`-qubit quantum channel :math:`\\mathcal{E}` + and a target :math:`n`-qubit unitary :math:`\\mathcal{U}`, + both in their Choi representation, their process fidelity is defined as .. math:: - F_{\\text{pro}}(\\mathcal{E}, \\mathcal{U}) = \\frac{1}{d^{2}} \\, - \\text{tr}(\\mathcal{E}^{\\dagger} \\, \\mathcal{U}) + \\operatorname{F}_{\\text{pro}}(\\mathcal{E}, \\, \\mathcal{U}) = + \\frac{1}{d^{2}} \\, \\text{Tr}(\\mathcal{E}^{\\dagger} \\, + \\mathcal{U}) \\, , + + where :math:`d = 2^{n}`. For more about the Choi representation of quantum channels, + please see :func:`qibo.quantum_info.to_choi`. Args: - channel: quantum channel :math:`\\mathcal{E}`. - target (optional): quantum channel :math:`U`. If ``None``, target is the - Identity channel. Defaults to ``None``. + channel: quantum channel :math:`\\mathcal{E}` in the Choi representation. + target (optional): quantum channel :math:`\\mathcal{U}` in the Choi representation. + If ``None``, target is the Identity channel. Defaults to ``None``. check_unitary (bool, optional): if ``True``, checks if one of the input channels is unitary. Default: ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -438,7 +461,7 @@ def process_fidelity(channel, target=None, check_unitary: bool = False, backend= Defaults to ``None``. Returns: - float: Process fidelity between ``channel`` and ``target``. + float: Process fidelity :math:`\\operatorname{F}_{\\text{pro}}`. """ backend = _check_backend(backend) @@ -794,7 +817,7 @@ def frame_potential( .. math:: \\mathcal{F}_{\\mathcal{U}}^{(t)} = \\int_{U,V \\in \\mathcal{U}} \\, - \\text{d}U \\, \\text{d}V \\, \\bigl| \\, \\text{tr}(U^{\\dagger} \\, V) + \\text{d}U \\, \\text{d}V \\, \\bigl| \\, \\text{Tr}(U^{\\dagger} \\, V) \\, \\bigr|^{2t} \\, , where :math:`\\mathcal{U}` is the group of unitaries defined by the parametrized circuit. @@ -802,7 +825,7 @@ def frame_potential( .. math:: \\mathcal{F}_{\\mathcal{U}}^{(t)} \\approx \\frac{1}{N} \\, - \\sum_{k=1}^{N} \\, \\bigl| \\, \\text{tr}(U_{k}^{\\dagger} \\, V_{k}) \\, \\bigr|^{2t} \\, , + \\sum_{k=1}^{N} \\, \\bigl| \\, \\text{Tr}(U_{k}^{\\dagger} \\, V_{k}) \\, \\bigr|^{2t} \\, , where :math:`N` is the number of ``samples``. From 97215775badbb29f82c7ed9e33741403a3a3154c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Fri, 8 Nov 2024 13:34:50 +0400 Subject: [PATCH 14/15] more changes --- src/qibo/quantum_info/metrics.py | 123 ++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 42 deletions(-) diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index ea432c2d89..7aa77dc1a0 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -450,9 +450,27 @@ def process_fidelity(channel, target=None, check_unitary: bool = False, backend= where :math:`d = 2^{n}`. For more about the Choi representation of quantum channels, please see :func:`qibo.quantum_info.to_choi`. + Example:: + + from qibo.quantum_info.metrics import process_fidelity + from qibo.quantum_info.random_ensembles import random_unitary, random_quantum_channel + from qibo.quantum_info.superoperator_transformations import to_choi + + nqubits = 2 + dims = 2**nqubits + + channel = random_quantum_channel(dims) + channel = to_choi(channel) + + unitary = random_unitary(dims) + unitary = to_choi(unitary) + + pro_fid = process_fidelity(channel, unitary) + + Args: - channel: quantum channel :math:`\\mathcal{E}` in the Choi representation. - target (optional): quantum channel :math:`\\mathcal{U}` in the Choi representation. + channel (ndarray): quantum channel :math:`\\mathcal{E}` in the Choi representation. + target (ndarray, optional): quantum channel :math:`\\mathcal{U}` in the Choi representation. If ``None``, target is the Identity channel. Defaults to ``None``. check_unitary (bool, optional): if ``True``, checks if one of the input channels is unitary. Default: ``False``. @@ -513,28 +531,30 @@ def process_fidelity(channel, target=None, check_unitary: bool = False, backend= def process_infidelity(channel, target=None, check_unitary: bool = False, backend=None): - """Process infidelity between quantum channel :math:`\\mathcal{E}` and a - ``target`` unitary channel :math:`U`. The process infidelity is defined as + """Calculate the process infidelity between a quantum channel and a target unitary. + + Given a generic :math:`n`-qubit quantum channel :math:`\\mathcal{E}` + and a target :math:`n`-qubit unitary :math:`\\mathcal{U}`, + both in their Choi representation, their process infidelity is defined as .. math:: - 1 - F_{\\text{pro}}(\\mathcal{E}, \\mathcal{U}) \\, , + 1 - \\operatorname{F}_{\\text{pro}}(\\mathcal{E}, \\, \\mathcal{U}) \\, , - where :math:`F_{\\text{pro}}` is the :func:`qibo.quantum_info.process_fidelity`. + where :math:`\\operatorname{F}_{\\text{pro}}` is their + :func:`qibo.quantum_info.process_fidelity`. Args: - channel: quantum channel :math:`\\mathcal{E}`. - target (optional): quantum channel :math:`U`. If ``None``, target is the - Identity channel. Defaults to ``None``. + channel (ndarray): quantum channel :math:`\\mathcal{E}` in the Choi representation. + target (ndarray, optional): quantum channel :math:`\\mathcal{U}` in the Choi representation. + If ``None``, target is the Identity channel. Defaults to ``None``. check_unitary (bool, optional): if ``True``, checks if one of the input channels is unitary. Defaults to ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend. Defaults to ``None``. - Returns: - float: Process infidelity between ``channel`` :math:`\\mathcal{E}` - and ``target`` :math:`U`. + float: Process infidelity :math:`1 - \\operatorname{F}_{\\text{pro}}`. """ return 1 - process_fidelity( channel, target=target, check_unitary=check_unitary, backend=backend @@ -544,24 +564,35 @@ def process_infidelity(channel, target=None, check_unitary: bool = False, backen def average_gate_fidelity( channel, target=None, check_unitary: bool = False, backend=None ): - """Average gate fidelity between a quantum ``channel`` :math:`\\mathcal{E}` - and a ``target`` unitary channel :math:`U`. The average gate fidelity is - defined as + """Calculate the average gate fidelity between a quantum channel and a target unitary. + + Given a generic :math:`n`-qubit quantum channel :math:`\\mathcal{E}` + and a target :math:`n`-qubit unitary :math:`\\mathcal{U}`, both in their Choi representation, + their average gate fidelity is defined as .. math:: - F_{\\text{avg}}(\\mathcal{E}, \\mathcal{U}) = \\frac{d \\, - F_{pro}(\\mathcal{E}, \\mathcal{U}) + 1}{d + 1} + \\operatorname{F}_{\\text{avg}}(\\mathcal{E}, \\, \\mathcal{U}) = + \\frac{d^{2} \\, \\operatorname{F}_{\\text{pro}}(\\mathcal{E}, + \\mathcal{U}) + 1}{d^{2} + 1} \\, , + + where :math:`d = 2^{n}`, and :math:`\\operatorname{F}_{pro}(\\mathcal{E}, \\mathcal{U})` + is the :func:`qibo.quantum_info.process_fidelily` between :math:`\\mathcal{E}` + and :math:`\\mathcal{U}`. + + Example:: + from qibo import matrices + from qibo.quantum_info import average_gate_fidelity - where :math:`d` is the dimension of the channels and - :math:`F_{pro}(\\mathcal{E}, \\mathcal{U})` is the - :meth:`~qibo.metrics.process_fidelily` of channel - :math:`\\mathcal{E}` with respect to the unitary - channel :math:`\\mathcal{U}`. + X = matrices.X + H = matrices.H + + avf - Args: - channel: quantum channel :math:`\\mathcal{E}`. - target (optional): quantum channel :math:`\\mathcal{U}`. - If ``None``, target is the Identity channel. Defaults to ``None``. + channel (ndarray): quantum channel :math:`\\mathcal{E}` in the Choi representation. + target (ndarray, optional): quantum channel :math:`\\mathcal{U}` + in the Choi representation. If ``None``, target is the Identity channel. + Defaults to ``None``. check_unitary (bool, optional): if ``True``, checks if one of the input channels is unitary. Default: ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -584,19 +615,23 @@ def average_gate_fidelity( def gate_error(channel, target=None, check_unitary: bool = False, backend=None): - """Gate error between a quantum ``channel`` :math:`\\mathcal{E}` and a - ``target`` unitary channel :math:`U`, which is defined as + """Calculate the gate error between a quantum channel and a target unitary. + + Given a generic :math:`n`-qubit quantum channel :math:`\\mathcal{E}` + and a :math:`n`-qubit target unitary :math:`\\mathcal{U}`, both in the Choi representation, + the gate error between them is defined as .. math:: - E(\\mathcal{E}, \\mathcal{U}) = 1 - F_{\\text{avg}}(\\mathcal{E}, \\mathcal{U}) \\, , + 1 - \\operatorname{F}_{\\text{avg}}(\\mathcal{E}, \\, \\mathcal{U}) \\, , - where :math:`F_{\\text{avg}}(\\mathcal{E}, \\mathcal{U})` is the + where :math:`F_{\\text{avg}}(\\mathcal{E}, \\, \\mathcal{U})` is the :func:`qibo.quantum_info.average_gate_fidelity`. Args: - channel: quantum channel :math:`\\mathcal{E}`. - target (optional): quantum channel :math:`\\mathcal{U}`. If ``None``, - target is the Identity channel. Defaults to ``None``. + channel (ndarray): quantum channel :math:`\\mathcal{E}` in the Choi representation. + target (ndarray, optional): quantum channel :math:`\\mathcal{U}` + in the Choi representation. If ``None``, target is the Identity channel. + Defaults to ``None``. check_unitary (bool, optional): if ``True``, checks if one of the input channels is unitary. Default: ``False``. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used @@ -604,8 +639,7 @@ def gate_error(channel, target=None, check_unitary: bool = False, backend=None): Defaults to ``None``. Returns: - float: Gate error between ``channel`` :math:`\\mathcal{E}` - and ``target`` :math:`\\mathcal{U}`. + float: Gate error :math:`1 - \\operatorname{F}_{\\text{avg}}`. """ error = 1 - average_gate_fidelity( channel, target, check_unitary=check_unitary, backend=backend @@ -615,18 +649,23 @@ def gate_error(channel, target=None, check_unitary: bool = False, backend=None): def diamond_norm(channel, target=None, backend=None, **kwargs): # pragma: no cover - """Calculates the diamond norm :math:`\\|\\mathcal{E}\\|_{\\diamond}` of - ``channel`` :math:`\\mathcal{E}`, which is given by + """Calculate the diamond of a quantum channel or the diamind distance between two channels. + + Given a :math:`n`-qubit quantum channel :math:`\\mathcal{E} \\in \\mathbb{C}^{d}`, + its diamond norm is defined as .. math:: - \\|\\mathcal{E}\\|_{\\diamond} = \\max_{\\rho} \\, \\| \\left(\\mathcal{E} \\otimes I_{d^{2}}\\right)(\\rho) \\|_{1} \\, , + \\|\\mathcal{E}\\|_{\\diamond} = \\max_{\\rho\\in\\mathbb{C}^{d^{2}}} \\, + \\| \\left(\\mathcal{E} \\otimes I_{d}\\right)(\\rho) \\|_{1} \\, , + + where :math:`d = 2^{n}`, :math:`I_{d}` is the :math:`d \\times d` Identity operator, + and :math:`\\|\\cdot\\|_{1}` denotes the Schatten :math:`1`-norm. - where :math:`I_{d^{2}}` is the :math:`d^{2} \\times d^{2}` Identity operator, - :math:`d = 2^{n}`, :math:`n` is the number of qubits, - and :math:`\\|\\cdot\\|_{1}` denotes the trace norm. + If a ``target`` channel :math:`\\Lambda` is specified, then the function calculates + the diamond distance between the them, *i.e.* - If a ``target`` channel :math:`\\Lambda` is specified, - then it calculates :math:`\\| \\mathcal{E} - \\Lambda\\|_{\\diamond}`. + .. math:: + \\| \\mathcal{E} - \\Lambda\\|_{\\diamond} \\, . Example:: From 574105814030f65a7dbf17265b49fb943c1466b3 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 11 Nov 2024 10:00:50 +0400 Subject: [PATCH 15/15] add example --- src/qibo/quantum_info/metrics.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index 7aa77dc1a0..676fe33ce3 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -581,12 +581,15 @@ def average_gate_fidelity( Example:: from qibo import matrices - from qibo.quantum_info import average_gate_fidelity + from qibo.quantum_info import average_gate_fidelity, to_choi + # The import above is equivalent to + # from qibo.quantum_info.metrics import average_gate_fidelity + # from qibo.quantum_info.superoperator_transformations import to_choi - X = matrices.X - H = matrices.H + X_choi = to_choi(matrices.X) + H_choi = to_choi(matrices.H) - avf - + agf = average_gate_fidelity(X_choi, H_choi) Args: channel (ndarray): quantum channel :math:`\\mathcal{E}` in the Choi representation. @@ -792,8 +795,10 @@ def expressibility( order: Optional[Union[int, float, str]] = 2, backend=None, ): - """Returns the expressibility :math:`\\|A\\|` of a parametrized circuit, - where + """Returns the expressibility of a parametrized circuit. + + + The expressibility of a parametrized circuits is defined as :math:`\\|A\\|`, where .. math:: A = \\int_{\\text{Haar}} d\\psi \\, \\left(|\\psi\\rangle\\right.\\left. @@ -806,7 +811,8 @@ def expressibility( power_t (int): power that defines the :math:`t`-design. samples (int): number of samples to estimate the integrals. order (int or float or str, optional): order of the norm :math:`\\|A\\|`. - For specifications, see :meth:`qibo.backends.abstract.calculate_norm`. + For specifications, see + :meth:`qibo.backends.abstract.Backend.calculate_norm_density_matrix`. Defaults to :math:`2`. backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be used in the execution. If ``None``, it uses the current backend.