From d1d008d4c113df069c15a55368e8d77e204b7dc9 Mon Sep 17 00:00:00 2001 From: Alejandro Sopena Date: Tue, 17 Sep 2024 08:33:47 +0200 Subject: [PATCH 01/10] computer meyer_wallach with states --- src/qibo/quantum_info/entanglement.py | 29 +++++++++++++++++-------- tests/test_quantum_info_entanglement.py | 6 +++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 23c2f7d3f2..daac7ad0c6 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -193,15 +193,18 @@ def entanglement_fidelity( return entang_fidelity -def meyer_wallach_entanglement(circuit, backend=None): - """Computes the Meyer-Wallach entanglement Q of the `circuit`, +def meyer_wallach_entanglement(state, backend=None): + """Computes the Meyer-Wallach entanglement Q of `state`, .. math:: Q(\\theta) = 1 - \\frac{1}{N} \\, \\sum_{k} \\, - \\text{tr}\\left(\\rho_{k^{2}}(\\theta)\\right) \\, . + \\text{tr}\\left(\\rho_{k^{2}}\\right) \\, , + + where :math:`\\rho_{k^{2}}` is the reduced density matrix obtained by tracing out qubit :math:`k` + from the total system. Args: - circuit (:class:`qibo.models.Circuit`): Parametrized circuit. + state (ndarray): statevector or density matrix. 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``. @@ -212,17 +215,24 @@ def meyer_wallach_entanglement(circuit, backend=None): backend = _check_backend(backend) - circuit.density_matrix = True - nqubits = circuit.nqubits + if ( + (len(state.shape) not in [1, 2]) + or (len(state) == 0) + or (len(state.shape) == 2 and state.shape[0] != state.shape[1]) + ): + raise_error( + TypeError, + f"state must have dims either (k,) or (k,k), but have dims {state.shape}.", + ) - rho = backend.execute_circuit(circuit).state() + nqubits = int(np.log2(len(state))) if len(state.shape) == 1 else state.shape[0] ent = 0 for j in range(nqubits): trace_q = list(range(nqubits)) trace_q.pop(j) - rho_r = partial_trace(rho, trace_q, backend=backend) + rho_r = partial_trace(state, trace_q, backend=backend) trace = purity(rho_r, backend=backend) @@ -280,7 +290,8 @@ def entangling_capability(circuit, samples: int, seed=None, backend=None): for _ in range(samples): params = local_state.uniform(-np.pi, np.pi, circuit.trainable_gates.nparams) circuit.set_parameters(params) - entanglement = meyer_wallach_entanglement(circuit, backend=backend) + state = backend.execute_circuit(circuit).state() + entanglement = meyer_wallach_entanglement(state, backend=backend) res.append(entanglement) capability = 2 * np.real(np.sum(res)) / samples diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index a641d01c94..e7b91b1c97 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -130,17 +130,19 @@ def test_meyer_wallach_entanglement(backend): circuit1 = Circuit(nqubits) circuit1.add([gates.RX(0, np.pi / 4)] for _ in range(nqubits)) + state1 = backend.execute_circuit(circuit1).state() circuit2 = Circuit(nqubits) circuit2.add([gates.RX(0, np.pi / 4)] for _ in range(nqubits)) circuit2.add(gates.CNOT(0, 1)) + state2 = backend.execute_circuit(circuit2).state() backend.assert_allclose( - meyer_wallach_entanglement(circuit1, backend=backend), 0.0, atol=PRECISION_TOL + meyer_wallach_entanglement(state1, backend=backend), 0.0, atol=PRECISION_TOL ) backend.assert_allclose( - meyer_wallach_entanglement(circuit2, backend=backend), 0.5, atol=PRECISION_TOL + meyer_wallach_entanglement(state2, backend=backend), 0.5, atol=PRECISION_TOL ) From a4e9a2a373ed1a5afc98fd86802fb5127a321b7c Mon Sep 17 00:00:00 2001 From: Alejandro Sopena Date: Tue, 17 Sep 2024 09:09:51 +0200 Subject: [PATCH 02/10] fix coverage --- tests/test_quantum_info_entanglement.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index e7b91b1c97..471615b749 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -126,6 +126,11 @@ def test_entanglement_fidelity(backend, channel, nqubits, check_hermitian): def test_meyer_wallach_entanglement(backend): + with pytest.raises(TypeError): + state = np.random.rand(2, 3, 2) + state = backend.cast(state, dtype=state.dtype) + test = meyer_wallach_entanglement(state, backend=backend) + nqubits = 2 circuit1 = Circuit(nqubits) From 51e455e9260af7d8099728610833dcd5cc4ce07d Mon Sep 17 00:00:00 2001 From: Alejandro Sopena Date: Tue, 17 Sep 2024 09:40:35 +0200 Subject: [PATCH 03/10] update documentation --- src/qibo/quantum_info/entanglement.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index daac7ad0c6..256876ab98 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -194,14 +194,14 @@ def entanglement_fidelity( def meyer_wallach_entanglement(state, backend=None): - """Computes the Meyer-Wallach entanglement Q of `state`, + """Computes the Meyer-Wallach entanglement :math:`Q` of a ``state``, .. math:: Q(\\theta) = 1 - \\frac{1}{N} \\, \\sum_{k} \\, - \\text{tr}\\left(\\rho_{k^{2}}\\right) \\, , + \\text{tr}\\left(\\rho_{k}^{2}\\right) \\, , - where :math:`\\rho_{k^{2}}` is the reduced density matrix obtained by tracing out qubit :math:`k` - from the total system. + where :math:`\\rho_{k}^{2}` is the reduced density matrix obtained by tracing out qubit :math:`k`, + and :math:`N` is the total number of qubits in ``state``. Args: state (ndarray): statevector or density matrix. @@ -245,16 +245,16 @@ def meyer_wallach_entanglement(state, backend=None): def entangling_capability(circuit, samples: int, seed=None, backend=None): """Returns the entangling capability :math:`\\text{Ent}` of a parametrized - circuit, which is average Meyer-Wallach entanglement Q of the circuit, i.e. + circuit, defined as the average Meyer-Wallach entanglement :math:`Q` of the ``circuit``, i.e. .. math:: - \\text{Ent} = \\frac{2}{S}\\sum_{k}Q_k \\, , + \\text{Ent} = \\frac{2}{|S|}\\sum_{\\theta_i\\in S}Q_{\\theta_i} \\, , - where :math:`S` is the number of samples. + where :math:`S` is the set of sampled circuit parameters. Args: circuit (:class:`qibo.models.Circuit`): Parametrized circuit. - samples (int): number of samples to estimate the integral. + samples (int): number of sampled circuit parameter vectors :math:`|S|` seed (int or :class:`numpy.random.Generator`, optional): Either a generator of random numbers or a fixed seed to initialize a generator. If ``None``, initializes a generator with a random seed. Default: ``None``. From cdb2612819e4be2283e71a0b8fbe4aaa5e435531 Mon Sep 17 00:00:00 2001 From: Alejandro Sopena Date: Wed, 2 Oct 2024 05:19:37 +0200 Subject: [PATCH 04/10] adding references to docstrings --- src/qibo/quantum_info/entanglement.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 256876ab98..184ed4f443 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -197,11 +197,14 @@ def meyer_wallach_entanglement(state, backend=None): """Computes the Meyer-Wallach entanglement :math:`Q` of a ``state``, .. math:: - Q(\\theta) = 1 - \\frac{1}{N} \\, \\sum_{k} \\, - \\text{tr}\\left(\\rho_{k}^{2}\\right) \\, , + Q(\\rho) = 2\\left(1 - \\frac{1}{N} \\, \\sum_{k} \\, + \\text{tr}\\left(\\rho_{k}^{2}\\right)\\right) \\, , - where :math:`\\rho_{k}^{2}` is the reduced density matrix obtained by tracing out qubit :math:`k`, + 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 (2001) `_. Args: state (ndarray): statevector or density matrix. @@ -238,7 +241,7 @@ def meyer_wallach_entanglement(state, backend=None): ent += trace - entanglement = 1 - ent / nqubits + entanglement = 2 * (1 - ent / nqubits) return entanglement @@ -248,9 +251,10 @@ def entangling_capability(circuit, samples: int, seed=None, backend=None): circuit, defined as the average Meyer-Wallach entanglement :math:`Q` of the ``circuit``, i.e. .. math:: - \\text{Ent} = \\frac{2}{|S|}\\sum_{\\theta_i\\in S}Q_{\\theta_i} \\, , + \\text{Ent} = \\frac{2}{|S|}\\sum_{\\theta_i\\in S}Q(\\rho_i) \\, , - where :math:`S` is the set of sampled circuit parameters. + where :math:`S` is the set of sampled circuit parameters, and :math:`\\rho_i` is the + state prepared by the circuit with parameters :math:`\\theta_i`. Args: circuit (:class:`qibo.models.Circuit`): Parametrized circuit. From 36195397bc155d860fc9d74627d57d672ae896b0 Mon Sep 17 00:00:00 2001 From: Alejandro Sopena Date: Wed, 2 Oct 2024 05:19:49 +0200 Subject: [PATCH 05/10] fix test --- tests/test_quantum_info_entanglement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_quantum_info_entanglement.py b/tests/test_quantum_info_entanglement.py index 471615b749..01a08cd3ca 100644 --- a/tests/test_quantum_info_entanglement.py +++ b/tests/test_quantum_info_entanglement.py @@ -147,7 +147,7 @@ def test_meyer_wallach_entanglement(backend): ) backend.assert_allclose( - meyer_wallach_entanglement(state2, backend=backend), 0.5, atol=PRECISION_TOL + meyer_wallach_entanglement(state2, backend=backend), 1, atol=PRECISION_TOL ) From 3e4d376ec529e88a364a2286b74674f9d6734393 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 7 Oct 2024 11:20:49 +0400 Subject: [PATCH 06/10] tidying up --- src/qibo/quantum_info/entanglement.py | 48 ++++++++++++++++----------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index 184ed4f443..c0c8ba51d0 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -194,7 +194,7 @@ def entanglement_fidelity( def meyer_wallach_entanglement(state, backend=None): - """Computes the Meyer-Wallach entanglement :math:`Q` of a ``state``, + """Compute the Meyer-Wallach entanglement :math:`Q` of a ``state``, .. math:: Q(\\rho) = 2\\left(1 - \\frac{1}{N} \\, \\sum_{k} \\, @@ -203,8 +203,9 @@ def meyer_wallach_entanglement(state, backend=None): 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 (2001) `_. + proposed in `Brennen (2003) `_, + which is equivalent to the definition introduced in `Meyer and Wallach (2002) + `_. Args: state (ndarray): statevector or density matrix. @@ -213,7 +214,16 @@ def meyer_wallach_entanglement(state, backend=None): Defaults to ``None``. Returns: - float: Meyer-Wallach entanglement. + float: Meyer-Wallach entanglement :math:`Q`. + + + References: + 1. G. K. Brennen, *An observable measure of entanglement for pure states of + multi-qubit systems*, `Quantum Information and Computation, vol. 3 (6), 619-626 + `_ (2003). + + 2. D. A. Meyer and N. R. Wallach, * Global entanglement in multiparticle systems* + `J. Math. Phys. 43, 4273–4278 `_ (2002). """ backend = _check_backend(backend) @@ -230,7 +240,7 @@ def meyer_wallach_entanglement(state, backend=None): nqubits = int(np.log2(len(state))) if len(state.shape) == 1 else state.shape[0] - ent = 0 + entanglement = 0 for j in range(nqubits): trace_q = list(range(nqubits)) trace_q.pop(j) @@ -239,22 +249,24 @@ def meyer_wallach_entanglement(state, backend=None): trace = purity(rho_r, backend=backend) - ent += trace + entanglement += trace - entanglement = 2 * (1 - ent / nqubits) - - return entanglement + return 2 * (1 - entanglement / nqubits) def entangling_capability(circuit, samples: int, seed=None, backend=None): - """Returns the entangling capability :math:`\\text{Ent}` of a parametrized - circuit, defined as the average Meyer-Wallach entanglement :math:`Q` of the ``circuit``, i.e. + """Return 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. .. math:: - \\text{Ent} = \\frac{2}{|S|}\\sum_{\\theta_i\\in S}Q(\\rho_i) \\, , + \\text{Ent} = \\frac{2}{|\\mathcal{S}|}\\sum_{\\theta_{k} \\in \\mathcal{S}} + \\, Q(\\rho_{k}) \\, , - where :math:`S` is the set of sampled circuit parameters, and :math:`\\rho_i` is the - state prepared by the circuit with parameters :math:`\\theta_i`. + 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}`. Args: circuit (:class:`qibo.models.Circuit`): Parametrized circuit. @@ -290,14 +302,12 @@ def entangling_capability(circuit, samples: int, seed=None, backend=None): np.random.default_rng(seed) if seed is None or isinstance(seed, int) else seed ) - res = [] + capability = [] for _ in range(samples): params = local_state.uniform(-np.pi, np.pi, circuit.trainable_gates.nparams) circuit.set_parameters(params) state = backend.execute_circuit(circuit).state() entanglement = meyer_wallach_entanglement(state, backend=backend) - res.append(entanglement) - - capability = 2 * np.real(np.sum(res)) / samples + capability.append(entanglement) - return capability + return 2 * np.real(np.sum(capability)) / samples From 430da1a4b832f7d6949b09c250b329a6e0644894 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 07:21:17 +0000 Subject: [PATCH 07/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibo/quantum_info/entanglement.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index c0c8ba51d0..af4fe3ffcd 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -203,8 +203,8 @@ def meyer_wallach_entanglement(state, backend=None): 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) + proposed in `Brennen (2003) `_, + which is equivalent to the definition introduced in `Meyer and Wallach (2002) `_. Args: @@ -218,11 +218,11 @@ def meyer_wallach_entanglement(state, backend=None): References: - 1. G. K. Brennen, *An observable measure of entanglement for pure states of + 1. G. K. Brennen, *An observable measure of entanglement for pure states of multi-qubit systems*, `Quantum Information and Computation, vol. 3 (6), 619-626 `_ (2003). - 2. D. A. Meyer and N. R. Wallach, * Global entanglement in multiparticle systems* + 2. D. A. Meyer and N. R. Wallach, * Global entanglement in multiparticle systems* `J. Math. Phys. 43, 4273–4278 `_ (2002). """ @@ -256,16 +256,16 @@ 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. - - It is defined as the average Meyer-Wallach entanglement :math:`Q` + + It is defined as the average Meyer-Wallach entanglement :math:`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}} + \\text{Ent} = \\frac{2}{|\\mathcal{S}|}\\sum_{\\theta_{k} \\in \\mathcal{S}} \\, Q(\\rho_{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 + 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}`. Args: From c539855904bb58c0cb4265ab2bd00d9b8b487196 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 7 Oct 2024 13:57:19 +0400 Subject: [PATCH 08/10] better notation --- src/qibo/quantum_info/linalg_operations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/linalg_operations.py b/src/qibo/quantum_info/linalg_operations.py index 6fc48e6e74..5408934244 100644 --- a/src/qibo/quantum_info/linalg_operations.py +++ b/src/qibo/quantum_info/linalg_operations.py @@ -149,8 +149,8 @@ def partial_trace(state, traced_qubits: Union[List[int], Tuple[int]], backend=No return backend.np.reshape(rho, shape) order = tuple(sorted(traced_qubits)) - order += tuple(i for i in range(nqubits) if i not in traced_qubits) - order += tuple(i + nqubits for i in order) + order += tuple(set(list(range(nqubits))) ^ set(traced_qubits)) + order += tuple(k + nqubits for k in order) shape = 2 * (2 ** len(traced_qubits), 2 ** (nqubits - len(traced_qubits))) state = backend.np.transpose(state, order) From 70100ef3a801a61f960ac0e0ab5fc05206000a29 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 7 Oct 2024 13:57:36 +0400 Subject: [PATCH 09/10] fix bug and add note --- src/qibo/quantum_info/entanglement.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index af4fe3ffcd..a1b3999074 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -238,7 +238,7 @@ def meyer_wallach_entanglement(state, backend=None): f"state must have dims either (k,) or (k,k), but have dims {state.shape}.", ) - nqubits = int(np.log2(len(state))) if len(state.shape) == 1 else state.shape[0] + nqubits = int(np.log2(state.shape[-1])) entanglement = 0 for j in range(nqubits): @@ -268,6 +268,9 @@ def entangling_capability(circuit, samples: int, seed=None, backend=None): 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. + Args: circuit (:class:`qibo.models.Circuit`): Parametrized circuit. samples (int): number of sampled circuit parameter vectors :math:`|S|` @@ -279,7 +282,7 @@ def entangling_capability(circuit, samples: int, seed=None, backend=None): Defaults to ``None``. Returns: - float: Entangling capability. + float: Entangling capability :math:`\\text{Ent}`. """ if not isinstance(samples, int): From 47199291aacfb448694764341d176ce5873a5bd7 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 7 Oct 2024 14:03:03 +0400 Subject: [PATCH 10/10] fix docstring --- src/qibo/quantum_info/entanglement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/entanglement.py b/src/qibo/quantum_info/entanglement.py index a1b3999074..cc79980bae 100644 --- a/src/qibo/quantum_info/entanglement.py +++ b/src/qibo/quantum_info/entanglement.py @@ -222,7 +222,7 @@ def meyer_wallach_entanglement(state, backend=None): multi-qubit systems*, `Quantum Information and Computation, vol. 3 (6), 619-626 `_ (2003). - 2. D. A. Meyer and N. R. Wallach, * Global entanglement in multiparticle systems* + 2. D. A. Meyer and N. R. Wallach, *Global entanglement in multiparticle systems*, `J. Math. Phys. 43, 4273–4278 `_ (2002). """