From a2d4ebd41e7f41d3177d5068cb10d2bb395fce81 Mon Sep 17 00:00:00 2001 From: Emilio Pelaez Date: Sun, 25 Jun 2023 12:36:57 -0500 Subject: [PATCH 1/2] add bures angle --- docs/states.rst | 1 + tests/test_state_metrics/test_bures_angle.py | 66 ++++++++++++++++++ toqito/state_metrics/__init__.py | 1 + toqito/state_metrics/bures_angle.py | 70 ++++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 tests/test_state_metrics/test_bures_angle.py create mode 100644 toqito/state_metrics/bures_angle.py diff --git a/docs/states.rst b/docs/states.rst index a9c160176..7f8a6d5db 100644 --- a/docs/states.rst +++ b/docs/states.rst @@ -26,6 +26,7 @@ Distance Metrics for Quantum States toqito.state_metrics.sub_fidelity toqito.state_metrics.trace_distance toqito.state_metrics.bures_distance + toqito.state_metrics.bures_angle toqito.state_metrics.matsumoto_fidelity Optimizations over Quantum States diff --git a/tests/test_state_metrics/test_bures_angle.py b/tests/test_state_metrics/test_bures_angle.py new file mode 100644 index 000000000..b3b965614 --- /dev/null +++ b/tests/test_state_metrics/test_bures_angle.py @@ -0,0 +1,66 @@ +"""Tests for bures_angle.""" +import numpy as np + + +from toqito.state_metrics import bures_angle +from toqito.states import basis + + +def test_bures_angle_default(): + """Test bures_angle default arguments.""" + rho = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) + sigma = rho + + ang = bures_angle(rho, sigma) + np.testing.assert_equal(np.isclose(ang, 0), True) + + +def test_bures_angle_non_identical_states_1(): + """Test the bures_angle between two non-identical states.""" + e_0, e_1 = basis(2, 0), basis(2, 1) + rho = 3 / 4 * e_0 * e_0.conj().T + 1 / 4 * e_1 * e_1.conj().T + sigma = 2 / 3 * e_0 * e_0.conj().T + 1 / 3 * e_1 * e_1.conj().T + + ang = bures_angle(rho, sigma) + np.testing.assert_equal(np.isclose(ang, 0.06499, rtol=1e-03), True) + + +def test_bures_angle_non_identical_states_2(): + """Test the bures_angle between two non-identical states.""" + e_0, e_1 = basis(2, 0), basis(2, 1) + rho = 3 / 4 * e_0 * e_0.conj().T + 1 / 4 * e_1 * e_1.conj().T + sigma = 1 / 8 * e_0 * e_0.conj().T + 7 / 8 * e_1 * e_1.conj().T + + ang = bures_angle(rho, sigma) + np.testing.assert_equal(np.isclose(ang, 0.4955, rtol=1e-03), True) + + +def test_bures_angle_pure_states(): + """Test the bures_angle between two pure states.""" + e_0, e_1 = basis(2, 0), basis(2, 1) + e_plus = (e_0 + e_1) / np.sqrt(2) + rho = e_plus * e_plus.conj().T + sigma = e_0 * e_0.conj().T + + ang = bures_angle(rho, sigma) + np.testing.assert_equal(np.isclose(ang, 0.5718, rtol=1e-03), True) + + +def test_bures_angle_non_square(): + """Tests for invalid dim.""" + rho = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) + sigma = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) + with np.testing.assert_raises(ValueError): + bures_angle(rho, sigma) + + +def test_bures_angle_invalid_dim(): + """Tests for invalid dim.""" + rho = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) + sigma = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + with np.testing.assert_raises(ValueError): + bures_angle(rho, sigma) + + +if __name__ == "__main__": + np.testing.run_module_suite() diff --git a/toqito/state_metrics/__init__.py b/toqito/state_metrics/__init__.py index 598b447a0..7951bbe6b 100644 --- a/toqito/state_metrics/__init__.py +++ b/toqito/state_metrics/__init__.py @@ -7,4 +7,5 @@ from toqito.state_metrics.sub_fidelity import sub_fidelity from toqito.state_metrics.trace_distance import trace_distance from toqito.state_metrics.bures_distance import bures_distance +from toqito.state_metrics.bures_angle import bures_angle from toqito.state_metrics.matsumoto_fidelity import matsumoto_fidelity diff --git a/toqito/state_metrics/bures_angle.py b/toqito/state_metrics/bures_angle.py new file mode 100644 index 000000000..434dfcbc7 --- /dev/null +++ b/toqito/state_metrics/bures_angle.py @@ -0,0 +1,70 @@ +"""Bures angle metric.""" +import numpy as np + +from toqito.state_metrics import fidelity + + +def bures_angle(rho_1: np.ndarray, rho_2: np.ndarray, decimals: int = 10) -> float: + r""" + Compute the Bures angle of two density matrices [WikBures]_. + + Calculate the Bures angle between two density matrices :code:`rho_1` and :code:`rho_2` + defined by: + + .. math:: + \arccos{\sqrt{F (\rho_1, \rho_2)}} + + where :math:`F(\cdot)` denotes the fidelity between :math:`\rho_1` and :math:`\rho_2`. The + return is a value between :math:`0` and :math:`\pi / 2`, with :math:`0` corresponding to + matrices :code:`rho_1 = rho_2` and :math:`\pi / 2` corresponding to the case :code: `rho_1` + and :code:`rho_2` with orthogonal support. + + Examples + ========== + + Consider the following Bell state + + .. math:: + u = \frac{1}{\sqrt{2}} \left( |00 \rangle + |11 \rangle \right) \in \mathcal{X}. + + The corresponding density matrix of :math:`u` may be calculated by: + + .. math:: + \rho = u u^* = \frac{1}{2} \begin{pmatrix} + 1 & 0 & 0 & 1 \\ + 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 \\ + 1 & 0 & 0 & 1 + \end{pmatrix} \in \text{D}(\mathcal{X}). + + In the event where we calculate the Bures angle between states that are identical, we + should obtain the value of :math:`0`. This can be observed in :code:`toqito` as follows. + + >>> from toqito.state_metrics import bures_angle + >>> import numpy as np + >>> rho = 1 / 2 * np.array( + >>> [[1, 0, 0, 1], + >>> [0, 0, 0, 0], + >>> [0, 0, 0, 0], + >>> [1, 0, 0, 1]] + >>> ) + >>> sigma = rho + >>> bures_angle(rho, sigma) + 0 + + References + ========== + .. [WikBures] Wikipedia: Bures distance + https://en.wikipedia.org/wiki/Bures_metric#Bures_distance + + :raises ValueError: If matrices are not of equal dimension. + :param rho_1: Density operator. + :param rho_2: Density operator. + :param decimals: Number of decimal places to round to (default 10). + :return: The Bures angle between :code:`rho_1` and :code:`rho_2`. + """ + # Perform error checking + if not np.all(rho_1.shape == rho_2.shape): + raise ValueError("InvalidDim: `rho_1` and `rho_2` must be matrices of the same size.") + # Round fidelity to only 10 decimals to avoid error when :code:`rho_1 = rho_2`. + return np.real(np.arccos(np.sqrt(np.round(fidelity(rho_1, rho_2), decimals)))) From 9c84023eec169dd125c4abfcf307e98b480952f9 Mon Sep 17 00:00:00 2001 From: Emilio Pelaez Date: Sun, 25 Jun 2023 12:39:19 -0500 Subject: [PATCH 2/2] add docs --- docs/_autosummary/toqito.state_metrics.bures_angle.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/_autosummary/toqito.state_metrics.bures_angle.rst diff --git a/docs/_autosummary/toqito.state_metrics.bures_angle.rst b/docs/_autosummary/toqito.state_metrics.bures_angle.rst new file mode 100644 index 000000000..45d1e765c --- /dev/null +++ b/docs/_autosummary/toqito.state_metrics.bures_angle.rst @@ -0,0 +1,6 @@ +toqito.state\_metrics.bures\_angle +===================================== + +.. currentmodule:: toqito.state_metrics + +.. autofunction:: bures_angle \ No newline at end of file