Source code for thewalrus.quantum.gaussian_checks
# Copyright 2019-2020 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.
"""
Tests for various properties of covariance matrices as well as fidelity
calculations for Gaussian states.
"""
# pylint: disable=too-many-arguments
import numpy as np
from scipy.linalg import sqrtm
from ..symplectic import sympmat
[docs]def is_valid_cov(cov, hbar=2, rtol=1e-05, atol=1e-08):
r"""Checks if the covariance matrix is a valid quantum covariance matrix.
Args:
cov (array): a covariance matrix
hbar (float): value of hbar in the uncertainty relation
rtol (float): the relative tolerance parameter used in `np.allclose`
atol (float): the absolute tolerance parameter used in `np.allclose`
Returns:
(bool): whether the given covariance matrix is a valid covariance matrix
"""
(n, m) = cov.shape
if n != m:
# raise ValueError("The input matrix must be square")
return False
if not np.allclose(cov, np.transpose(cov), rtol=rtol, atol=atol):
# raise ValueError("The input matrix is not symmetric")
return False
if n % 2 != 0:
# raise ValueError("The input matrix is of even dimension")
return False
nmodes = n // 2
vals = np.linalg.eigvalsh(cov + 0.5j * hbar * sympmat(nmodes))
vals[np.abs(vals) < atol] = 0.0
if np.all(vals >= 0):
# raise ValueError("The input matrix violates the uncertainty relation")
return True
return False
[docs]def is_pure_cov(cov, hbar=2, rtol=1e-05, atol=1e-08):
r"""Checks if the covariance matrix is a valid quantum covariance matrix
that corresponds to a quantum pure state
Args:
cov (array): a covariance matrix
hbar (float): value of hbar in the uncertainty relation
rtol (float): the relative tolerance parameter used in `np.allclose`
atol (float): the absolute tolerance parameter used in `np.allclose`
Returns:
(bool): whether the given covariance matrix corresponds to a pure state
"""
if is_valid_cov(cov, hbar=hbar, rtol=rtol, atol=atol):
purity = 1 / np.sqrt(np.linalg.det(2 * cov / hbar))
if np.allclose(purity, 1.0, rtol=rtol, atol=atol):
return True
return False
[docs]def is_classical_cov(cov, hbar=2, atol=1e-08):
r"""Checks if the covariance matrix can be efficiently sampled.
Args:
cov (array): a covariance matrix
hbar (float): value of hbar in the uncertainty relation
atol (float): the absolute tolerance parameter used in `np.allclose`
Returns:
(bool): whether the given covariance matrix corresponds to a classical state
"""
if is_valid_cov(cov, hbar=hbar, atol=atol):
(n, _) = cov.shape
vals = np.linalg.eigvalsh(cov - 0.5 * hbar * np.identity(n))
vals[np.abs(vals) < atol] = 0.0
if np.all(vals >= 0):
return True
return False
[docs]def fidelity(mu1, cov1, mu2, cov2, hbar=2, rtol=1e-05, atol=1e-08):
r"""Calculates the fidelity between two Gaussian quantum states.
For two pure states :math:`|\psi_1 \rangle, \ |\psi_2 \rangle`
the fidelity is given by :math:`|\langle \psi_1|\psi_2 \rangle|^2`
Note that if the covariance matrices correspond to pure states this
function reduces to the modulus square of the overlap of their state vectors.
For the derivation see `'Quantum Fidelity for Arbitrary Gaussian States', Banchi et al. <10.1103/PhysRevLett.115.260501>`_.
The actual implementation used here corresponds to the *square* of Eq. 96 of
`'Gaussian states and operations - a quick reference', Brask <https://arxiv.org/abs/2102.05748>`_.
Args:
mu1 (array): vector of means of the first state
cov1 (array): covariance matrix of the first state
mu2 (array): vector of means of the second state
cov2 (array): covariance matrix of the second state
hbar (float): value of hbar in the uncertainty relation
rtol (float): the relative tolerance parameter used in `np.allclose`
atol (float): the absolute tolerance parameter used in `np.allclose`
Returns:
(float): value of the fidelity between the two states
"""
n0, n1 = cov1.shape
m0, m1 = cov2.shape
(l0,) = mu1.shape
(l1,) = mu1.shape
if not n0 == n1 == m0 == m1 == l0 == l1:
raise ValueError("The inputs have incompatible shapes")
# We first convert all the inputs to quantities where hbar = 1
sigma1 = cov1 / hbar
sigma2 = cov2 / hbar
deltar = (mu1 - mu2) / np.sqrt(hbar)
omega = sympmat(n0 // 2) # The symplectic matrix
sigma = sigma1 + sigma2
sigma_inv = np.linalg.inv(sigma)
vaux = omega.T @ sigma_inv @ (0.25 * omega + sigma2 @ omega @ sigma1)
sqrtm_arg = np.identity(n0) + 0.25 * np.linalg.inv(vaux @ omega @ vaux @ omega)
# The sqrtm function has issues with matrices that are close to zero, hence we branch
if np.allclose(sqrtm_arg, 0, rtol=rtol, atol=atol):
mat_sqrtm = np.zeros_like(sqrtm_arg)
else:
mat_sqrtm = sqrtm(sqrtm_arg)
det_arg = 2 * (mat_sqrtm + np.identity(n0)) @ vaux
f = np.sqrt(np.linalg.det(sigma_inv) * np.linalg.det(det_arg)) * np.exp(
-0.5 * deltar @ sigma_inv @ deltar
)
# Note that we only take the square root and that we have a prefactor of 0.5
# as opposed to 0.25 in Brask. This is because this function returns the square
# of their fidelities.
return f
[docs]def is_symplectic(S, rtol=1e-05, atol=1e-08):
"""Checks if the matrix is symplectic.
Args:
S (array): a matrix
rtol (float): the relative tolerance parameter used in ``np.allclose``
atol (float): the absolute tolerance parameter used in ``np.allclose``
Returns:
bool: whether the given matrix is symplectic
"""
(n, m) = S.shape
if n != m:
return False
if n % 2 != 0:
return False
n = n // 2
A = S[0:n, 0:n]
B = S[0:n, n : 2 * n]
C = S[n : 2 * n, 0:n]
D = S[n : 2 * n, n : 2 * n]
# The equations below are equivalent to S.T @ Omega @ S = Omega where Omega is the symplectic form
if (
np.allclose(A.T @ C, C.T @ A, rtol=rtol, atol=atol)
and np.allclose(B.T @ D, D.T @ B, rtol=rtol, atol=atol)
and np.allclose(A.T @ D - C.T @ B, np.eye(n), rtol=rtol, atol=atol)
):
return True
return False
_modules/thewalrus/quantum/gaussian_checks
Download Python script
Download Notebook
View on GitHub