Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal modes #354

Open
wants to merge 149 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 119 commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
267f409
first commit on internal_modes, with first draft of pnr_prob function
jakeffbulmer Dec 22, 2021
d8f122d
make numba import consistent
jakeffbulmer Dec 22, 2021
e385563
add fully distinguishable functionality
jakeffbulmer Dec 22, 2021
5ffa2e3
new name
jakeffbulmer Dec 22, 2021
018fb6f
include __init__ file
jakeffbulmer Dec 22, 2021
ccdefb7
include single mode density matrix function
jakeffbulmer Dec 22, 2021
d0c6f33
missing import
jakeffbulmer Dec 22, 2021
34f9196
adding functionality for preparing internal mode covariance matrices
jakeffbulmer Dec 22, 2021
56dad89
starting to add tests
jakeffbulmer Dec 22, 2021
3d219c9
new test comparing 3 methods for fully distinguishable GBS
jakeffbulmer Dec 22, 2021
726132f
adding new tests for distinguishable gbs
jakeffbulmer Jan 7, 2022
ef34d39
adding tests for density_matrix_single_mode with GKP circuits
jakeffbulmer Jan 7, 2022
86656b1
heralded density matrix test
jakeffbulmer Jan 7, 2022
985a4d7
add some comments
jakeffbulmer Jan 14, 2022
0aede73
not quite sure to be honest
jakeffbulmer Jan 28, 2022
e497213
fix prepare cov issues
jakeffbulmer Jan 28, 2022
97300df
Bug fix found from other repo
DavidSPhillips Mar 8, 2022
faac228
Black reformatting
DavidSPhillips Mar 8, 2022
736de0f
Adding weights to matrix orthonormalisation
DavidSPhillips Apr 13, 2022
2561191
Making sure int32 arrays aren't causing problems in numba
DavidSPhillips May 17, 2022
083aff0
Resolved merge conflict by using master version
DavidSPhillips May 19, 2022
7ad6bf0
Ignoring all pycache folders
DavidSPhillips May 19, 2022
4c4bcf9
Merge branch 'master' into internal_modes
DavidSPhillips Jul 12, 2022
b773ea1
Can herald density matrix in arbitrary spatial mode
DavidSPhillips Jul 12, 2022
71f2f5d
Option to add LO overlaps added to density matrix
DavidSPhillips Jul 12, 2022
042c537
Extra check added
DavidSPhillips Jul 13, 2022
a34fbbd
Addition of hbar as a kwarg where applicable
DavidSPhillips Jul 13, 2022
264dc67
Further changes to make density matrix compatible with numba jit
DavidSPhillips Jul 15, 2022
459c5b0
Removing pycache folder
DavidSPhillips Jul 15, 2022
ec3e04a
Further tidying up
DavidSPhillips Jul 15, 2022
d9b31c8
Merge branch 'master' into internal_modes
DavidSPhillips Jul 29, 2022
a4fbe8b
Fixing issue with svd conditon
DavidSPhillips Jul 29, 2022
4c094f3
Moving LO overlap constraint check to non-jit function
DavidSPhillips Jul 29, 2022
70f6c52
removing pycache
DavidSPhillips Jul 29, 2022
bd7439d
Merge branch 'master' into internal_modes
DavidSPhillips Aug 23, 2022
9bf38f0
Merge branch 'master' into internal_modes
DavidSPhillips Sep 6, 2022
8a88eab
Merge branch 'master' into internal_modes
DavidSPhillips Sep 13, 2022
4ef6e43
Updating internal mode test functions with updates
DavidSPhillips Sep 23, 2022
9fc7eee
Merge branch 'master' into internal_modes
DavidSPhillips Sep 23, 2022
41df752
Removing strawberryfields dependency on test_internal_modes
DavidSPhillips Sep 28, 2022
388aeba
Fixing global phase on eigendecomposition and Takagi
DavidSPhillips Oct 11, 2022
833333d
Updating internal modes test functions
DavidSPhillips Oct 11, 2022
88ab802
Fixing mistake in previous commits
DavidSPhillips Oct 12, 2022
1bff0eb
Correcting decomposition issues and more real if close added
DavidSPhillips Oct 13, 2022
4cee917
Takagi normalisation isn't going to work
DavidSPhillips Oct 14, 2022
f84cb57
Adding takagi decomposition similar to strawberryfields
DavidSPhillips Oct 17, 2022
ed92bb8
Tidying up
DavidSPhillips Oct 17, 2022
a1e1598
Adding test for new Takagi function in symplectic
DavidSPhillips Oct 18, 2022
51c40a7
Adding autonne and takagi to autosummary
DavidSPhillips Oct 18, 2022
865d7f6
Tests for orthonormal_basis and state_prep added
DavidSPhillips Oct 18, 2022
ba177ef
Adding test function for prepare_cov
DavidSPhillips Oct 18, 2022
61c6b67
Readding sign convention to W matrices from Takagi output
DavidSPhillips Oct 19, 2022
301405b
Further improvements to Takagi function
DavidSPhillips Oct 20, 2022
e419c3a
Updates to docstring for Takagi function
DavidSPhillips Oct 20, 2022
f2f11d6
Making location of test functions more obvious in test_internal_modes
DavidSPhillips Oct 20, 2022
3aa4f4d
Moving Autonne and Takagi from symplectic to decompositions
DavidSPhillips Oct 21, 2022
c7d6561
Missing comma added
DavidSPhillips Oct 26, 2022
0dfc3e5
Further missing commas added
DavidSPhillips Oct 26, 2022
08fb4aa
Seeing if this solves the repoze.lru problem
DavidSPhillips Oct 26, 2022
035cea9
Changing test internal modes takagi import to decompositions
DavidSPhillips Oct 26, 2022
c60d1b9
Removing typing conventions
DavidSPhillips Oct 26, 2022
339cb2a
From itertools adding chain to test_internal_modes and groupby to dec…
DavidSPhillips Oct 26, 2022
e58dec3
Fixing issue that causes NaNs in takagi
DavidSPhillips Oct 26, 2022
9b01d52
Fixing issue that causes NaNs in takagi in another place
DavidSPhillips Oct 26, 2022
1680034
Fixing takagi not reversing order when false and changing np.conjugat…
DavidSPhillips Oct 26, 2022
f5984f0
Passes black in all the files
Oct 28, 2022
24dbcea
Making the linter a bit less unhappy
Oct 28, 2022
ed5b182
Making the linter a bit less unhappy
Oct 28, 2022
d5519ac
Making the linter a bit less unhappy
Oct 28, 2022
037deeb
Making the linter a bit less unhappy
Oct 28, 2022
5cfcd0a
Passing black
Oct 28, 2022
f7e8d28
Making the linter a bit less unhappy
Oct 28, 2022
5a4b040
Passing black
Oct 28, 2022
e691e18
Making pylint less unhappy
Oct 28, 2022
b80f951
Changes in _hafnian.py
Oct 28, 2022
61fb1a5
Changes atol
Oct 28, 2022
4084080
Passes black
Oct 28, 2022
02661b0
simplfies code
Oct 28, 2022
f58d3fc
passes black
Oct 28, 2022
b4a8587
minor changes
Oct 28, 2022
6171eab
minor changes
Oct 28, 2022
46d037f
Most test pass. Had to remove memoization in guan code function
Nov 10, 2022
936a99d
Passes black
Nov 10, 2022
4861386
Making the linter happy
Nov 10, 2022
745a6bc
Decreasing threshold for throwing vacuum modes away
DavidSPhillips Nov 21, 2022
da5016c
Making black happy
DavidSPhillips Nov 21, 2022
1039bd9
Setting state prep thresh to 0 for tests
DavidSPhillips Nov 21, 2022
100c522
Black again
DavidSPhillips Nov 21, 2022
99c1c1d
Setting prepare cov threshold to 0 for tests
DavidSPhillips Nov 21, 2022
d839f09
Correcting error in previous commit
DavidSPhillips Nov 21, 2022
20f4633
Fixing mistake in test density matrix
DavidSPhillips Nov 21, 2022
2ca5d68
Changes to test density matrix
DavidSPhillips Nov 21, 2022
55288b6
Changes to test density matrix again
DavidSPhillips Nov 21, 2022
df23fbf
Cutoff kwarg to test density matrix
DavidSPhillips Nov 21, 2022
32193e2
Further changes to test density matrix
DavidSPhillips Nov 21, 2022
3b2a749
Adding first test for Lowdin modes
DavidSPhillips Nov 23, 2022
37228d3
Typo
DavidSPhillips Nov 23, 2022
817905a
Adding test for finding LO overlaps with Lowdin modes
DavidSPhillips Nov 23, 2022
40787cd
Adding test for finding density matrix in specific LO mode
DavidSPhillips Nov 23, 2022
f587a24
Adding extra import
DavidSPhillips Nov 23, 2022
75f3800
Adding tqdm import
DavidSPhillips Nov 23, 2022
36df0c1
Removing tqdm entirely
DavidSPhillips Nov 23, 2022
245425d
Adding test for heradled single photon
DavidSPhillips Nov 24, 2022
9ea0901
Some extra checks
DavidSPhillips Nov 25, 2022
6b90010
Update thewalrus/internal_modes/prepare_cov.py
DavidSPhillips Nov 25, 2022
3f1c540
Update thewalrus/internal_modes/prepare_cov.py
DavidSPhillips Nov 25, 2022
e45e8f6
Changing Q to cov for readability
DavidSPhillips Nov 25, 2022
09c61a3
Further changes for clarity and reducing unnecessary calculations
DavidSPhillips Nov 25, 2022
1ef13de
Using inbuilt function for 0), pack-reused 0
DavidSPhillips Nov 25, 2022
b0f124e
More changes for clarity and readability
DavidSPhillips Nov 25, 2022
b54ea57
Removing TODO as it's already done
DavidSPhillips Nov 30, 2022
9dfe6ab
Fix in nb_binom for windows
DavidSPhillips Nov 30, 2022
ef3c9a4
Chaning the name of useful_tools to utils
DavidSPhillips Nov 30, 2022
a60154d
Making black happy
DavidSPhillips Nov 30, 2022
b3b6f17
Using beam_splitter and expand instead of writing explicit unitaries …
DavidSPhillips Dec 1, 2022
6ffdb0f
Testing O matrix explicitly and adding more coverage for testing
DavidSPhillips Dec 1, 2022
77e9981
Improving pylint
DavidSPhillips Dec 1, 2022
e6057ee
More things for pylint
DavidSPhillips Dec 1, 2022
127189d
More pylint disabling for things that won't be changed
DavidSPhillips Dec 1, 2022
adfc7ab
Update thewalrus/internal_modes/distinguishable_pnr_prob.py
nquesada Dec 10, 2022
98ece11
Internal modes update (#355)
rachelchadwick Dec 16, 2022
801c741
Adding assertion tests to density_matrix_internal_modes
DavidSPhillips Dec 16, 2022
691e74e
black
DavidSPhillips Dec 16, 2022
f7d7372
Adding assertion tests to orthonormal_basis
DavidSPhillips Dec 16, 2022
439c7e4
Adding assertion tests to state_prep
DavidSPhillips Dec 16, 2022
108eba7
Adding assertion tests to prepare_cov
DavidSPhillips Dec 16, 2022
ae971e6
Further codecov stuff
DavidSPhillips Feb 22, 2023
9fae6be
Further codecov stuff again
DavidSPhillips Feb 22, 2023
1370740
Setting normalize=False as default in internal modes desnity matrix
DavidSPhillips Mar 1, 2023
a3e2233
Running black through selected files
DavidSPhillips Mar 1, 2023
90f017f
Normalising density matrices for tests
DavidSPhillips Mar 1, 2023
5492942
Extra normalize added to test_mixed_heralded_photon
DavidSPhillips Mar 1, 2023
bb88521
Merge branch 'master' into internal_modes
DavidSPhillips Mar 1, 2023
50696e3
Seeing if this solves black issue
DavidSPhillips Mar 1, 2023
063fd4f
More black issues
DavidSPhillips Mar 1, 2023
307e760
Merge branch 'master' into internal_modes
DavidSPhillips Mar 1, 2023
0fc18b8
Merge branch 'master' into internal_modes
DavidSPhillips Mar 6, 2023
8b67753
Merge branch 'master' into internal_modes
DavidSPhillips Mar 7, 2023
c625410
Merging master into internal_modes (#365)
nquesada May 31, 2023
e9c7d09
Merge branch 'master' into internal_modes
nquesada May 31, 2023
a00ca6d
Merge branch 'master' into internal_modes
nquesada Jul 17, 2023
ece2cb4
Merge branch 'master' into internal_modes
nquesada Jul 18, 2023
e6de063
Adding probability tests (#368)
nquesada Nov 4, 2023
127a1a7
Merge branch 'master' into internal_modes
nquesada Feb 13, 2024
7c63905
Merge branch 'master' into internal_modes
elib20 Apr 29, 2024
c390f84
Off diagonal (#383)
nquesada May 1, 2024
7b227d8
fix segfaults
timmysilv Jun 20, 2024
f7358e6
add assert back as comment for posterity
timmysilv Jun 20, 2024
2b249e9
Merge branch 'master' into internal_modes
RyosukeNORO Jul 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ examples/*ipynp*
.eggs/*
*.egg-info*
thewalrus.egg-info
thewalrus/__pycache__/*
*/__pycache__/*
thewalrus/*.so

tests/__pycache__/*
tests/*.o
coverage_html_report
coverage.xml
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ numba==0.55.1
numpy==1.21.5
scipy==1.8.0
sympy==1.10
repoze.lru>=0.7
29 changes: 28 additions & 1 deletion thewalrus/_hafnian.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def nb_binom(n, k): # pragma: no cover
"""
if k < 0 or k > n:
return 0
if k in (0, n):
if k == 0 or k == n:
return 1
binom = 1
for i in range(min(k, n - k)):
Expand Down Expand Up @@ -285,6 +285,33 @@ def f_loop_odd(AX, AX_S, XD_S, D_S, n, oddloop, oddVX_S): # pragma: no cover
return comb[count, :]


@numba.jit(nopython=True, cache=True)
def f_from_powertrace(powertraces, n):
"""Evaluate the polynomial coefficients of the function in the eigenvalue-trace formula, using the powertraces.

Args:
pow_traces (array): power traces of some matrix
n (int): number of polynomial coefficients to compute

Returns:
array: polynomial coefficients
"""
count = 0
comb = np.zeros((2, n // 2 + 1), dtype=np.complex128)
comb[0, 0] = 1
for i in range(1, n // 2 + 1):
factor = powertraces[i] / (2 * i)
powfactor = 1.0
count = 1 - count
comb[count, :] = comb[1 - count, :]
for j in range(1, n // (2 * i) + 1):
powfactor = powfactor * factor / j
for k in range(i * j + 1, n // 2 + 2):
comb[count, k - 1] += comb[1 - count, k - i * j - 1] * powfactor

return comb[count, n // 2]


@numba.jit(nopython=True, cache=True)
def get_AX_S(kept_edges, A): # pragma: no cover
"""Given the kept edges, return the appropriate scaled submatrices to compute ``f``.
Expand Down
276 changes: 202 additions & 74 deletions thewalrus/decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,220 @@
-------

.. autosummary::
williamson
symplectic_eigenvals
autonne
blochmessiah
symplectic_eigenvals
takagi
williamson

Code details
------------
"""
from itertools import groupby
import numpy as np

from scipy.linalg import block_diag, sqrtm, schur
from thewalrus.symplectic import sympmat
from thewalrus.quantum.gaussian_checks import is_symplectic


def autonne(A, rtol=1e-05, atol=1e-08, svd_order=True):
r"""Autonne-Takagi decomposition of a complex symmetric (not Hermitian!) matrix.

Args:
A (array): square, symmetric matrix
rtol (float): the relative tolerance parameter between ``A`` and ``A.T``
atol (float): the absolute tolerance parameter between ``A`` and ``A.T``
svd_order (boolean): whether to return result by ordering the singular values of ``A`` in descending (``True``) or asceding (``False``) order.

Returns:
tuple[array, array]: (r, U), where r are the singular values,
and U is the Autonne-Takagi unitary, such that :math:`A = U \diag(r) U^T`.
"""
n, m = A.shape
if n != m:
raise ValueError("The input matrix is not square")
if not np.allclose(A, A.T, rtol=rtol, atol=atol):
raise ValueError("The input matrix is not symmetric")
Areal = A.real
Aimag = A.imag

B = np.empty((2 * n, 2 * n))
B[:n, :n] = Areal
B[n : 2 * n, :n] = Aimag
B[:n, n : 2 * n] = Aimag
B[n : 2 * n, n : 2 * n] = -Areal
vals, vects = np.linalg.eigh(B)
U = vects[:n, n : 2 * n] + 1j * vects[n : 2 * n, n : 2 * n]
if svd_order:
return (vals[n : 2 * n])[::-1], U[:, ::-1]
return vals[n : 2 * n], U


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, _ = S.shape

if not is_symplectic(S):
raise ValueError("Input matrix is not symplectic.")

# 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 @ R.conj().T
# Polar Decomposition
u1, d1, v1 = np.linalg.svd(Sc)
Sig = u1 @ np.diag(d1) @ u1.conj().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(u2.conj().T @ (v2.T))
uf = np.block([[takagibeta, 0 * takagibeta], [0 * takagibeta, takagibeta.conj()]])
vf = np.block(
[
[takagibeta.conj().T @ alpha, 0 * takagibeta],
[0 * takagibeta, (takagibeta.conj().T @ alpha).conj()],
]
)
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 = R.conj().T @ uf @ R
vff = R.conj().T @ vf @ R
dff = R.conj().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


def symplectic_eigenvals(cov):
r"""Returns the symplectic eigenvalues of a covariance matrix.

Args:
cov (array): a covariance matrix

Returns:
(array): symplectic eigenvalues
"""
M = int(len(cov) / 2)
D, _ = williamson(cov)
return np.diag(D)[:M]


def takagi(A, rtol=1e-05, atol=1e-08, rounding=13, svd_order=True):
r"""Autonne-Takagi decomposition of a complex symmetric (not Hermitian!) matrix, this is the strawberryfields version.
Note that singular values of A are considered equal if they are equal after np.round(values, rounding).
See :cite:`cariolaro2016` and references therein for a derivation.
Args:
A (array[complex]): square, symmetric matrix
rounding (int): the number of decimal places to use when rounding the singular values of A
rtol (float): the relative tolerance parameter between ``A`` and ``A.T``
atol (float): the absolute tolerance parameter between ``A`` and ``A.T``
svd_order (boolean): whether to return result by ordering the singular values of ``A`` in descending (``True``) or asceding (``False``) order.
Returns:
tuple[array, array]: (rl, U), where rl are the (rounded) singular values,
and U is the Takagi unitary, such that :math:`N = U \diag(rl) U^T`.
"""
# pylint: disable=too-many-statements,too-many-branches
n, m = A.shape
if n != m:
raise ValueError("The input matrix is not square")
if not np.allclose(A, A.T, rtol=rtol, atol=atol):
raise ValueError("The input matrix is not symmetric")

A = np.real_if_close(A)

if np.allclose(A, 0):
return np.zeros(n), np.eye(n)

if np.isrealobj(A):
# If the matrix A is real one can be more clever and use its eigendecomposition
ls, U = np.linalg.eigh(A)
U = U / np.exp(1j * np.angle(U)[0])
ls = np.round(ls, rounding)
vals = np.abs(ls) # These are the Takagi eigenvalues
phases = -np.ones(vals.shape[0], dtype=np.complex128)
for j, l in enumerate(ls):
if np.allclose(l, 0) or l > 0:
phases[j] = 1
phases = np.sqrt(phases)
Uc = U @ np.diag(phases) # One needs to readjust the phases
signs = np.sign(Uc.real)[0]
for k, s in enumerate(signs):
if np.allclose(s, 0):
signs[k] = 1
Uc = np.real_if_close(Uc / signs)
list_vals = [(vals[i], i) for i in range(len(vals))]
# And also rearrange the unitary and values so that they are decreasingly ordered
list_vals.sort(reverse=svd_order)
sorted_ls, permutation = zip(*list_vals)
return np.array(sorted_ls), Uc[:, np.array(permutation)]

phi = np.angle(A[0, 0])
Amr = np.real_if_close(np.exp(-1j * phi) * A)
if np.isrealobj(Amr):
vals, U = takagi(Amr, rtol=rtol, atol=atol, rounding=rounding, svd_order=svd_order)
return vals, U * np.exp(1j * phi / 2)

v, l, ws = np.linalg.svd(A)
if not svd_order:
v, l, ws = v[:, ::-1], l[::-1], ws[::-1, :]
w = ws.conj().T
rl = np.round(l, rounding)

# Generate list with degenerancies
result = []
for k, g in groupby(rl):
result.append(list(g))

# Generate lists containing the columns that correspond to degenerancies
kk = 0
for k in result:
for ind, _ in enumerate(k):
k[ind] = kk
kk = kk + 1

# Generate the lists with the degenerate column subspaces
vas = []
was = []
for i in result:
vas.append(v[:, i])
was.append(w[:, i])

# Generate the matrices qs of the degenerate subspaces
qs = []
for i in range(len(result)):
qs.append(sqrtm(np.transpose(vas[i]) @ was[i]))

# Construct the Takagi unitary
qb = block_diag(*qs)

U = v @ np.conj(qb)
return rl, U


def williamson(V, rtol=1e-05, atol=1e-08):
r"""Williamson decomposition of positive-definite (real) symmetric matrix.

Expand Down Expand Up @@ -100,75 +300,3 @@ def williamson(V, rtol=1e-05, atol=1e-08):
Db = np.diag(dd + dd)
S = Mm12 @ Ktt @ sqrtm(Db)
return Db, np.linalg.inv(S).T


def symplectic_eigenvals(cov):
r"""Returns the symplectic eigenvalues of a covariance matrix.

Args:
cov (array): a covariance matrix

Returns:
(array): symplectic eigenvalues
"""
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, _ = S.shape

if not is_symplectic(S):
raise ValueError("Input matrix is not symplectic.")

# 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be deleted

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It hasn't been deleted, when I moved autonne here and added takagi I moved the functions such that they're in alphabetical order. All is still there, just reordered.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was that necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that some other scripts had been orgnised in that way, so when I moved the functions I had the choice to just put them underneath or organise them in that way. I chose the latter.

21 changes: 21 additions & 0 deletions thewalrus/internal_modes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2022 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Functions to do internal modes/distinguishable GBS
"""

from .pnr_statistics import pnr_prob
from .distinguishable_pnr_prob import distinguishable_pnr_prob
from .fock_density_matrices import density_matrix_single_mode
from .prepare_cov import *
Loading