Source code for strawberryfields.backends.gaussianbackend.backend
# Copyright 2019 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.
# pylint: disable=too-many-public-methods
"""Gaussian backend"""
import warnings
from numpy import empty, concatenate, array, identity, sqrt, vstack, zeros_like, allclose, ix_
from thewalrus.samples import hafnian_sample_state, torontonian_sample_state
from thewalrus.symplectic import xxpp_to_xpxp
from strawberryfields.backends import BaseGaussian
from strawberryfields.backends.states import BaseGaussianState
from .gaussiancircuit import GaussianModes
[docs]class GaussianBackend(BaseGaussian):
r"""The GaussianBackend implements a simulation of quantum optical circuits
in NumPy using the Gaussian formalism, returning a :class:`~.GaussianState`
state object.
The primary component of the GaussianBackend is a
:attr:`~.GaussianModes` object which is used to simulate a multi-mode quantum optical system.
:class:`~.GaussianBackend` provides the basic API-compatible interface to the simulator, while the
:attr:`~.GaussianModes` object actually carries out the mathematical simulation.
The :attr:`GaussianModes` simulators maintain an internal covariance matrix & vector of means
representation of a multi-mode quantum optical system.
Note that unlike commonly used covariance matrix representations we encode our state in two complex
matrices :math:`N` and :math:`M` that are defined as follows
:math:`N_{i,j} = \langle a^\dagger _i a_j \rangle`
:math:`M_{i,j} = \langle a _i a_j \rangle`
and a vector of means :math:`\alpha_i =\langle a_i \rangle`.
..
.. currentmodule:: strawberryfields.backends.gaussianbackend
.. autosummary::
:toctree: api
~gaussiancircuit.GaussianModes
~ops
"""
short_name = "gaussian"
circuit_spec = "gaussian"
def __init__(self):
"""Initialize the backend."""
super().__init__()
self._supported["mixed_states"] = True
self._init_modes = None
self.circuit = None
[docs] def begin_circuit(self, num_subsystems, **kwargs):
self._init_modes = num_subsystems
self.circuit = GaussianModes(num_subsystems)
[docs] def prepare_coherent_state(self, r, phi, mode):
self.circuit.loss(0.0, mode)
self.circuit.displace(r, phi, mode)
[docs] def prepare_squeezed_state(self, r, phi, mode):
self.circuit.loss(0.0, mode)
self.circuit.squeeze(r, phi, mode)
[docs] def prepare_displaced_squeezed_state(self, r_d, phi_d, r_s, phi_s, mode):
self.circuit.loss(0.0, mode)
self.circuit.squeeze(r_s, phi_s, mode)
self.circuit.displace(r_d, phi_d, mode)
[docs] def beamsplitter(self, theta, phi, mode1, mode2):
self.circuit.beamsplitter(-theta, -phi, mode1, mode2)
[docs] def measure_homodyne(self, phi, mode, shots=1, select=None, **kwargs):
r"""Measure a :ref:`phase space quadrature <homodyne>` of the given mode.
See :meth:`.BaseBackend.measure_homodyne`.
Keyword Args:
eps (float): Homodyne amounts to projection onto a quadrature eigenstate.
This eigenstate is approximated by a squeezed state whose variance has been
squeezed to the amount ``eps``, :math:`V_\text{meas} = \texttt{eps}^2`.
Perfect homodyning is obtained when ``eps`` :math:`\to 0`.
Returns:
float: measured value
"""
if shots != 1:
if select is not None:
raise NotImplementedError(
"Gaussian backend currently does not support "
"postselection if shots != 1 for homodyne measurement"
)
raise NotImplementedError(
"Gaussian backend currently does not support " "shots != 1 for homodyne measurement"
)
# phi is the rotation of the measurement operator, hence the minus
self.circuit.phase_shift(-phi, mode)
if select is None:
eps = kwargs.get("eps", 0.0002)
qs = self.circuit.homodyne(mode, shots, eps)[0, 0]
else:
val = select * 2 / sqrt(2 * self.circuit.hbar)
qs = self.circuit.post_select_homodyne(mode, val, **kwargs)
# `qs` will always be a single value since multiple shots is not supported
return array([[qs * sqrt(2 * self.circuit.hbar) / 2]])
[docs] def measure_heterodyne(self, mode, shots=1, select=None, **kwargs):
if shots != 1:
if select is not None:
raise NotImplementedError(
"Gaussian backend currently does not support "
"postselection if shots != 1 for heterodyne measurement"
)
raise NotImplementedError(
"Gaussian backend currently does not support "
"shots != 1 for heterodyne measurement"
)
if select is None:
m = identity(2)
res = 0.5 * self.circuit.measure_dyne(m, [mode], shots=shots)
return array([[res[0, 0] + 1j * res[0, 1]]])
res = select
self.circuit.post_select_heterodyne(mode, select)
# `res` will always be a single value since multiple shots is not supported
return array([[res]])
[docs] def prepare_gaussian_state(self, r, V, modes):
if isinstance(modes, int):
modes = [modes]
# make sure number of modes matches shape of r and V
N = len(modes)
if len(r) != 2 * N:
raise ValueError("Length of means vector must be twice the number of modes.")
if V.shape != (2 * N, 2 * N):
raise ValueError(
"Shape of covariance matrix must be [2N, 2N], where N is the number of modes."
)
# convert xp-ordering to symmetric ordering
means = vstack([r[:N], r[N:]]).reshape(-1, order="F")
cov = xxpp_to_xpxp(V)
self.circuit.fromscovmat(cov, modes)
self.circuit.fromsmean(means, modes)
[docs] def passive(self, T, modes):
T_expand = identity(self.circuit.nlen, dtype=T.dtype)
T_expand[ix_(modes, modes)] = T
self.circuit.apply_u(T_expand)
[docs] def measure_fock(self, modes, shots=1, select=None, **kwargs):
if select is not None:
raise NotImplementedError(
"Gaussian backend currently does not support " "postselection"
)
if shots != 1:
warnings.warn(
"Cannot simulate non-Gaussian states. "
"Conditional state after Fock measurement has not been updated."
)
mu = self.circuit.mean
mean = self.circuit.smeanxp()
cov = self.circuit.scovmatxp()
x_idxs = array(modes)
p_idxs = x_idxs + len(mu)
modes_idxs = concatenate([x_idxs, p_idxs])
reduced_cov = cov[ix_(modes_idxs, modes_idxs)]
reduced_mean = mean[modes_idxs]
# check we are sampling from a gaussian state with zero mean
if allclose(mu, zeros_like(mu)):
samples = hafnian_sample_state(reduced_cov, shots)
else:
samples = hafnian_sample_state(reduced_cov, shots, mean=reduced_mean)
return samples
[docs] def measure_threshold(self, modes, shots=1, select=None, **kwargs):
if shots != 1:
if select is not None:
raise NotImplementedError(
"Gaussian backend currently does not support " "postselection"
)
warnings.warn(
"Cannot simulate non-Gaussian states. "
"Conditional state after Threshold measurement has not been updated."
)
mu = self.circuit.mean
cov = self.circuit.scovmatxp()
mean = self.circuit.smeanxp()
# check we are sampling from a gaussian state with zero mean
x_idxs = array(modes)
p_idxs = x_idxs + len(mu)
modes_idxs = concatenate([x_idxs, p_idxs])
reduced_cov = cov[ix_(modes_idxs, modes_idxs)]
reduced_mean = mean[modes_idxs]
samples = torontonian_sample_state(mu=reduced_mean, cov=reduced_cov, samples=shots)
return samples
[docs] def state(self, modes=None, **kwargs):
"""Returns the state of the quantum simulation.
See :meth:`.BaseBackend.state`.
Returns:
GaussianState: state description
"""
m = self.circuit.scovmat()
r = self.circuit.smean()
if modes is None:
modes = list(range(len(self.get_modes())))
listmodes = list(concatenate((2 * array(modes), 2 * array(modes) + 1)))
covmat = empty((2 * len(modes), 2 * len(modes)))
means = r[listmodes]
for i, ii in enumerate(listmodes):
for j, jj in enumerate(listmodes):
covmat[i, j] = m[ii, jj]
means *= sqrt(2 * self.circuit.hbar) / 2
covmat *= self.circuit.hbar / 2
mode_names = ["q[{}]".format(i) for i in array(self.get_modes())[modes]]
return BaseGaussianState((means, covmat), len(modes), mode_names=mode_names)
_modules/strawberryfields/backends/gaussianbackend/backend
Download Python script
Download Notebook
View on GitHub