From 4bad7cc199c7864a1b789a0885b1ee1b08872838 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Tue, 23 Jan 2024 18:17:02 +0400 Subject: [PATCH 01/12] refactoring `random_statevector` --- src/qibo/quantum_info/random_ensembles.py | 24 +++++------------------ 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 9ef63a653d..d831da5c84 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -415,7 +415,7 @@ def random_quantum_channel( return super_op -def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): +def random_statevector(dims: int, seed=None, backend=None): """Creates a random statevector :math:`\\ket{\\psi}`. .. math:: @@ -427,10 +427,6 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): Args: dims (int): dimension of the matrix. - haar (bool, optional): if ``True``, statevector is created by sampling a - Haar random unitary :math:`U_{\\text{haar}}` and acting with it on a - random computational basis state :math:`\\ket{k}`, i.e. - :math:`\\ket{\\psi} = U_{\\text{haar}} \\ket{k}`. Defaults to ``False``. 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``. @@ -445,9 +441,6 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): if dims <= 0: raise_error(ValueError, "dim must be of type int and >= 1") - if not isinstance(haar, bool): - raise_error(TypeError, f"haar must be type bool, but it is type {type(haar)}.") - if ( seed is not None and not isinstance(seed, int) @@ -464,18 +457,11 @@ def random_statevector(dims: int, haar: bool = False, seed=None, backend=None): np.random.default_rng(seed) if seed is None or isinstance(seed, int) else seed ) - if not haar: - # sample real and imag parts of complex amplitude in [-1, 1] - state = 1j * (2 * local_state.random(dims) - 1) - state += 2 * local_state.random(dims) - 1 - state /= np.linalg.norm(state) - state = backend.cast(state, dtype=state.dtype) - else: - # select a random column of a haar random unitary - k = local_state.integers(low=0, high=dims) - state = random_unitary(dims, measure="haar", seed=seed, backend=backend)[:, k] + state = local_state.standard_normal(dims).astype(complex) + state += 1.0j * local_state.standard_normal(dims) + state /= np.linalg.norm(state) - return state + return backend.cast(state, dtype=state.dtype) def random_density_matrix( From 4b64d1956efb36707ec04f3fddbb5baeac5cfa1a Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Tue, 23 Jan 2024 18:19:13 +0400 Subject: [PATCH 02/12] adjust `random_density_matrix` --- 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 d831da5c84..495081c1c9 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -567,7 +567,7 @@ def random_density_matrix( if pure: state = random_statevector(dims, seed=seed, backend=backend) - state = np.outer(state, np.transpose(np.conj(state))) + state = np.outer(state, np.conj(state)) else: if metric in ["hilbert-schmidt", "ginibre"]: state = random_gaussian_matrix( @@ -583,7 +583,7 @@ def random_density_matrix( state, random_gaussian_matrix(dims, rank, seed=seed, backend=backend) ) state = np.dot(state, np.transpose(np.conj(state))) - state = state / np.trace(state) + state /= np.trace(state) state = backend.cast(state, dtype=state.dtype) From f9bb2a7e6fa00548d77512b5709f129bcd3f12d0 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Tue, 23 Jan 2024 18:20:12 +0400 Subject: [PATCH 03/12] adjust tests --- tests/test_quantum_info_random.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_quantum_info_random.py b/tests/test_quantum_info_random.py index 01000ab822..0c46afe4ab 100644 --- a/tests/test_quantum_info_random.py +++ b/tests/test_quantum_info_random.py @@ -228,25 +228,21 @@ def test_random_quantum_channel(backend, representation, measure, rank, order): ) -@pytest.mark.parametrize("haar", [False, True]) @pytest.mark.parametrize("seed", [None, 10, np.random.default_rng(10)]) -def test_random_statevector(backend, haar, seed): +def test_random_statevector(backend, seed): with pytest.raises(TypeError): dims = "10" random_statevector(dims, backend=backend) with pytest.raises(ValueError): dims = 0 random_statevector(dims, backend=backend) - with pytest.raises(TypeError): - dims = 2 - random_statevector(dims, haar=1, backend=backend) with pytest.raises(TypeError): dims = 2 random_statevector(dims, seed=0.1, backend=backend) # tests if random statevector is a pure state dims = 4 - state = random_statevector(dims, haar=haar, seed=seed, backend=backend) + state = random_statevector(dims, seed=seed, backend=backend) backend.assert_allclose(purity(state) <= 1.0 + PRECISION_TOL, True) backend.assert_allclose(purity(state) >= 1.0 - PRECISION_TOL, True) From 9c761843944d0e6c1d67a897aea29db162ae6bc0 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Tue, 23 Jan 2024 18:22:19 +0400 Subject: [PATCH 04/12] fix `haar_integral` --- src/qibo/quantum_info/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qibo/quantum_info/utils.py b/src/qibo/quantum_info/utils.py index eb5a447d8d..991b23172a 100644 --- a/src/qibo/quantum_info/utils.py +++ b/src/qibo/quantum_info/utils.py @@ -339,9 +339,7 @@ def haar_integral( rand_unit_density, dtype=rand_unit_density.dtype ) for _ in range(samples): - haar_state = np.reshape( - random_statevector(dim, haar=True, backend=backend), (-1, 1) - ) + haar_state = np.reshape(random_statevector(dim, backend=backend), (-1, 1)) rho = haar_state @ np.conj(np.transpose(haar_state)) From f31d5c508d3e1ff7866ad8aa30e90605de9ed6e1 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 27 Jan 2024 09:46:41 +0400 Subject: [PATCH 05/12] test new random generation --- src/qibo/quantum_info/random_ensembles.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 495081c1c9..3309bdc5b0 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -454,7 +454,9 @@ def random_statevector(dims: int, seed=None, backend=None): backend = GlobalBackend() local_state = ( - np.random.default_rng(seed) if seed is None or isinstance(seed, int) else seed + backend.np.random.default_rng(seed) + if seed is None or isinstance(seed, int) + else seed ) state = local_state.standard_normal(dims).astype(complex) From 380a80b30f4e311f8953d613f088f51298bc1ab7 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 27 Jan 2024 09:51:43 +0400 Subject: [PATCH 06/12] revamp seed --- src/qibo/quantum_info/random_ensembles.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index 3309bdc5b0..c2fac74687 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -453,11 +453,13 @@ def random_statevector(dims: int, seed=None, backend=None): if backend is None: # pragma: no cover backend = GlobalBackend() - local_state = ( - backend.np.random.default_rng(seed) - if seed is None or isinstance(seed, int) - else seed - ) + if seed is None or isinstance(seed, int): + if backend.__class__.__name__ in ["CupyBackend", "CuQuantumBackend"]: + local_state = backend.np.random.default_rng(seed) + else: + local_state = np.random.default_rng(seed) + else: + local_state = seed state = local_state.standard_normal(dims).astype(complex) state += 1.0j * local_state.standard_normal(dims) From 514e4143fe30352632353c2ea296ac97184f6b89 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sun, 28 Jan 2024 08:39:13 +0400 Subject: [PATCH 07/12] fix coverage --- src/qibo/quantum_info/random_ensembles.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index c2fac74687..abe58141b4 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -454,7 +454,10 @@ def random_statevector(dims: int, seed=None, backend=None): backend = GlobalBackend() if seed is None or isinstance(seed, int): - if backend.__class__.__name__ in ["CupyBackend", "CuQuantumBackend"]: + if backend.__class__.__name__ in [ + "CupyBackend", + "CuQuantumBackend", + ]: # pragma: no cover local_state = backend.np.random.default_rng(seed) else: local_state = np.random.default_rng(seed) From ba3de9c3173018d21791bb86afc11b45cb501e3a Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sat, 3 Feb 2024 18:40:02 +0400 Subject: [PATCH 08/12] fix local state and backend --- src/qibo/quantum_info/random_ensembles.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/qibo/quantum_info/random_ensembles.py b/src/qibo/quantum_info/random_ensembles.py index ddccc3b6f0..8c61aa38b7 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -419,19 +419,7 @@ def random_statevector(dims: int, seed=None, backend=None): TypeError, "seed must be either type int or numpy.random.Generator." ) - if backend is None: # pragma: no cover - backend = GlobalBackend() - - if seed is None or isinstance(seed, int): - if backend.__class__.__name__ in [ - "CupyBackend", - "CuQuantumBackend", - ]: # pragma: no cover - local_state = backend.np.random.default_rng(seed) - else: - local_state = np.random.default_rng(seed) - else: - local_state = seed + backend, local_state = _set_backend_and_local_state(seed, backend) state = local_state.standard_normal(dims).astype(complex) state += 1.0j * local_state.standard_normal(dims) From 287528256b3cdbb15489a63e1873e1fe6a0050d6 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sun, 4 Feb 2024 04:08:28 +0000 Subject: [PATCH 09/12] Update src/qibo/quantum_info/random_ensembles.py Co-authored-by: Alejandro Sopena <44305203+AlejandroSopena@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 8c61aa38b7..dd66a7e0a6 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -424,6 +424,7 @@ def random_statevector(dims: int, seed=None, backend=None): state = local_state.standard_normal(dims).astype(complex) state += 1.0j * local_state.standard_normal(dims) state /= np.linalg.norm(state) + state = backend.cast(state, dtype=state.dtype) return backend.cast(state, dtype=state.dtype) From 5192b14599a79b95ec684ab9f0296ea863dd2f82 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Sun, 4 Feb 2024 04:08:34 +0000 Subject: [PATCH 10/12] Update src/qibo/quantum_info/random_ensembles.py Co-authored-by: Alejandro Sopena <44305203+AlejandroSopena@users.noreply.github.com> --- 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 dd66a7e0a6..3d11c0d1ce 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -426,7 +426,7 @@ def random_statevector(dims: int, seed=None, backend=None): state /= np.linalg.norm(state) state = backend.cast(state, dtype=state.dtype) - return backend.cast(state, dtype=state.dtype) + return state def random_density_matrix( From 2bfd6ecbad2b348a7f4b47bbb033e25160aea742 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 04:08:54 +0000 Subject: [PATCH 11/12] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 3d11c0d1ce..18e2eedaa1 100644 --- a/src/qibo/quantum_info/random_ensembles.py +++ b/src/qibo/quantum_info/random_ensembles.py @@ -424,7 +424,7 @@ def random_statevector(dims: int, seed=None, backend=None): state = local_state.standard_normal(dims).astype(complex) state += 1.0j * local_state.standard_normal(dims) state /= np.linalg.norm(state) - state = backend.cast(state, dtype=state.dtype) + state = backend.cast(state, dtype=state.dtype) return state From a72a265b64e550e77ea642269d993b9ff809aa08 Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Mon, 5 Feb 2024 12:49:12 +0400 Subject: [PATCH 12/12] concise test --- tests/test_quantum_info_random.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_quantum_info_random.py b/tests/test_quantum_info_random.py index b91ad4862a..0749723637 100644 --- a/tests/test_quantum_info_random.py +++ b/tests/test_quantum_info_random.py @@ -238,8 +238,7 @@ def test_random_statevector(backend, seed): # tests if random statevector is a pure state dims = 4 state = random_statevector(dims, seed=seed, backend=backend) - backend.assert_allclose(purity(state) <= 1.0 + PRECISION_TOL, True) - backend.assert_allclose(purity(state) >= 1.0 - PRECISION_TOL, True) + backend.assert_allclose(abs(purity(state) - 1.0) < PRECISION_TOL, True) @pytest.mark.parametrize("normalize", [False, True])