From 8c0457e012e1db381d3e61afc5f5bfbe02936fe2 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 16 Sep 2024 15:13:28 +0400 Subject: [PATCH 01/13] add flag to method --- src/qibo/backends/abstract.py | 4 ++-- src/qibo/backends/numpy.py | 4 ++-- src/qibo/backends/pytorch.py | 4 ++-- src/qibo/backends/tensorflow.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qibo/backends/abstract.py b/src/qibo/backends/abstract.py index abe1f20d83..5be8dd479d 100644 --- a/src/qibo/backends/abstract.py +++ b/src/qibo/backends/abstract.py @@ -318,12 +318,12 @@ def calculate_overlap_density_matrix(self, state1, state2): # pragma: no cover raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvalues(self, matrix, k=6): # pragma: no cover + def calculate_eigenvalues(self, matrix, k=6, hermitian: bool = True): # pragma: no cover """Calculate eigenvalues of a matrix.""" raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvectors(self, matrix, k=6): # pragma: no cover + def calculate_eigenvectors(self, matrix, k=6, hermitian: bool = True): # pragma: no cover """Calculate eigenvectors of a matrix.""" raise_error(NotImplementedError) diff --git a/src/qibo/backends/numpy.py b/src/qibo/backends/numpy.py index bcde6ac013..d1bf8a6bf2 100644 --- a/src/qibo/backends/numpy.py +++ b/src/qibo/backends/numpy.py @@ -719,7 +719,7 @@ def calculate_overlap_density_matrix(self, state1, state2): self.np.matmul(self.np.conj(self.cast(state1)).T, self.cast(state2)) ) - def calculate_eigenvalues(self, matrix, k=6, hermitian=True): + def calculate_eigenvalues(self, matrix, k: int = 6, hermitian: bool = True): if self.is_sparse(matrix): log.warning( "Calculating sparse matrix eigenvectors because " @@ -730,7 +730,7 @@ def calculate_eigenvalues(self, matrix, k=6, hermitian=True): return np.linalg.eigvalsh(matrix) return np.linalg.eigvals(matrix) - def calculate_eigenvectors(self, matrix, k=6, hermitian=True): + def calculate_eigenvectors(self, matrix, k: int = 6, hermitian: bool = True): if self.is_sparse(matrix): if k < matrix.shape[0]: from scipy.sparse.linalg import eigsh diff --git a/src/qibo/backends/pytorch.py b/src/qibo/backends/pytorch.py index 392aeed405..529aa86df9 100644 --- a/src/qibo/backends/pytorch.py +++ b/src/qibo/backends/pytorch.py @@ -176,12 +176,12 @@ def sample_shots(self, probabilities, nshots): self.cast(probabilities, dtype="float"), nshots, replacement=True ) - def calculate_eigenvalues(self, matrix, k=6, hermitian=True): + def calculate_eigenvalues(self, matrix, k: int = 6, hermitian: bool = True): if hermitian: return self.np.linalg.eigvalsh(matrix) # pylint: disable=not-callable return self.np.linalg.eigvals(matrix) # pylint: disable=not-callable - def calculate_eigenvectors(self, matrix, k=6, hermitian=True): + def calculate_eigenvectors(self, matrix, k: int = 6, hermitian: int = True): if hermitian: return self.np.linalg.eigh(matrix) # pylint: disable=not-callable return self.np.linalg.eig(matrix) # pylint: disable=not-callable diff --git a/src/qibo/backends/tensorflow.py b/src/qibo/backends/tensorflow.py index 6f4deed49f..2088cd69be 100644 --- a/src/qibo/backends/tensorflow.py +++ b/src/qibo/backends/tensorflow.py @@ -177,12 +177,12 @@ def calculate_norm_density_matrix(self, state, order="nuc"): return self.np.trace(state) return self.tf.norm(state, ord=order) - def calculate_eigenvalues(self, matrix, k=6, hermitian=True): + def calculate_eigenvalues(self, matrix, k: int = 6, hermitian: bool = True): if hermitian: return self.tf.linalg.eigvalsh(matrix) return self.tf.linalg.eigvals(matrix) - def calculate_eigenvectors(self, matrix, k=6, hermitian=True): + def calculate_eigenvectors(self, matrix, k: int = 6, hermitian: bool = True): if hermitian: return self.tf.linalg.eigh(matrix) return self.tf.linalg.eig(matrix) From 34bc3010df981ee9544be706c375e867e982902c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 16 Sep 2024 15:29:08 +0400 Subject: [PATCH 02/13] rename function and remove error --- src/qibo/quantum_info/entropies.py | 8 ++++---- src/qibo/quantum_info/metrics.py | 22 +++------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index c9c889b603..f50ea3d085 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -9,7 +9,7 @@ from qibo.backends.pytorch import PyTorchBackend from qibo.config import PRECISION_TOL, raise_error from qibo.quantum_info.linalg_operations import partial_trace -from qibo.quantum_info.metrics import _check_hermitian_or_not_gpu, purity +from qibo.quantum_info.metrics import _check_hermitian, purity def shannon_entropy(prob_dist, base: float = 2, backend=None): @@ -459,7 +459,7 @@ def von_neumann_entropy( eigenvalues = backend.calculate_eigenvalues( state, hermitian=( - not check_hermitian or _check_hermitian_or_not_gpu(state, backend=backend) + not check_hermitian or _check_hermitian(state, backend=backend) ), ) @@ -550,13 +550,13 @@ def relative_von_neumann_entropy( eigenvalues_state = backend.calculate_eigenvalues( state, hermitian=( - not check_hermitian or _check_hermitian_or_not_gpu(state, backend=backend) + not check_hermitian or _check_hermitian(state, backend=backend) ), ) eigenvalues_target = backend.calculate_eigenvalues( target, hermitian=( - not check_hermitian or _check_hermitian_or_not_gpu(target, backend=backend) + not check_hermitian or _check_hermitian(target, backend=backend) ), ) diff --git a/src/qibo/quantum_info/metrics.py b/src/qibo/quantum_info/metrics.py index 4699efbda6..c6b78f46c8 100644 --- a/src/qibo/quantum_info/metrics.py +++ b/src/qibo/quantum_info/metrics.py @@ -234,7 +234,7 @@ def fidelity(state, target, check_hermitian: bool = False, backend=None): abs(purity_state - 1) > PRECISION_TOL and abs(purity_target - 1) > PRECISION_TOL ): - hermitian = check_hermitian is False or _check_hermitian_or_not_gpu( + hermitian = check_hermitian is False or _check_hermitian( state, backend=backend ) # using eigh since rho is supposed to be Hermitian @@ -812,10 +812,8 @@ def frame_potential( return potential / samples**2 -def _check_hermitian_or_not_gpu(matrix, backend=None): - """Checks if a given matrix is Hermitian and whether the backend is neither - :class:`qibojit.backends.CupyBackend` nor - :class:`qibojit.backends.CuQuantumBackend`. +def _check_hermitian(matrix, backend=None): + """Checks if a given matrix is Hermitian. Args: matrix: input array. @@ -825,10 +823,6 @@ def _check_hermitian_or_not_gpu(matrix, backend=None): Returns: bool: whether the matrix is Hermitian. - - Raises: - NotImplementedError: If `matrix` is not Hermitian and - `backend` is not :class:`qibojit.backends.CupyBackend` """ backend = _check_backend(backend) @@ -838,14 +832,4 @@ def _check_hermitian_or_not_gpu(matrix, backend=None): hermitian = bool(float(norm) <= PRECISION_TOL) - if hermitian is False and backend.__class__.__name__ in [ - "CupyBackend", - "CuQuantumBackend", - ]: # pragma: no cover - raise_error( - NotImplementedError, - "GPU backends do not support `np.linalg.eig` " - + "or `np.linalg.eigvals` for non-Hermitian matrices.", - ) - return hermitian From 57fb1036adb1650b791b540d571637f5206dd815 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:35:36 +0000 Subject: [PATCH 03/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibo/backends/abstract.py | 8 ++++++-- src/qibo/quantum_info/entropies.py | 12 +++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/qibo/backends/abstract.py b/src/qibo/backends/abstract.py index 5be8dd479d..d007021d25 100644 --- a/src/qibo/backends/abstract.py +++ b/src/qibo/backends/abstract.py @@ -318,12 +318,16 @@ def calculate_overlap_density_matrix(self, state1, state2): # pragma: no cover raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvalues(self, matrix, k=6, hermitian: bool = True): # pragma: no cover + def calculate_eigenvalues( + self, matrix, k=6, hermitian: bool = True + ): # pragma: no cover """Calculate eigenvalues of a matrix.""" raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvectors(self, matrix, k=6, hermitian: bool = True): # pragma: no cover + def calculate_eigenvectors( + self, matrix, k=6, hermitian: bool = True + ): # pragma: no cover """Calculate eigenvectors of a matrix.""" raise_error(NotImplementedError) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index f50ea3d085..cd815189e4 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -458,9 +458,7 @@ def von_neumann_entropy( eigenvalues = backend.calculate_eigenvalues( state, - hermitian=( - not check_hermitian or _check_hermitian(state, backend=backend) - ), + hermitian=(not check_hermitian or _check_hermitian(state, backend=backend)), ) log_prob = backend.np.where( @@ -549,15 +547,11 @@ def relative_von_neumann_entropy( eigenvalues_state = backend.calculate_eigenvalues( state, - hermitian=( - not check_hermitian or _check_hermitian(state, backend=backend) - ), + hermitian=(not check_hermitian or _check_hermitian(state, backend=backend)), ) eigenvalues_target = backend.calculate_eigenvalues( target, - hermitian=( - not check_hermitian or _check_hermitian(target, backend=backend) - ), + hermitian=(not check_hermitian or _check_hermitian(target, backend=backend)), ) log_state = backend.np.where( From b7592bca883e3ad64577096fc39bbd000268fd48 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 16 Sep 2024 15:36:20 +0400 Subject: [PATCH 04/13] type hint --- src/qibo/backends/abstract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/backends/abstract.py b/src/qibo/backends/abstract.py index 5be8dd479d..d676f14255 100644 --- a/src/qibo/backends/abstract.py +++ b/src/qibo/backends/abstract.py @@ -318,12 +318,12 @@ def calculate_overlap_density_matrix(self, state1, state2): # pragma: no cover raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvalues(self, matrix, k=6, hermitian: bool = True): # pragma: no cover + def calculate_eigenvalues(self, matrix, k: int = 6, hermitian: bool = True): # pragma: no cover """Calculate eigenvalues of a matrix.""" raise_error(NotImplementedError) @abc.abstractmethod - def calculate_eigenvectors(self, matrix, k=6, hermitian: bool = True): # pragma: no cover + def calculate_eigenvectors(self, matrix, k: int = 6, hermitian: bool = True): # pragma: no cover """Calculate eigenvectors of a matrix.""" raise_error(NotImplementedError) From 6c51c9229e0b3b61fe0bba0cc4059bb1b469f100 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 14:45:08 +0400 Subject: [PATCH 05/13] 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..fbcefdadcc 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -1740,6 +1740,12 @@ Classical relative entropy .. autofunction:: qibo.quantum_info.classical_relative_entropy +Classical mutual information +"""""""""""""""""""""""""""" + +.. autofunction:: qibo.quantum_info.classical_mutual_information + + Classical Rényi entropy """"""""""""""""""""""" From f7dc49d26600f77e069d36dd1a602cdf72c19499 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 15:09:38 +0400 Subject: [PATCH 06/13] 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 fbcefdadcc..229a859f09 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -1791,6 +1791,12 @@ Relative von Neumann entropy an error will be raised when using `cupy` backend. +Mutual information +"""""""""""""""""" + +.. autofunction:: qibo.quantum_info.mutual_information + + Rényi entropy """"""""""""" From 4f1e28e74218e243326d20fbddae35650c725bb2 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 15:10:01 +0400 Subject: [PATCH 07/13] functions --- src/qibo/quantum_info/entropies.py | 80 +++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index c9c889b603..ff2a4dcfe1 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -30,7 +30,7 @@ def shannon_entropy(prob_dist, base: float = 2, backend=None): Defaults to ``None``. Returns: - (float): Shannon entropy :math:`H(\\mathcal{p})`. + float: Shannon entropy :math:`H(\\mathcal{p})`. """ backend = _check_backend(backend) @@ -143,6 +143,40 @@ def classical_relative_entropy(prob_dist_p, prob_dist_q, base: float = 2, backen return entropy_p - relative +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. + + Given two random variables :math:`(X, \\, Y)`, their mutual information is given by + + .. math:: + I(X, \\, Y) \\equiv H(p(x)) + H(q(y)) - H(p(x, \\, y)) \\, , + + 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`. + + 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)`. + 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 :class:`qibo.backends.GlobalBackend`. + Defaults to ``None``. + + Returns: + float: Mutual information :math:`I(X, \\, Y)`. + """ + return ( + shannon_entropy(prob_dist_p, base, backend) + + shannon_entropy(prob_dist_q, base, backend) + - shannon_entropy(prob_dist_joint, base, backend) + ) + + def classical_renyi_entropy( prob_dist, alpha: Union[float, int], base: float = 2, backend=None ): @@ -580,6 +614,50 @@ def relative_von_neumann_entropy( return float(backend.np.real(entropy_state - relative)) +def mutual_information( + state, partition, base: float = 2, check_hermitian: bool = False, backend=None +): + """Calculates the mutual information of a bipartite state. + + Given a qubit ``partition`` :math:`A`, the mutual information + of state :math:`\\rho` is given by + + .. math:: + I(\\rho}) \\equiv S(\\rho_{A}) + S(\\rho_{B}) - S(\\rho) \\, , + + where :math:`B` is the remaining qubits that are not in partition :math:`A`, + and :math:`S(\\cdot)` is the :func:`qibo.quantum_info.von_neumann_entropy`. + + 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`. + 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 + in the execution. If ``None``, it uses + :class:`qibo.backends.GlobalBackend`. Defaults to ``None``. + + Returns: + float: Mutual information :math:`I(\\rho)` of ``state`` :math:`\\rho`. + """ + nqubits = np.log2(len(state)) + + if not nqubits.is_integer(): + raise_error(ValueError, f"dimensions of ``state`` must be a power of 2.") + + partition_b = set(list(range(nqubits))) ^ set(partition) + + state_a = partial_trace(state, partition_b, backend) + state_b = partial_trace(state, partition, backend) + + return ( + von_neumann_entropy(state_a, base, check_hermitian, False, backend) + + von_neumann_entropy(state_b, base, check_hermitian, False, backend) + - von_neumann_entropy(state, base, check_hermitian, False, backend) + ) + + 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`. From cbda5932cd919056057c3517be3736552c58c01d Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 15:41:48 +0400 Subject: [PATCH 08/13] tests --- tests/test_quantum_info_entropies.py | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_quantum_info_entropies.py b/tests/test_quantum_info_entropies.py index 84129e3c6d..b47c496f7d 100644 --- a/tests/test_quantum_info_entropies.py +++ b/tests/test_quantum_info_entropies.py @@ -5,11 +5,13 @@ from qibo.config import PRECISION_TOL from qibo.quantum_info.entropies import ( _matrix_power, + classical_mutual_information, classical_relative_entropy, classical_relative_renyi_entropy, classical_renyi_entropy, classical_tsallis_entropy, entanglement_entropy, + mutual_information, relative_renyi_entropy, relative_von_neumann_entropy, renyi_entropy, @@ -125,6 +127,25 @@ def test_classical_relative_entropy(backend, base, kind): backend.assert_allclose(divergence, target, atol=1e-5) +@pytest.mark.parametrize("base", [2, 10, np.e, 5]) +def test_classical_mutual_information(backend, base): + prob_p = np.random.rand(10) + prob_q = np.random.rand(10) + prob_p /= np.sum(prob_p) + prob_q /= np.sum(prob_q) + + joint_dist = np.kron(prob_p, prob_q) + joint_dist /= np.sum(joint_dist) + + prob_p = backend.cast(prob_p, dtype=prob_p.dtype) + prob_q = backend.cast(prob_q, dtype=prob_q.dtype) + joint_dist = backend.cast(joint_dist, dtype=joint_dist.dtype) + + assert ( + classical_mutual_information(joint_dist, prob_p, prob_q, base, backend) == 0.0 + ) + + @pytest.mark.parametrize("kind", [None, list]) @pytest.mark.parametrize("base", [2, 10, np.e, 5]) @pytest.mark.parametrize("alpha", [0, 1, 2, 3, np.inf]) @@ -499,6 +520,18 @@ def test_relative_entropy(backend, base, check_hermitian): ) +@pytest.mark.parametrize("check_hermitian", [False, True]) +@pytest.mark.parametrize("base", [2, 10, np.e, 5]) +def test_mutual_information(backend, base, check_hermitian): + state_a = random_density_matrix(4, backend=backend) + state_b = random_density_matrix(4, backend=backend) + state = backend.np.kron(state_a, state_b) + + assert ( + mutual_information(state, [0, 1], base, check_hermitian, backend) == 0.0 + ) + + @pytest.mark.parametrize("base", [2, 10, np.e, 5]) @pytest.mark.parametrize("alpha", [0, 1, 2, 3, np.inf]) def test_renyi_entropy(backend, alpha, base): From 577043badb14cc1ca663d042f7e7d4a3c86e1930 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:42:15 +0000 Subject: [PATCH 09/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_quantum_info_entropies.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_quantum_info_entropies.py b/tests/test_quantum_info_entropies.py index b47c496f7d..cb73f452b4 100644 --- a/tests/test_quantum_info_entropies.py +++ b/tests/test_quantum_info_entropies.py @@ -527,9 +527,7 @@ def test_mutual_information(backend, base, check_hermitian): state_b = random_density_matrix(4, backend=backend) state = backend.np.kron(state_a, state_b) - assert ( - mutual_information(state, [0, 1], base, check_hermitian, backend) == 0.0 - ) + assert mutual_information(state, [0, 1], base, check_hermitian, backend) == 0.0 @pytest.mark.parametrize("base", [2, 10, np.e, 5]) From 42a0650412578cb043a2e8f5a2345d75946b8b19 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 16:12:55 +0400 Subject: [PATCH 10/13] fix test --- tests/test_quantum_info_entropies.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_quantum_info_entropies.py b/tests/test_quantum_info_entropies.py index cb73f452b4..62f03d37ad 100644 --- a/tests/test_quantum_info_entropies.py +++ b/tests/test_quantum_info_entropies.py @@ -141,8 +141,10 @@ def test_classical_mutual_information(backend, base): prob_q = backend.cast(prob_q, dtype=prob_q.dtype) joint_dist = backend.cast(joint_dist, dtype=joint_dist.dtype) - assert ( - classical_mutual_information(joint_dist, prob_p, prob_q, base, backend) == 0.0 + backend.assert_allclose( + classical_mutual_information(joint_dist, prob_p, prob_q, base, backend), + 0.0, + atol=1e-10, ) @@ -527,7 +529,11 @@ def test_mutual_information(backend, base, check_hermitian): state_b = random_density_matrix(4, backend=backend) state = backend.np.kron(state_a, state_b) - assert mutual_information(state, [0, 1], base, check_hermitian, backend) == 0.0 + backend.assert_allclose( + mutual_information(state, [0, 1], base, check_hermitian, backend), + 0.0, + atol=1e-10, + ) @pytest.mark.parametrize("base", [2, 10, np.e, 5]) From 7c6c096f9b471944e4063c9665beb367ad4e1642 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Wed, 18 Sep 2024 16:13:49 +0400 Subject: [PATCH 11/13] fix bug --- src/qibo/quantum_info/entropies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index ff2a4dcfe1..79490f4d94 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -646,7 +646,7 @@ def mutual_information( if not nqubits.is_integer(): raise_error(ValueError, f"dimensions of ``state`` must be a power of 2.") - partition_b = set(list(range(nqubits))) ^ set(partition) + partition_b = set(list(range(nqubits))) ^ set(list(partition)) state_a = partial_trace(state, partition_b, backend) state_b = partial_trace(state, partition, backend) From f9625a27a731ac6d35e309a978d68f499c308391 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 19 Sep 2024 09:15:51 +0400 Subject: [PATCH 12/13] fix bug --- src/qibo/quantum_info/entropies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/entropies.py b/src/qibo/quantum_info/entropies.py index 79490f4d94..b779084287 100644 --- a/src/qibo/quantum_info/entropies.py +++ b/src/qibo/quantum_info/entropies.py @@ -646,7 +646,7 @@ def mutual_information( if not nqubits.is_integer(): raise_error(ValueError, f"dimensions of ``state`` must be a power of 2.") - partition_b = set(list(range(nqubits))) ^ set(list(partition)) + partition_b = set(list(range(int(nqubits)))) ^ set(list(partition)) state_a = partial_trace(state, partition_b, backend) state_b = partial_trace(state, partition, backend) From 3ba442d8ec0d80b3904cc5bed27d057a94d0fbcc Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 19 Sep 2024 13:19:34 +0400 Subject: [PATCH 13/13] coverage --- tests/test_quantum_info_entropies.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_quantum_info_entropies.py b/tests/test_quantum_info_entropies.py index 62f03d37ad..a0644a17c4 100644 --- a/tests/test_quantum_info_entropies.py +++ b/tests/test_quantum_info_entropies.py @@ -525,6 +525,11 @@ def test_relative_entropy(backend, base, check_hermitian): @pytest.mark.parametrize("check_hermitian", [False, True]) @pytest.mark.parametrize("base", [2, 10, np.e, 5]) def test_mutual_information(backend, base, check_hermitian): + with pytest.raises(ValueError): + state = np.ones((3, 3)) + state = backend.cast(state, dtype=state.dtype) + test = mutual_information(state, [0], backend) + state_a = random_density_matrix(4, backend=backend) state_b = random_density_matrix(4, backend=backend) state = backend.np.kron(state_a, state_b)