From 53d769357b82ae8e0757500de3cb6fbaa6269c9a Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 19 Sep 2024 12:59:34 +0400 Subject: [PATCH 1/6] function --- src/qibo/quantum_info/entanglement.py | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 23c2f7d3f2..c566ee3a33 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -4,7 +4,7 @@ from qibo.backends import _check_backend from qibo.config import PRECISION_TOL, raise_error -from qibo.quantum_info.linalg_operations import partial_trace +from qibo.quantum_info.linalg_operations import matrix_power, partial_trace from qibo.quantum_info.metrics import fidelity, purity @@ -116,6 +116,39 @@ def entanglement_of_formation( return ent_of_form +def negativity(state, bipartition, backend=None): + """Calculates 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) \\, , + + 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 + (also known as nuclear norm or trace norm). + + Args: + state (ndarray): statevector or density matrix. + 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 :class:`qibo.backends.GlobalBackend`. + Defaults to ``None``. + + Returns: + float: Negativity :math:`\\operatorname{Neg}(\\rho)` of state :math:`\\rho`. + """ + backend = _check_backend(backend) + + reduced = partial_trace(state, bipartition, backend) + reduced = backend.np.conj(reduced.T) @ reduced + norm = backend.np.trace(matrix_power(reduced, 1 / 2, backend)) + + return float((norm - 1) / 2) + + def entanglement_fidelity( channel, nqubits: int, state=None, check_hermitian: bool = False, backend=None ): From 0b8bf4a33fa957e288593d32884afc4d03c04787 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:21:15 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibo/quantum_info/entanglement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index c566ee3a33..9ddb4f78fc 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -123,10 +123,10 @@ def negativity(state, bipartition, backend=None): the negativity :math:`\\operatorname{Neg}(\\rho)` is given by .. math:: - \\operatorname{Neg}(\\rho) = \\frac{1}{2} \\, + \\operatorname{Neg}(\\rho) = \\frac{1}{2} \\, \\left( \\norm{\\rho_{B}}_{1} - 1 \\right) \\, , - where :math:`\\rho_{B}` is the reduced density matrix after tracing out qubits in + 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 (also known as nuclear norm or trace norm). From 1e9f1e30d076de6081248458accff97ff68acc79 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 19 Sep 2024 13:39:04 +0400 Subject: [PATCH 3/6] api ref --- doc/source/api-reference/qibo.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index d877c4c28d..4c69c142fc 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -1704,6 +1704,12 @@ Entanglement of formation .. autofunction:: qibo.quantum_info.entanglement_of_formation +Negativity +"""""""""" + +.. autofunction:: qibo.quantum_info.negativity + + Entanglement fidelity """"""""""""""""""""" From 68e97991c6c9e6644afd027db8966be442c153b4 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 23 Sep 2024 11:09:53 +0400 Subject: [PATCH 4/6] backup --- src/qibo/quantum_info/entanglement.py | 6 ++++-- tests/test_quantum_info_entanglement.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 9ddb4f78fc..1cae07447b 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -141,12 +141,14 @@ def negativity(state, bipartition, backend=None): float: Negativity :math:`\\operatorname{Neg}(\\rho)` of state :math:`\\rho`. """ backend = _check_backend(backend) - + print(state) reduced = partial_trace(state, bipartition, backend) + print() + print(reduced) reduced = backend.np.conj(reduced.T) @ reduced norm = backend.np.trace(matrix_power(reduced, 1 / 2, backend)) - return float((norm - 1) / 2) + return float(backend.np.real((norm - 1) / 2)) def entanglement_fidelity( diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index a641d01c94..a989195f6b 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -9,6 +9,7 @@ entanglement_of_formation, entangling_capability, meyer_wallach_entanglement, + negativity, ) from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector @@ -66,6 +67,25 @@ def test_concurrence_and_formation(backend, bipartition, base, check_purity): backend.assert_allclose(ent_form, 0.0, atol=PRECISION_TOL) +@pytest.mark.parametrize("p", [1 / 5, 1 / 3 + 0.01, 1]) +def test_negativity(backend, p): + # werner state + zero, one = np.array([1, 0]), np.array([0, 1]) + psi = (np.kron(zero, one) - np.kron(one, zero)) / np.sqrt(2) + psi = np.outer(psi, psi.T) + psi = backend.cast(psi) + state = p * psi + (1 - p) * backend.identity_density_matrix(2, normalize=True) + + neg = negativity(state, [0]) + + if p == 1 / 5: + target = 0.0 + else: + target = 1.0 + + assert neg == target + + @pytest.mark.parametrize("check_hermitian", [False, True]) @pytest.mark.parametrize("nqubits", [4, 6]) @pytest.mark.parametrize("channel", [gates.DepolarizingChannel]) From bc4b7c7c73fad0744d4efc640b937745e8cb19f5 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 23 Sep 2024 14:51:07 +0400 Subject: [PATCH 5/6] remove prints --- src/qibo/quantum_info/entanglement.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 1cae07447b..792f52a82a 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -4,7 +4,11 @@ from qibo.backends import _check_backend from qibo.config import PRECISION_TOL, raise_error -from qibo.quantum_info.linalg_operations import matrix_power, partial_trace +from qibo.quantum_info.linalg_operations import ( + matrix_power, + partial_trace, + partial_transpose, +) from qibo.quantum_info.metrics import fidelity, purity @@ -141,10 +145,8 @@ def negativity(state, bipartition, backend=None): float: Negativity :math:`\\operatorname{Neg}(\\rho)` of state :math:`\\rho`. """ backend = _check_backend(backend) - print(state) - reduced = partial_trace(state, bipartition, backend) - print() - print(reduced) + + reduced = partial_transpose(state, bipartition, backend) reduced = backend.np.conj(reduced.T) @ reduced norm = backend.np.trace(matrix_power(reduced, 1 / 2, backend)) From 625870c37fcdcd1dee76386879ead8221e8183e4 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 23 Sep 2024 14:51:13 +0400 Subject: [PATCH 6/6] tests --- tests/test_quantum_info_entanglement.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index a989195f6b..cc79ea1ff8 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -67,7 +67,7 @@ def test_concurrence_and_formation(backend, bipartition, base, check_purity): backend.assert_allclose(ent_form, 0.0, atol=PRECISION_TOL) -@pytest.mark.parametrize("p", [1 / 5, 1 / 3 + 0.01, 1]) +@pytest.mark.parametrize("p", [1 / 5, 1 / 3 + 0.01, 1.0]) def test_negativity(backend, p): # werner state zero, one = np.array([1, 0]), np.array([0, 1]) @@ -76,14 +76,16 @@ def test_negativity(backend, p): psi = backend.cast(psi) state = p * psi + (1 - p) * backend.identity_density_matrix(2, normalize=True) - neg = negativity(state, [0]) + neg = negativity(state, [0], backend=backend) if p == 1 / 5: target = 0.0 + elif p == 1.0: + target = 1 / 2 else: - target = 1.0 + target = 3 / 400 - assert neg == target + backend.assert_allclose(neg, target, atol=1e-10) @pytest.mark.parametrize("check_hermitian", [False, True])