From 2adecc9831fec935272803af458a481b4724cea5 Mon Sep 17 00:00:00 2001 From: MHoude2 Date: Wed, 10 Aug 2022 17:05:38 -0400 Subject: [PATCH 1/8] adding bloch-messiah decomposition --- thewalrus/decompositions.py | 66 ++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/thewalrus/decompositions.py b/thewalrus/decompositions.py index f1f5c462f..71ae4fb53 100644 --- a/thewalrus/decompositions.py +++ b/thewalrus/decompositions.py @@ -28,6 +28,7 @@ .. autosummary:: williamson symplectic_eigenvals + blochmessiah Code details ------------ @@ -36,7 +37,7 @@ from scipy.linalg import block_diag, sqrtm, schur from thewalrus.symplectic import sympmat - +from thewalrus.quantum.gaussian_checks import is_symplectic def williamson(V, rtol=1e-05, atol=1e-08): r"""Williamson decomposition of positive-definite (real) symmetric matrix. @@ -112,3 +113,66 @@ def symplectic_eigenvals(cov): M = int(len(cov) / 2) D, _ = williamson(cov) return np.diag(D)[:M] + + +def blochmessiah(S): + """Returns the Bloch-Messiah decomposition of a symplectic matrix S = uff @ dff @ vff + where uff and vff are orthogonal symplectic matrices and dff is a diagonal matrix + of the form diag(d1,d2,...,dn,d1^-1, d2^-1,...,dn^-1), + + Args: + S (array[float]): 2N x 2N real symplectic matrix + + Returns: + tupple(array[float], : orthogonal symplectic matrix uff + array[float], : diagional matrix dff + array[float]) : orthogonal symplectic matrix vff + """ + + N, m = S.shape + + if N != m: + return False + if N % 2 != 0: + return False + if not is_symplectic(S): + return False + # Changing Basis + R = (1 / np.sqrt(2)) * np.block( + [[np.eye(N // 2), 1j * np.eye(N // 2)], [np.eye(N // 2), -1j * np.eye(N // 2)]] + ) + Sc = R @ S @ np.conjugate(R).T + # Polar Decomposition + u1, d1, v1 = np.linalg.svd(Sc) + Sig = u1 @ np.diag(d1) @ np.conjugate(u1).T + Unitary = u1 @ v1 + # Blocks of Unitary and Hermitian symplectics + alpha = Unitary[0 : N // 2, 0 : N // 2] + beta = Sig[0 : N // 2, N // 2 : N] + # Bloch-Messiah in this Basis + u2, d2, v2 = np.linalg.svd(beta) + sval = np.arcsinh(d2) + takagibeta = u2 @ sqrtm(np.conjugate(u2).T @ (v2.T)) + uf = np.block( + [[takagibeta, 0 * takagibeta], [0 * takagibeta, np.conjugate(takagibeta)]] + ) + vf = np.block( + [ + [np.conjugate(takagibeta).T @ alpha, 0 * takagibeta], + [0 * takagibeta, np.conjugate(np.conjugate(takagibeta).T @ alpha)], + ] + ) + df = np.block( + [ + [np.diag(np.cosh(sval)), np.diag(np.sinh(sval))], + [np.diag(np.sinh(sval)), np.diag(np.cosh(sval))], + ] + ) + # Rotating Back to Original Basis + uff = np.conjugate(R).T @ uf @ R + vff = np.conjugate(R).T @ vf @ R + dff = np.conjugate(R).T @ df @ R + dff = np.real_if_close(dff) + vff = np.real_if_close(vff) + uff = np.real_if_close(uff) + return uff, dff, vff \ No newline at end of file From dd00588bf46fb03471da6ebe32fb4e17c37005d3 Mon Sep 17 00:00:00 2001 From: MHoude2 Date: Wed, 10 Aug 2022 17:22:41 -0400 Subject: [PATCH 2/8] Added tests --- thewalrus/tests/test_decompositions.py | 33 +++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/thewalrus/tests/test_decompositions.py b/thewalrus/tests/test_decompositions.py index 77b47b02b..dc076a915 100644 --- a/thewalrus/tests/test_decompositions.py +++ b/thewalrus/tests/test_decompositions.py @@ -18,8 +18,9 @@ from scipy.linalg import block_diag from thewalrus.random import random_interferometer as haar_measure -from thewalrus.decompositions import williamson +from thewalrus.decompositions import williamson, blochmessiah from thewalrus.symplectic import sympmat as omega +from thewalrus.quantum.gaussian_checks import is_symplectic class TestWilliamsonDecomposition: @@ -147,3 +148,33 @@ def test_mixed_state(self, create_cov, hbar, tol): assert np.allclose(sorted(nbar[:n]), sorted(nbar_in), atol=tol, rtol=0) # check S is symplectic assert np.allclose(S @ O @ S.T, O, atol=tol, rtol=0) + +class TestBlochMessiahDecomposition: + """Tests for the Williamson decomposition""" + @pytest.mark.parametrize("N", range(50, 500, 50)) + def test_blochmessiah_rand(N): + """Tests blochmessiah function for different matrix sizes.""" + S = random_symplectic(N) + u, d, v = blochmessiah(S) + assert np.allclose(u @ d @ v, S) + np.allclose(u.T @ u, np.eye(len(u))) + np.allclose(v.T @ v, np.eye(len(v))) + is_symplectic(u) + is_symplectic(v) + + def test_blochmessiah_odd(): + """Tests that odd matrices return False in blochmessiah.""" + S = np.random.rand(5, 5) + assert not blochmessiah(S) + + def test_blochmessiah_rect(): + """Tests that rectangular matrices return False in blochmessiah""" + S = np.random.rand(4, 5) + assert not blochmessiah(S) + + def test_blochmessiah_false(): + """Tests that non-symplectic mattrices return False in blochmessiah""" + S = np.random.rand(4, 4) + assert not blochmessiah(S) + + \ No newline at end of file From de8abdde80cad63f466c79684048359a88f885f7 Mon Sep 17 00:00:00 2001 From: MHoude2 Date: Wed, 10 Aug 2022 17:29:10 -0400 Subject: [PATCH 3/8] added end space --- thewalrus/decompositions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/thewalrus/decompositions.py b/thewalrus/decompositions.py index 71ae4fb53..19395a972 100644 --- a/thewalrus/decompositions.py +++ b/thewalrus/decompositions.py @@ -175,4 +175,5 @@ def blochmessiah(S): dff = np.real_if_close(dff) vff = np.real_if_close(vff) uff = np.real_if_close(uff) - return uff, dff, vff \ No newline at end of file + return uff, dff, vff + \ No newline at end of file From dc1b02819644873dea98988c0019b63c4233f868 Mon Sep 17 00:00:00 2001 From: MHoude2 Date: Wed, 10 Aug 2022 17:45:04 -0400 Subject: [PATCH 4/8] changelog modif (still need PR # and link) --- .github/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 125e1954c..d2002bcd8 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,6 +1,8 @@ # Version 0.19.0-dev ### New features +* New function to produce Bloch-Messiah decomposition of symplectic matrices. + * New functions for calculating properties of distinguihable squeezed states of light having passed through an interferometer. [#326](https://github.com/XanaduAI/thewalrus/pull/326) * New function `ltor` is added which allows `threshold_detector_prob` to act more consistently on displaced and zero-mean Gaussian states. [#317](https://github.com/XanaduAI/thewalrus/pull/317) From f2266af40a0cfd0f609cc6f9b1d58f141f7f4582 Mon Sep 17 00:00:00 2001 From: Nicolas Quesada Date: Thu, 11 Aug 2022 09:50:03 -0400 Subject: [PATCH 5/8] Passes black --- thewalrus/decompositions.py | 6 ++---- thewalrus/tests/test_decompositions.py | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/thewalrus/decompositions.py b/thewalrus/decompositions.py index 19395a972..e179ec43f 100644 --- a/thewalrus/decompositions.py +++ b/thewalrus/decompositions.py @@ -39,6 +39,7 @@ from thewalrus.symplectic import sympmat from thewalrus.quantum.gaussian_checks import is_symplectic + def williamson(V, rtol=1e-05, atol=1e-08): r"""Williamson decomposition of positive-definite (real) symmetric matrix. @@ -153,9 +154,7 @@ def blochmessiah(S): u2, d2, v2 = np.linalg.svd(beta) sval = np.arcsinh(d2) takagibeta = u2 @ sqrtm(np.conjugate(u2).T @ (v2.T)) - uf = np.block( - [[takagibeta, 0 * takagibeta], [0 * takagibeta, np.conjugate(takagibeta)]] - ) + uf = np.block([[takagibeta, 0 * takagibeta], [0 * takagibeta, np.conjugate(takagibeta)]]) vf = np.block( [ [np.conjugate(takagibeta).T @ alpha, 0 * takagibeta], @@ -176,4 +175,3 @@ def blochmessiah(S): vff = np.real_if_close(vff) uff = np.real_if_close(uff) return uff, dff, vff - \ No newline at end of file diff --git a/thewalrus/tests/test_decompositions.py b/thewalrus/tests/test_decompositions.py index dc076a915..cd68e9ea3 100644 --- a/thewalrus/tests/test_decompositions.py +++ b/thewalrus/tests/test_decompositions.py @@ -149,8 +149,10 @@ def test_mixed_state(self, create_cov, hbar, tol): # check S is symplectic assert np.allclose(S @ O @ S.T, O, atol=tol, rtol=0) + class TestBlochMessiahDecomposition: """Tests for the Williamson decomposition""" + @pytest.mark.parametrize("N", range(50, 500, 50)) def test_blochmessiah_rand(N): """Tests blochmessiah function for different matrix sizes.""" @@ -176,5 +178,3 @@ def test_blochmessiah_false(): """Tests that non-symplectic mattrices return False in blochmessiah""" S = np.random.rand(4, 4) assert not blochmessiah(S) - - \ No newline at end of file From 470dfbf6378a0ca11db19d1578845fde47d62ec6 Mon Sep 17 00:00:00 2001 From: Nicolas Quesada Date: Thu, 11 Aug 2022 09:56:23 -0400 Subject: [PATCH 6/8] Fixes tests --- thewalrus/tests/test_decompositions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/thewalrus/tests/test_decompositions.py b/thewalrus/tests/test_decompositions.py index cd68e9ea3..d3882da71 100644 --- a/thewalrus/tests/test_decompositions.py +++ b/thewalrus/tests/test_decompositions.py @@ -18,6 +18,7 @@ from scipy.linalg import block_diag from thewalrus.random import random_interferometer as haar_measure +from thewalrus.random import random_symplectic from thewalrus.decompositions import williamson, blochmessiah from thewalrus.symplectic import sympmat as omega from thewalrus.quantum.gaussian_checks import is_symplectic @@ -154,7 +155,7 @@ class TestBlochMessiahDecomposition: """Tests for the Williamson decomposition""" @pytest.mark.parametrize("N", range(50, 500, 50)) - def test_blochmessiah_rand(N): + def test_blochmessiah_rand(self, N): """Tests blochmessiah function for different matrix sizes.""" S = random_symplectic(N) u, d, v = blochmessiah(S) @@ -164,17 +165,17 @@ def test_blochmessiah_rand(N): is_symplectic(u) is_symplectic(v) - def test_blochmessiah_odd(): + def test_blochmessiah_odd(self): """Tests that odd matrices return False in blochmessiah.""" S = np.random.rand(5, 5) assert not blochmessiah(S) - def test_blochmessiah_rect(): + def test_blochmessiah_rect(self): """Tests that rectangular matrices return False in blochmessiah""" S = np.random.rand(4, 5) assert not blochmessiah(S) - def test_blochmessiah_false(): + def test_blochmessiah_false(self): """Tests that non-symplectic mattrices return False in blochmessiah""" S = np.random.rand(4, 4) assert not blochmessiah(S) From fff267b6c79eef8ab4e7c4ff69499343008cabfc Mon Sep 17 00:00:00 2001 From: Nicolas Quesada <991946+nquesada@users.noreply.github.com> Date: Sat, 27 Aug 2022 12:10:12 -0400 Subject: [PATCH 7/8] Update ACKNOWLEDGMENTS.md --- .github/ACKNOWLEDGMENTS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ACKNOWLEDGMENTS.md b/.github/ACKNOWLEDGMENTS.md index c9ba3d427..d67ea4afa 100644 --- a/.github/ACKNOWLEDGMENTS.md +++ b/.github/ACKNOWLEDGMENTS.md @@ -62,4 +62,6 @@ * [Fabian Laudenbach](https://github.com/fab1an-q) (Xanadu) - :revolving_hearts: Entangler of hearts -* [Martin Houde](https://github.com/MHoude2) (Polytechnique Montréal) - :upside_down: Minister of amplification \ No newline at end of file +* [Martin Houde](https://github.com/MHoude2) (Polytechnique Montréal) - :upside_down: Minister of amplification + +* Will McCutcheon (Heriot-Watt University) - From 355fe9c7e49396992bea8b01f519d44f0363e8ed Mon Sep 17 00:00:00 2001 From: Nicolas Quesada <991946+nquesada@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:03:08 -0400 Subject: [PATCH 8/8] Update ACKNOWLEDGMENTS.md --- .github/ACKNOWLEDGMENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ACKNOWLEDGMENTS.md b/.github/ACKNOWLEDGMENTS.md index d67ea4afa..7281c8060 100644 --- a/.github/ACKNOWLEDGMENTS.md +++ b/.github/ACKNOWLEDGMENTS.md @@ -62,6 +62,6 @@ * [Fabian Laudenbach](https://github.com/fab1an-q) (Xanadu) - :revolving_hearts: Entangler of hearts -* [Martin Houde](https://github.com/MHoude2) (Polytechnique Montréal) - :upside_down: Minister of amplification +* [Martin Houde](https://github.com/MHoude2) (Polytechnique Montréal) - 🙃 Minister of amplification -* Will McCutcheon (Heriot-Watt University) - +* Will McCutcheon (Heriot-Watt University) - 🧅 Gaussian Onion Merchant