From 387dc5fe445d7c573374b6f63718e64e08626379 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 13:13:05 +0400 Subject: [PATCH 01/18] include `stinespring` in `random_quantum_channel` --- src/qibo/quantum_info/random_ensembles.py | 40 +++++++++++++++++++++-- tests/test_quantum_info_random.py | 11 ++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 6ba2dfb804..9149b8b367 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -16,6 +16,7 @@ choi_to_kraus, choi_to_liouville, choi_to_pauli, + choi_to_stinespring, vectorization, ) @@ -202,6 +203,9 @@ def random_quantum_channel( order: str = "row", normalize: bool = False, precision_tol: Optional[float] = None, + validate_cp: bool = True, + nqubits: Optional[int] = None, + initial_state_env=None, seed=None, backend=None, ): @@ -216,7 +220,8 @@ def random_quantum_channel( returns Liouville representation. If ``"pauli"``, returns Pauli-Liouville representation. If "pauli-" or "chi-", (e.g. "pauli-IZXY"), returns it in the Pauli basis with the corresponding order of single-qubit Pauli elements - (see :func:`qibo.quantum_info.pauli_basis`). Defaults to ``"liouville"``. + (see :func:`qibo.quantum_info.pauli_basis`). If ``"stinespring"``, + returns random channel in the Stinespring representation. Defaults to ``"liouville"``. measure (str, optional): probability measure in which to sample the unitary from. If ``None``, functions returns :math:`\\exp{(-i \\, H)}`, where :math:`H` is a Hermitian operator. If ``"haar"``, returns an Unitary @@ -232,6 +237,21 @@ def random_quantum_channel( problem. Any eigenvalue :math:`\\lambda <` ``precision_tol`` is set to 0 (zero). If ``None``, ``precision_tol`` defaults to ``qibo.config.PRECISION_TOL=1e-8``. Defaults to ``None``. + validate_cp (bool, optional): used when ``representation="stinespring"``. + If ``True``, checks if the Choi representation of superoperator + used as intermediate step is a completely positive map. + If ``False``, it assumes that it is completely positive (and Hermitian). + Defaults to ``True``. + nqubits (int, optional): used when ``representation="stinespring"``. + Total number of qubits in the system that is interacting with + the environment. Must be equal or greater than the number of + qubits that Kraus representation of the system superoperator acts on. + If ``None``, defaults to the number of qubits in the Kraus operators. + Defauts to ``None``. + initial_state_env (ndarray, optional): used when ``representation="stinespring"``. + Statevector representing the initial state of the enviroment. + If ``None``, it assumes the environment in its ground state. + Defaults to ``None``. 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. Defaults to ``None``. @@ -248,7 +268,14 @@ def random_quantum_channel( f"representation must be type str, but it is type {type(representation)}", ) - if representation not in ["chi", "choi", "kraus", "liouville", "pauli"]: + if representation not in [ + "chi", + "choi", + "kraus", + "liouville", + "pauli", + "stinespring", + ]: if ( ("chi-" not in representation and "pauli-" not in representation) or len(representation.split("-")) != 2 @@ -292,6 +319,15 @@ def random_quantum_channel( pauli_order=pauli_order, backend=backend, ) + elif representation == "stinespring": + super_op = choi_to_stinespring( + super_op, + precision_tol=precision_tol, + order=order, + validate_cp=validate_cp, + initial_state_env=initial_state_env, + backend=backend, + ) return super_op diff --git a/tests/test_quantum_info_random.py b/tests/test_quantum_info_random.py index 8af5bc0063..92df660a9c 100644 --- a/tests/test_quantum_info_random.py +++ b/tests/test_quantum_info_random.py @@ -148,7 +148,16 @@ def test_random_unitary(backend): @pytest.mark.parametrize("measure", [None, "haar"]) @pytest.mark.parametrize( "representation", - ["chi", "chi-IZXY", "choi", "kraus", "liouville", "pauli", "pauli-IZXY"], + [ + "chi", + "chi-IZXY", + "choi", + "kraus", + "liouville", + "pauli", + "pauli-IZXY", + "stinespring", + ], ) def test_random_quantum_channel(backend, representation, measure): with pytest.raises(TypeError): From aec2c5e8a1366a7598f51e4dec05283b1174ed7c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:35:54 +0400 Subject: [PATCH 02/18] faster `random_statevector` --- src/qibo/quantum_info/random_ensembles.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 4b52efda24..1ba7c45d13 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -351,10 +351,9 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - probabilities = local_state.random(dims) - probabilities = probabilities / np.sum(probabilities) - phases = 2 * np.pi * local_state.random(dims) - state = np.sqrt(probabilities) * np.exp(1.0j * phases) + state = local_state.random(dims) + state += local_state.random(dims, dtype=complex) + state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) else: # select a random column of a haar random unitary From 1e5c77f92b789495c7e4d604b4f9391e5131d80e Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:42:33 +0400 Subject: [PATCH 03/18] shift distribution --- src/qibo/quantum_info/random_ensembles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 1ba7c45d13..2b60ee1962 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -351,8 +351,8 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = local_state.random(dims) - state += local_state.random(dims, dtype=complex) + state = 2 * local_state.random(dims, dtype=complex) - 1 + state += 1j * (2 * local_state.random(dims, dtype=complex) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) else: From aaa79efe7fe5aca9757038804def98712da7b5da Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:43:26 +0400 Subject: [PATCH 04/18] fix types --- src/qibo/quantum_info/random_ensembles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 2b60ee1962..244fa7f71b 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -351,8 +351,8 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = 2 * local_state.random(dims, dtype=complex) - 1 - state += 1j * (2 * local_state.random(dims, dtype=complex) - 1) + state = 2 * local_state.random(dims) - 1 + state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) else: From 73789de4e5668c271c4db22868b450ee23311b4f Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:57:53 +0400 Subject: [PATCH 05/18] fix dtype --- src/qibo/quantum_info/random_ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 244fa7f71b..b2b6299a18 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -351,7 +351,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = 2 * local_state.random(dims) - 1 + state = 2 * local_state.random(dims, dtype=complex) - 1 state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From 16da133df49cfc288f7f1c55fda17322fe4cde63 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 19:09:05 +0400 Subject: [PATCH 06/18] faster `random_unitary` --- src/qibo/quantum_info/random_ensembles.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index b2b6299a18..e60f740e88 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -4,6 +4,8 @@ from typing import Optional, Union import numpy as np +from scipy import stats +from scipy.linalg import expm from qibo import Circuit, gates from qibo.backends import GlobalBackend, NumpyBackend @@ -183,15 +185,14 @@ def random_unitary(dims: int, measure: Optional[str] = None, seed=None, backend= backend = GlobalBackend() if measure == "haar": - gaussian_matrix = random_gaussian_matrix(dims, dims, seed=seed, backend=backend) - Q, R = np.linalg.qr(gaussian_matrix) - D = np.diag(R) - D = D / np.abs(D) - R = np.diag(D) - unitary = np.dot(Q, R) + # gaussian_matrix = random_gaussian_matrix(dims, dims, seed=seed, backend=backend) + # Q, R = np.linalg.qr(gaussian_matrix) + # D = np.diag(R) + # D = D / np.abs(D) + # R = np.diag(D) + # unitary = np.dot(Q, R) + unitary = stats.unitary_group.rvs(dims, random_state=seed) elif measure is None: - from scipy.linalg import expm - H = random_hermitian(dims, seed=seed, backend=NumpyBackend()) unitary = expm(-1.0j * H / 2) @@ -351,7 +352,8 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = 2 * local_state.random(dims, dtype=complex) - 1 + state = np.zeros(dims, dtype=complex) + state += 2 * local_state.random(dims) - 1 state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From cc027b8468b9207630673329585d0ad3af61fe24 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 19:26:35 +0400 Subject: [PATCH 07/18] fix statevector implementation --- src/qibo/quantum_info/random_ensembles.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index e60f740e88..19ec972994 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -352,8 +352,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = np.zeros(dims, dtype=complex) - state += 2 * local_state.random(dims) - 1 + state = (2 * local_state.random(dims) - 1).astype(complex) state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From 7606101376d033d6b7673f36d801b9b659c66eee Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 12:55:03 +0400 Subject: [PATCH 08/18] remove comments --- src/qibo/quantum_info/random_ensembles.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 19ec972994..c102d7f7b1 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -185,12 +185,6 @@ def random_unitary(dims: int, measure: Optional[str] = None, seed=None, backend= backend = GlobalBackend() if measure == "haar": - # gaussian_matrix = random_gaussian_matrix(dims, dims, seed=seed, backend=backend) - # Q, R = np.linalg.qr(gaussian_matrix) - # D = np.diag(R) - # D = D / np.abs(D) - # R = np.diag(D) - # unitary = np.dot(Q, R) unitary = stats.unitary_group.rvs(dims, random_state=seed) elif measure is None: H = random_hermitian(dims, seed=seed, backend=NumpyBackend()) From c58672e88cd56110a5644ae3524c06fba1534851 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 12:56:38 +0400 Subject: [PATCH 09/18] add comment --- src/qibo/quantum_info/random_ensembles.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index c102d7f7b1..6ba2dfb804 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -346,6 +346,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: + # sample real and imag parts of complex amplitude in [-1, 1] state = (2 * local_state.random(dims) - 1).astype(complex) state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) From 8c40c7286cc15bd5932edf39f999d9d1e5f3367e Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 13:13:05 +0400 Subject: [PATCH 10/18] include `stinespring` in `random_quantum_channel` --- src/qibo/quantum_info/random_ensembles.py | 40 +++++++++++++++++++++-- tests/test_quantum_info_random.py | 11 ++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 6ba2dfb804..9149b8b367 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -16,6 +16,7 @@ choi_to_kraus, choi_to_liouville, choi_to_pauli, + choi_to_stinespring, vectorization, ) @@ -202,6 +203,9 @@ def random_quantum_channel( order: str = "row", normalize: bool = False, precision_tol: Optional[float] = None, + validate_cp: bool = True, + nqubits: Optional[int] = None, + initial_state_env=None, seed=None, backend=None, ): @@ -216,7 +220,8 @@ def random_quantum_channel( returns Liouville representation. If ``"pauli"``, returns Pauli-Liouville representation. If "pauli-" or "chi-", (e.g. "pauli-IZXY"), returns it in the Pauli basis with the corresponding order of single-qubit Pauli elements - (see :func:`qibo.quantum_info.pauli_basis`). Defaults to ``"liouville"``. + (see :func:`qibo.quantum_info.pauli_basis`). If ``"stinespring"``, + returns random channel in the Stinespring representation. Defaults to ``"liouville"``. measure (str, optional): probability measure in which to sample the unitary from. If ``None``, functions returns :math:`\\exp{(-i \\, H)}`, where :math:`H` is a Hermitian operator. If ``"haar"``, returns an Unitary @@ -232,6 +237,21 @@ def random_quantum_channel( problem. Any eigenvalue :math:`\\lambda <` ``precision_tol`` is set to 0 (zero). If ``None``, ``precision_tol`` defaults to ``qibo.config.PRECISION_TOL=1e-8``. Defaults to ``None``. + validate_cp (bool, optional): used when ``representation="stinespring"``. + If ``True``, checks if the Choi representation of superoperator + used as intermediate step is a completely positive map. + If ``False``, it assumes that it is completely positive (and Hermitian). + Defaults to ``True``. + nqubits (int, optional): used when ``representation="stinespring"``. + Total number of qubits in the system that is interacting with + the environment. Must be equal or greater than the number of + qubits that Kraus representation of the system superoperator acts on. + If ``None``, defaults to the number of qubits in the Kraus operators. + Defauts to ``None``. + initial_state_env (ndarray, optional): used when ``representation="stinespring"``. + Statevector representing the initial state of the enviroment. + If ``None``, it assumes the environment in its ground state. + Defaults to ``None``. 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. Defaults to ``None``. @@ -248,7 +268,14 @@ def random_quantum_channel( f"representation must be type str, but it is type {type(representation)}", ) - if representation not in ["chi", "choi", "kraus", "liouville", "pauli"]: + if representation not in [ + "chi", + "choi", + "kraus", + "liouville", + "pauli", + "stinespring", + ]: if ( ("chi-" not in representation and "pauli-" not in representation) or len(representation.split("-")) != 2 @@ -292,6 +319,15 @@ def random_quantum_channel( pauli_order=pauli_order, backend=backend, ) + elif representation == "stinespring": + super_op = choi_to_stinespring( + super_op, + precision_tol=precision_tol, + order=order, + validate_cp=validate_cp, + initial_state_env=initial_state_env, + backend=backend, + ) return super_op diff --git a/tests/test_quantum_info_random.py b/tests/test_quantum_info_random.py index 8af5bc0063..92df660a9c 100644 --- a/tests/test_quantum_info_random.py +++ b/tests/test_quantum_info_random.py @@ -148,7 +148,16 @@ def test_random_unitary(backend): @pytest.mark.parametrize("measure", [None, "haar"]) @pytest.mark.parametrize( "representation", - ["chi", "chi-IZXY", "choi", "kraus", "liouville", "pauli", "pauli-IZXY"], + [ + "chi", + "chi-IZXY", + "choi", + "kraus", + "liouville", + "pauli", + "pauli-IZXY", + "stinespring", + ], ) def test_random_quantum_channel(backend, representation, measure): with pytest.raises(TypeError): From d8a15573c4d1fa79f859f80b896c2a248a318dfb Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:43:26 +0400 Subject: [PATCH 11/18] fix types --- src/qibo/quantum_info/random_ensembles.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 9149b8b367..afc4af6816 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -382,8 +382,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - # sample real and imag parts of complex amplitude in [-1, 1] - state = (2 * local_state.random(dims) - 1).astype(complex) + state = 2 * local_state.random(dims) - 1 state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From c69279f590127fbca92dd751628dce82c0653b30 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 18:57:53 +0400 Subject: [PATCH 12/18] fix dtype --- src/qibo/quantum_info/random_ensembles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index afc4af6816..076d19099d 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -382,7 +382,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = 2 * local_state.random(dims) - 1 + state = 2 * local_state.random(dims, dtype=complex) - 1 state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From 017489e3e9035556d40de9245ddfbcd833fe89b4 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 19:09:05 +0400 Subject: [PATCH 13/18] faster `random_unitary` --- src/qibo/quantum_info/random_ensembles.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 076d19099d..15e16e4e79 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -382,7 +382,8 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = 2 * local_state.random(dims, dtype=complex) - 1 + state = np.zeros(dims, dtype=complex) + state += 2 * local_state.random(dims) - 1 state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From f2444800d39bf23d0cf2d185e932fdc3b8d7eb9d Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 12 Aug 2023 19:26:35 +0400 Subject: [PATCH 14/18] fix statevector implementation --- src/qibo/quantum_info/random_ensembles.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 15e16e4e79..ca07552586 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -382,8 +382,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - state = np.zeros(dims, dtype=complex) - state += 2 * local_state.random(dims) - 1 + state = (2 * local_state.random(dims) - 1).astype(complex) state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) From 28440f04466c041cb3fce44b7cabc76973aef87f Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 12:56:38 +0400 Subject: [PATCH 15/18] add comment --- src/qibo/quantum_info/random_ensembles.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index ca07552586..9149b8b367 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -382,6 +382,7 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: + # sample real and imag parts of complex amplitude in [-1, 1] state = (2 * local_state.random(dims) - 1).astype(complex) state += 1j * (2 * local_state.random(dims) - 1) state /= np.linalg.norm(state) From a4fe972520d91a0853a03016f5e072343f536f1e Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 14:57:26 +0400 Subject: [PATCH 16/18] return to `master` version --- src/qibo/quantum_info/random_ensembles.py | 59 ++++++----------------- 1 file changed, 14 insertions(+), 45 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 9149b8b367..4b52efda24 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -4,8 +4,6 @@ from typing import Optional, Union import numpy as np -from scipy import stats -from scipy.linalg import expm from qibo import Circuit, gates from qibo.backends import GlobalBackend, NumpyBackend @@ -16,7 +14,6 @@ choi_to_kraus, choi_to_liouville, choi_to_pauli, - choi_to_stinespring, vectorization, ) @@ -186,8 +183,15 @@ def random_unitary(dims: int, measure: Optional[str] = None, seed=None, backend= backend = GlobalBackend() if measure == "haar": - unitary = stats.unitary_group.rvs(dims, random_state=seed) + gaussian_matrix = random_gaussian_matrix(dims, dims, seed=seed, backend=backend) + Q, R = np.linalg.qr(gaussian_matrix) + D = np.diag(R) + D = D / np.abs(D) + R = np.diag(D) + unitary = np.dot(Q, R) elif measure is None: + from scipy.linalg import expm + H = random_hermitian(dims, seed=seed, backend=NumpyBackend()) unitary = expm(-1.0j * H / 2) @@ -203,9 +207,6 @@ def random_quantum_channel( order: str = "row", normalize: bool = False, precision_tol: Optional[float] = None, - validate_cp: bool = True, - nqubits: Optional[int] = None, - initial_state_env=None, seed=None, backend=None, ): @@ -220,8 +221,7 @@ def random_quantum_channel( returns Liouville representation. If ``"pauli"``, returns Pauli-Liouville representation. If "pauli-" or "chi-", (e.g. "pauli-IZXY"), returns it in the Pauli basis with the corresponding order of single-qubit Pauli elements - (see :func:`qibo.quantum_info.pauli_basis`). If ``"stinespring"``, - returns random channel in the Stinespring representation. Defaults to ``"liouville"``. + (see :func:`qibo.quantum_info.pauli_basis`). Defaults to ``"liouville"``. measure (str, optional): probability measure in which to sample the unitary from. If ``None``, functions returns :math:`\\exp{(-i \\, H)}`, where :math:`H` is a Hermitian operator. If ``"haar"``, returns an Unitary @@ -237,21 +237,6 @@ def random_quantum_channel( problem. Any eigenvalue :math:`\\lambda <` ``precision_tol`` is set to 0 (zero). If ``None``, ``precision_tol`` defaults to ``qibo.config.PRECISION_TOL=1e-8``. Defaults to ``None``. - validate_cp (bool, optional): used when ``representation="stinespring"``. - If ``True``, checks if the Choi representation of superoperator - used as intermediate step is a completely positive map. - If ``False``, it assumes that it is completely positive (and Hermitian). - Defaults to ``True``. - nqubits (int, optional): used when ``representation="stinespring"``. - Total number of qubits in the system that is interacting with - the environment. Must be equal or greater than the number of - qubits that Kraus representation of the system superoperator acts on. - If ``None``, defaults to the number of qubits in the Kraus operators. - Defauts to ``None``. - initial_state_env (ndarray, optional): used when ``representation="stinespring"``. - Statevector representing the initial state of the enviroment. - If ``None``, it assumes the environment in its ground state. - Defaults to ``None``. 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. Defaults to ``None``. @@ -268,14 +253,7 @@ def random_quantum_channel( f"representation must be type str, but it is type {type(representation)}", ) - if representation not in [ - "chi", - "choi", - "kraus", - "liouville", - "pauli", - "stinespring", - ]: + if representation not in ["chi", "choi", "kraus", "liouville", "pauli"]: if ( ("chi-" not in representation and "pauli-" not in representation) or len(representation.split("-")) != 2 @@ -319,15 +297,6 @@ def random_quantum_channel( pauli_order=pauli_order, backend=backend, ) - elif representation == "stinespring": - super_op = choi_to_stinespring( - super_op, - precision_tol=precision_tol, - order=order, - validate_cp=validate_cp, - initial_state_env=initial_state_env, - backend=backend, - ) return super_op @@ -382,10 +351,10 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): ) if not haar: - # sample real and imag parts of complex amplitude in [-1, 1] - state = (2 * local_state.random(dims) - 1).astype(complex) - state += 1j * (2 * local_state.random(dims) - 1) - state /= np.linalg.norm(state) + probabilities = local_state.random(dims) + probabilities = probabilities / np.sum(probabilities) + phases = 2 * np.pi * local_state.random(dims) + state = np.sqrt(probabilities) * np.exp(1.0j * phases) state = backend.cast(state, dtype=state.dtype) else: # select a random column of a haar random unitary From 6baa3207bb1b65b0e5a4ab1682e523873e46416c Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 14 Aug 2023 15:10:13 +0400 Subject: [PATCH 17/18] stinespring representation in `random_quantum_channel` --- src/qibo/quantum_info/random_ensembles.py | 40 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 4b52efda24..cf09aa23e7 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -14,6 +14,7 @@ choi_to_kraus, choi_to_liouville, choi_to_pauli, + choi_to_stinespring, vectorization, ) @@ -207,6 +208,9 @@ def random_quantum_channel( order: str = "row", normalize: bool = False, precision_tol: Optional[float] = None, + validate_cp: bool = True, + nqubits: Optional[int] = None, + initial_state_env=None, seed=None, backend=None, ): @@ -221,7 +225,8 @@ def random_quantum_channel( returns Liouville representation. If ``"pauli"``, returns Pauli-Liouville representation. If "pauli-" or "chi-", (e.g. "pauli-IZXY"), returns it in the Pauli basis with the corresponding order of single-qubit Pauli elements - (see :func:`qibo.quantum_info.pauli_basis`). Defaults to ``"liouville"``. + (see :func:`qibo.quantum_info.pauli_basis`). If ``"stinespring"``, + returns random channel in the Stinespring representation. Defaults to ``"liouville"``. measure (str, optional): probability measure in which to sample the unitary from. If ``None``, functions returns :math:`\\exp{(-i \\, H)}`, where :math:`H` is a Hermitian operator. If ``"haar"``, returns an Unitary @@ -237,6 +242,21 @@ def random_quantum_channel( problem. Any eigenvalue :math:`\\lambda <` ``precision_tol`` is set to 0 (zero). If ``None``, ``precision_tol`` defaults to ``qibo.config.PRECISION_TOL=1e-8``. Defaults to ``None``. + validate_cp (bool, optional): used when ``representation="stinespring"``. + If ``True``, checks if the Choi representation of superoperator + used as intermediate step is a completely positive map. + If ``False``, it assumes that it is completely positive (and Hermitian). + Defaults to ``True``. + nqubits (int, optional): used when ``representation="stinespring"``. + Total number of qubits in the system that is interacting with + the environment. Must be equal or greater than the number of + qubits that Kraus representation of the system superoperator acts on. + If ``None``, defaults to the number of qubits in the Kraus operators. + Defauts to ``None``. + initial_state_env (ndarray, optional): used when ``representation="stinespring"``. + Statevector representing the initial state of the enviroment. + If ``None``, it assumes the environment in its ground state. + Defaults to ``None``. 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. Defaults to ``None``. @@ -253,7 +273,14 @@ def random_quantum_channel( f"representation must be type str, but it is type {type(representation)}", ) - if representation not in ["chi", "choi", "kraus", "liouville", "pauli"]: + if representation not in [ + "chi", + "choi", + "kraus", + "liouville", + "pauli", + "stinespring", + ]: if ( ("chi-" not in representation and "pauli-" not in representation) or len(representation.split("-")) != 2 @@ -297,6 +324,15 @@ def random_quantum_channel( pauli_order=pauli_order, backend=backend, ) + elif representation == "stinespring": + super_op = choi_to_stinespring( + super_op, + precision_tol=precision_tol, + order=order, + validate_cp=validate_cp, + initial_state_env=initial_state_env, + backend=backend, + ) return super_op From fccf8a7ca52ec150bfbdccc0a52a4ccf0fef40a3 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 17 Aug 2023 11:43:41 +0000 Subject: [PATCH 18/18] Update src/qibo/quantum_info/random_ensembles.py Co-authored-by: vodovozovaliza <55856544+vodovozovaliza@users.noreply.github.com> --- src/qibo/quantum_info/random_ensembles.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 91fe0a5b4d..4f831a7b28 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -329,6 +329,7 @@ def random_quantum_channel( precision_tol=precision_tol, order=order, validate_cp=validate_cp, + nqubits=nqubits, initial_state_env=initial_state_env, backend=backend, )