Skip to content

Commit

Permalink
Making Catstate take real arguments (#441)
Browse files Browse the repository at this point in the history
Co-authored-by: sduquemesa <675763+sduquemesa@users.noreply.github.com>
Co-authored-by: Theodor <theodor@xanadu.ai>
  • Loading branch information
3 people committed Oct 28, 2021
1 parent 0e06dc6 commit 688ac9f
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 197 deletions.
2 changes: 1 addition & 1 deletion strawberryfields/api/devicespec.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def gate_parameters(self):
>>> spec.gate_parameters
{'squeezing_amplitude_0': x=0, x=1, 'phase_0': x=0, 0≤x≤6.283185307179586}
"""
gate_parameters = dict()
gate_parameters = {}

for gate_name, param_ranges in self._spec["gate_parameters"].items():
# convert gate parameter allowed ranges to Range objects
Expand Down
11 changes: 11 additions & 0 deletions strawberryfields/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,17 @@ def cross_kerr_interaction(self, kappa, mode1, mode2):
"""
raise NotImplementedError

def two_mode_squeeze(self, r, phi, mode1, mode2):
r"""Apply a two-mode squeezing operator to the two specified modes.
Args:
r (float): squeezing magnitude
phi (float): squeezing angle
mode1 (int): first mode that two-mode squeezing gate acts on
mode2 (int): second mode that two-mode squeezing gate acts on
"""
raise NotImplementedError

def state(self, modes=None, **kwargs):
r"""Returns the state of the quantum simulation.
Expand Down
48 changes: 29 additions & 19 deletions strawberryfields/backends/bosonicbackend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def run_prog(self, prog, **kwargs):

# Internally also store all the measurement outcomes
if r.ind not in all_samples:
all_samples[r.ind] = list()
all_samples[r.ind] = []
all_samples[r.ind].append(val[:, i])

applied.append(cmd)
Expand Down Expand Up @@ -385,14 +385,18 @@ def prepare_gaussian_state(self, r, V, modes):
self.circuit.from_covmat(cov, modes)
self.circuit.from_mean(means, modes)

def prepare_cat(self, alpha, phi, representation, ampl_cutoff, D):
def prepare_cat(self, a, theta, p, representation, ampl_cutoff, D):
r"""Prepares the arrays of weights, means and covs for a cat state:
:math:`\ket{\text{cat}(\alpha)} = \frac{1}{N} (\ket{\alpha} +e^{i\phi\pi} \ket{-\alpha})`.
:math:`\ket{\text{cat}(\alpha)} = \frac{1}{N} (\ket{\alpha} +e^{i\phi} \ket{-\alpha})`,
where :math:`\alpha = ae^{i\theta}`.
Args:
alpha (complex): alpha value of cat state
phi (float): phi value of cat state
a (float): displacement magnitude :math:`|\alpha|`
theta (float): displacement angle :math:`\theta`
p (float): Parity, where :math:`\phi=p\pi`. ``p=0`` corresponds to an even
cat state, and ``p=1`` an odd cat state.
representation (str): whether to use the ``'real'`` or ``'complex'`` representation
ampl_cutoff (float): if using the ``'real'`` representation, this determines
how many terms to keep
Expand All @@ -402,24 +406,27 @@ def prepare_cat(self, alpha, phi, representation, ampl_cutoff, D):
tuple: arrays of the weights, means and covariances for the state
"""

phi = np.pi * p

if representation not in ("complex", "real"):
raise ValueError(
'The representation argument accepts only "real" or "complex" as arguments.'
)

# Case alpha = 0, prepare vacuum
if np.isclose(np.absolute(alpha), 0):
if np.isclose(a, 0):
weights = np.array([1], dtype=complex)
means = np.array([[0, 0]], dtype=complex)
covs = np.array([0.5 * self.circuit.hbar * np.identity(2)])
return weights, means, covs

# Normalization factor
norm = 1 / (2 * (1 + np.exp(-2 * np.absolute(alpha) ** 2) * np.cos(phi)))
norm = 1 / (2 * (1 + np.exp(-2 * a ** 2) * np.cos(phi)))
hbar = self.circuit.hbar

if representation == "complex":
phi = np.pi * phi

alpha = a * np.exp(1j * theta)

# Mean of |alpha><alpha| term
rplus = np.sqrt(2 * hbar) * np.array([alpha.real, alpha.imag])
Expand All @@ -442,32 +449,35 @@ def prepare_cat(self, alpha, phi, representation, ampl_cutoff, D):
return weights, means, covs

# The only remaining option is a real-valued cat state
return self.prepare_cat_real_rep(alpha, phi, ampl_cutoff, D)
return self.prepare_cat_real_rep(a, theta, p, ampl_cutoff, D)

def prepare_cat_real_rep(self, alpha, phi, ampl_cutoff, D):
def prepare_cat_real_rep(self, a, theta, p, ampl_cutoff, D):
r"""Prepares the arrays of weights, means and covs for a cat state:
:math:`\ket{\text{cat}(\alpha)} = \frac{1}{N} (\ket{\alpha} +e^{i\phi\pi} \ket{-\alpha})`.
:math:`\ket{\text{cat}(\alpha)} = \frac{1}{N} (\ket{\alpha} +e^{i\phi\pi} \ket{-\alpha})`,
where :math:`\alpha = ae^{i\theta}`.
For this representation, weights, means and covariances are real-valued.
Args:
alpha (complex): alpha value of cat state
phi (float): phi value of cat state
a (float): displacement magnitude :math:`|\alpha|`
theta (float): displacement angle :math:`\theta`
p (float): Parity, where :math:`\phi=p\pi`. ``p=0`` corresponds to an even
cat state, and ``p=1`` an odd cat state.
ampl_cutoff (float): this determines how many terms to keep
D (float): quality parameter of approximation
Returns:
tuple: arrays of the weights, means and covariances for the state
"""

# Normalization factor
norm = 1 / (2 * (1 + np.exp(-2 * np.absolute(alpha) ** 2) * np.cos(phi)))
phi = np.pi * phi
phi = np.pi * p
norm = 1 / (2 * (1 + np.exp(-2 * a ** 2) * np.cos(phi)))
hbar = self.circuit.hbar

# Defining useful constants
a = np.absolute(alpha)
phase = np.angle(alpha)
E = np.pi ** 2 * D * hbar / (16 * a ** 2)
v = hbar / 2
num_mean = 8 * a * np.sqrt(hbar) / (np.pi * D * np.sqrt(2))
Expand Down Expand Up @@ -528,8 +538,8 @@ def prepare_cat_real_rep(self, alpha, phi, ampl_cutoff, D):
cov = cov[filt]

# applying a rotation if necessary
if not np.isclose(phase, 0):
S = np.array([[np.cos(phase), -np.sin(phase)], [np.sin(phase), np.cos(phase)]])
if not np.isclose(theta, 0):
S = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
means = np.dot(S, means.T).T
cov = S @ cov @ S.T

Expand Down
2 changes: 1 addition & 1 deletion strawberryfields/backends/fockbackend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def begin_circuit(self, num_subsystems, **kwargs):
self.circuit = Circuit(num_subsystems, cutoff_dim, pure)
self._modemap = ModeMap(num_subsystems)

def add_mode(self, n=1):
def add_mode(self, n=1, **kwargs):
self.circuit.alloc(n)
self._modemap.add(n)

Expand Down
5 changes: 4 additions & 1 deletion strawberryfields/backends/gaussianbackend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def begin_circuit(self, num_subsystems, **kwargs):
self._init_modes = num_subsystems
self.circuit = GaussianModes(num_subsystems)

def add_mode(self, n=1):
def add_mode(self, n=1, **kwargs):
self.circuit.add_mode(n)

def del_mode(self, modes):
Expand Down Expand Up @@ -287,3 +287,6 @@ def state(self, modes=None, **kwargs):

mode_names = ["q[{}]".format(i) for i in array(self.get_modes())[modes]]
return BaseGaussianState((means, covmat), len(modes), mode_names=mode_names)

def mzgate(self, phi_in, phi_ex, mode1, mode2):
raise NotImplementedError
2 changes: 1 addition & 1 deletion strawberryfields/backends/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def fock_prob(self, n, **kwargs):
if self._pure:
return np.abs(self.ket()[tuple(n)]) ** 2

return self.dm()[tuple([n[i // 2] for i in range(len(n) * 2)])].real
return self.dm()[tuple(n[i // 2] for i in range(len(n) * 2))].real

def mean_photon(self, mode, **kwargs):
# pylint: disable=unused-argument
Expand Down
4 changes: 1 addition & 3 deletions strawberryfields/backends/tfbackend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"""
import sys
from .backend import TFBackend

try:
import tensorflow
Expand Down Expand Up @@ -168,6 +169,3 @@ def excepthook(type, value, traceback):
sys.excepthook = excepthook

raise ImportError(tf_info)


from .backend import TFBackend
8 changes: 7 additions & 1 deletion strawberryfields/backends/tfbackend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,13 @@ def del_mode(self, modes):
self.circuit.del_mode(remapped_modes)
self._modemap.delete(modes)

def add_mode(self, n=1):
def add_mode(self, n=1, **kwargs):
with tf.name_scope("Add_mode"):
self.circuit.add_mode(n)
self._modemap.add(n)

def thermal_loss(self, T, nbar, mode):
raise NotImplementedError

def measure_threshold(self, modes, shots=1, select=None, **kwargs):
raise NotImplementedError
12 changes: 4 additions & 8 deletions strawberryfields/backends/tfbackend/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ def measure_fock(self, modes, select=None, **kwargs):
Returns:
tuple[int]: The Fock number measurement results for each mode.
"""
# pylint: disable=unused-argument

# allow integer (non-list) arguments
# not part of the API, but provided for convenience
if isinstance(modes, int):
Expand Down Expand Up @@ -863,14 +865,8 @@ def measure_homodyne(self, phi, mode, select=None, **kwargs):
# \psi_n(x) = 1/sqrt[2^n n!](\frac{m \omega}{\pi \hbar})^{1/4}
# \exp{-\frac{m \omega}{2\hbar} x^2} H_n(\sqrt{\frac{m \omega}{\pi}} x)
# where H_n(x) is the (physicists) nth Hermite polynomial
if "max" in kwargs:
q_mag = kwargs["max"]
else:
q_mag = 10
if "num_bins" in kwargs:
num_bins = kwargs["num_bins"]
else:
num_bins = 100000
q_mag = kwargs.get("max", 10)
num_bins = kwargs.get("kwargs", 100000)
if "q_tensor" in self._cache:
# use cached q_tensor
q_tensor = self._cache["q_tensor"]
Expand Down
22 changes: 11 additions & 11 deletions strawberryfields/decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,12 @@ def triangular_compact(U, rtol=1e-12, atol=1e-12):
V = U.conj()
m = U.shape[0]

phases = dict()
phases = {}
phases["m"] = m
phases["phi_ins"] = dict() # mode : phi
phases["deltas"] = dict() # (mode, layer) : delta
phases["sigmas"] = dict() # (mode, layer) : sigma
phases["zetas"] = dict() # mode : zeta
phases["phi_ins"] = {} # mode : phi
phases["deltas"] = {} # (mode, layer) : delta
phases["sigmas"] = {} # (mode, layer) : sigma
phases["zetas"] = {} # mode : zeta

for j in range(m - 1):
x = m - 1
Expand Down Expand Up @@ -760,13 +760,13 @@ def _rectangular_compact_init(
V = U.conj()
m = U.shape[0]

phases = dict()
phases = {}
phases["m"] = m
phases["phi_ins"] = dict() # mode : phi
phases["deltas"] = dict() # (mode, layer) : delta
phases["sigmas"] = dict() # (mode, layer) : sigma
phases["zetas"] = dict() # mode : zeta
phases["phi_outs"] = dict() # mode : phi
phases["phi_ins"] = {} # mode : phi
phases["deltas"] = {} # (mode, layer) : delta
phases["sigmas"] = {} # (mode, layer) : sigma
phases["zetas"] = {} # mode : zeta
phases["phi_outs"] = {} # mode : phi

for j in range(m - 1):
if j % 2 == 0:
Expand Down
4 changes: 2 additions & 2 deletions strawberryfields/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,14 @@ def _run_program(self, prog, **kwargs):

# Internally also store all the measurement outcomes
if r.ind not in all_samples:
all_samples[r.ind] = list()
all_samples[r.ind] = []
all_samples[r.ind].append(val[:, :, i])
else:
samples_dict[r.ind] = val[:, i]

# Internally also store all the measurement outcomes
if r.ind not in all_samples:
all_samples[r.ind] = list()
all_samples[r.ind] = []
all_samples[r.ind].append(val[:, i])

applied.append(cmd)
Expand Down

0 comments on commit 688ac9f

Please sign in to comment.