From 185afccb029a8f96471cf8d503f728b00ea4221e Mon Sep 17 00:00:00 2001 From: Nicolas Quesada Date: Wed, 21 Apr 2021 15:27:14 -0400 Subject: [PATCH] Basis ordering (#237) * first working version * puts all the tests in the same class * making bots happy * updates autosummary * One extra test * updates changelog * Fixes error message * Update thewalrus/symplectic.py Co-authored-by: Josh Izaac * Update thewalrus/symplectic.py Co-authored-by: Josh Izaac * Update thewalrus/symplectic.py Co-authored-by: Josh Izaac --- .github/CHANGELOG.md | 2 ++ thewalrus/symplectic.py | 52 ++++++++++++++++++++++++++++ thewalrus/tests/test_symplectic.py | 54 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 353aa64e4..18e68c989 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -11,6 +11,8 @@ * Adds new functions `total_photon_number_distribution` and `characteristic_function` to study properties of the total photon number distribution of a `k` identical lossy squeezers. [#230](https://github.com/XanaduAI/thewalrus/pull/230/) +* Adds new functions `xxpp_to_xpxp` and `xpxp_to_xxpp` in the `symplectic` module to swap the ordering of the quadrature operators in vectors and matrices. [#237](https://github.com/XanaduAI/thewalrus/pull/237/) + ### Improvements diff --git a/thewalrus/symplectic.py b/thewalrus/symplectic.py index 8375d1506..74ef1efc7 100644 --- a/thewalrus/symplectic.py +++ b/thewalrus/symplectic.py @@ -30,6 +30,8 @@ reduced_state is_symplectic sympmat + xxpp_to_xpxp + xpxp_to_xxpp Gaussian states --------------- @@ -361,3 +363,53 @@ def autonne(A, rtol=1e-05, atol=1e-08, svd_order=True): if svd_order: return (vals[n : 2 * n])[::-1], U[:, ::-1] return vals[n : 2 * n], U + +def xxpp_to_xpxp(S): + """Permutes the entries of the input from xxpp ordering to xpxp ordering. + + Args: + S (array): input even dimensional square matrix or array + + Returns: + (array): permuted matrix or array + """ + shape = S.shape + n = shape[0] + + if n % 2 != 0: + raise ValueError("The input array is not even-dimensional") + + n = n // 2 + ind = np.arange(2 * n).reshape(2, -1).T.flatten() + + if len(shape) == 2: + if shape[0] != shape[1]: + raise ValueError("The input matrix is not square") + return S[:, ind][ind] + + return S[ind] + +def xpxp_to_xxpp(S): + """Permutes the entries of the input from xpxp ordering to xxpp ordering. + + Args: + S (array): input even dimensional square matrix or vector + + Returns: + (array): permuted matrix or vector + """ + shape = S.shape + n = shape[0] + + if n % 2 != 0: + raise ValueError("The input array is not even-dimensional") + + n = n // 2 + ind = np.arange(2 * n).reshape(-1, 2).T.flatten() + + if len(shape) == 2: + if shape[0] != shape[1]: + raise ValueError("The input matrix is not square") + return S[:, ind][ind] + + return S[ind] diff --git a/thewalrus/tests/test_symplectic.py b/thewalrus/tests/test_symplectic.py index 50e761c83..3c1e5be39 100644 --- a/thewalrus/tests/test_symplectic.py +++ b/thewalrus/tests/test_symplectic.py @@ -658,3 +658,57 @@ def test_autonne_error(): A = np.random.rand(n, m) with pytest.raises(ValueError, match="The input matrix is not symmetric"): symplectic.autonne(A) + + +class TestPhaseSpaceFunctions: + """Tests for the shared phase space operations""" + + def test_means_changebasis(self): + """Test the change of basis function applied to vectors. This function + converts from xp to symmetric ordering, and vice versa.""" + means_xp = np.array([1, 2, 3, 4, 5, 6]) + means_symmetric = np.array([1, 4, 2, 5, 3, 6]) + + assert np.all(symplectic.xxpp_to_xpxp(means_xp) == means_symmetric) + assert np.all(symplectic.xpxp_to_xxpp(means_symmetric) == means_xp) + + def test_cov_changebasis(self): + """Test the change of basis function applied to matrices. This function + converts from xp to symmetric ordering, and vice versa.""" + cov_xp = np.array( + [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]] + ) + + cov_symmetric = np.array( + [[0, 2, 1, 3], [8, 10, 9, 11], [4, 6, 5, 7], [12, 14, 13, 15]] + ) + + assert np.all(symplectic.xxpp_to_xpxp(cov_xp) == cov_symmetric) + assert np.all(symplectic.xpxp_to_xxpp(cov_symmetric) == cov_xp) + + @pytest.mark.parametrize("fun", [symplectic.xxpp_to_xpxp, symplectic.xpxp_to_xxpp]) + def test_change_basis_raises_not_square(self, fun): + """Test correct error is raised when a non-square matrix is passed""" + A = np.random.rand(4,6) + with pytest.raises(ValueError, match="The input matrix is not square"): + fun(A) + + @pytest.mark.parametrize("fun", [symplectic.xxpp_to_xpxp, symplectic.xpxp_to_xxpp]) + @pytest.mark.parametrize("dim", [1, 2]) + def test_change_basis_raises_not_even(self, fun, dim): + """Test correct error is raised when a non-even-dimensional array is passed""" + size = (5,) * dim + A = np.random.rand(*size) + with pytest.raises(ValueError, match="The input array is not even-dimensional"): + fun(A) + + @pytest.mark.parametrize("dim", [2, 4, 6, 8]) + def test_functional_inverse(self, dim): + """Check that xxpp_to_xpxp is the inverse of xpxp_to_xxpp and viceversa""" + M = np.random.rand(dim, dim) + assert np.all(M == symplectic.xxpp_to_xpxp(symplectic.xpxp_to_xxpp(M))) + assert np.all(M == symplectic.xpxp_to_xxpp(symplectic.xxpp_to_xpxp(M))) + + v = np.random.rand(dim) + assert np.all(v == symplectic.xxpp_to_xpxp(symplectic.xpxp_to_xxpp(v))) + assert np.all(v == symplectic.xpxp_to_xxpp(symplectic.xxpp_to_xpxp(v)))