Source code for pennylane_sf.remote
# Copyright 2018-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.
"""
The Strawberry Fields remote device implements all the :class:`~pennylane.device.Device` methods,
and provides access to Xanadu's continuous-variable quantum hardware.
"""
from collections import OrderedDict
import warnings
import numpy as np
import strawberryfields as sf
# import state preparations, gates and measurements
from strawberryfields.engine import xcc
from strawberryfields import ops
from strawberryfields.utils.post_processing import (
all_fock_probs_pnr,
samples_expectation,
samples_variance,
)
from pennylane.wires import Wires
from .simulator import StrawberryFieldsSimulator
[docs]class StrawberryFieldsRemote(StrawberryFieldsSimulator):
r"""StrawberryFields remote device for PennyLane.
A valid Strawberry Fields API token is needed for access. This token can be
passed when creating the device. The default configuration options of
Strawberry Fields are used to store the authentication token in a
configuration file.
Args:
backend (str): name of the remote backend to be used
wires (Iterable[Number, str]): Iterable that contains unique labels for the
modes as numbers or strings (i.e., ``['m1', ..., 'm4', 'n1',...,'n4']``).
The number of labels must match the number of modes accessible on the backend.
If not provided, modes are addressed as consecutive integers ``[0, 1, ...]``, and their number
is inferred from the backend.
cutoff_dim (int): Fock-space truncation dimension
shots (int): number of circuit evaluations/random samples used to
estimate expectation values of observables
hbar (float): the convention chosen in the canonical commutation
relation :math:`[x, p] = i \hbar`
sf_token (str): the SF API token used for remote access
"""
name = "Strawberry Fields Hardware PennyLane plugin"
short_name = "strawberryfields.remote"
_operation_map = {
"CatState": ops.Catstate,
"CoherentState": ops.Coherent,
"FockDensityMatrix": ops.DensityMatrix,
"DisplacedSqueezedState": ops.DisplacedSqueezed,
"FockState": ops.Fock,
"FockStateVector": ops.Ket,
"SqueezedState": ops.Squeezed,
"ThermalState": ops.Thermal,
"GaussianState": ops.Gaussian,
"Beamsplitter": ops.BSgate,
"CrossKerr": ops.CKgate,
"ControlledAddition": ops.CXgate,
"ControlledPhase": ops.CZgate,
"Displacement": ops.Dgate,
"Kerr": ops.Kgate,
"QuadraticPhase": ops.Pgate,
"Rotation": ops.Rgate,
"TwoModeSqueezing": ops.S2gate,
"Squeezing": ops.Sgate,
"CubicPhase": ops.Vgate,
"InterferometerUnitary": ops.Interferometer,
}
_observable_map = {
"Identity": None,
"NumberOperator": None,
"TensorN": None,
}
def __init__(self, *, backend, wires=None, cutoff_dim=5, shots=1000, hbar=2, sf_token=None):
if shots is None:
raise ValueError(
"The strawberryfields.remote device does not support analytic expectation values"
)
self.backend = backend
self.cutoff = cutoff_dim
eng = sf.RemoteEngine(self.backend)
self.num_wires = eng.device.modes
if wires is None:
# infer the number of modes from the device specs
# and use consecutive integer wire labels
wires = range(self.num_wires)
if isinstance(wires, int):
raise ValueError(
f"Device has a fixed number of {self.num_wires} modes. The wires argument can "
f"only be used to specify an iterable of wire labels."
)
if self.num_wires != len(wires):
raise ValueError(
f"Device has a fixed number of {self.num_wires} modes and "
f"cannot be created with {len(wires)} wires."
)
super().__init__(wires, shots=shots, hbar=hbar)
self.eng = eng
if sf_token is not None:
xcc.Settings(REFRESH_TOKEN=sf_token).save()
[docs] def reset(self):
"""Reset the device"""
sf.hbar = self.hbar
if self.q is not None:
self.q = None
if self.prog is not None:
self.prog = None
if self.samples is not None:
self.samples = None
[docs] def pre_measure(self):
ops.MeasureFock() | self.q # pylint: disable=pointless-statement, expression-not-assigned
# RemoteEngine.run includes compilation that checks the validity of the
# defined Program
results = self.eng.run(self.prog, shots=self.shots)
self.samples = results.samples
[docs] def sample(
self, observable, wires, par
): # pylint: disable=unused-argument, missing-function-docstring
if observable == "Identity":
return np.ones(self.shots)
device_wires = self.map_wires(wires)
selected_samples = self.samples[:, device_wires]
return np.prod(selected_samples, axis=1)
[docs] def expval(self, observable, wires, par):
if observable == "Identity":
return 1
return samples_expectation(self.samples, modes=self.map_wires(wires))
[docs] def var(self, observable, wires, par):
if observable == "Identity":
return 0
return samples_variance(self.samples, modes=self.map_wires(wires))
[docs] def probability(self, wires=None): # pylint: disable=missing-function-docstring
wires = wires or self.wires
wires = Wires(wires)
wires_to_trace_out = Wires.unique_wires([self.wires, wires])
device_wires_to_trace_out = self.map_wires(wires_to_trace_out)
device_wires = self.map_wires(self.wires)
fock_probs = all_fock_probs_pnr(self.samples)
cutoff = fock_probs.shape[0]
if self.cutoff < cutoff:
warnings.warn(
"Samples were generated where at least one mode had more photons than "
"the number allowed by the cutoff",
UserWarning,
)
sl = []
for wire in device_wires:
if wire in device_wires_to_trace_out:
sl.append(slice(None))
else:
sl.append(slice(self.cutoff))
all_probs = fock_probs[tuple(sl)]
else:
diff = self.cutoff - cutoff
all_probs = np.pad(fock_probs, [(0, diff)] * self.num_wires)
if len(device_wires_to_trace_out) > 0:
all_probs = np.sum(all_probs, axis=device_wires_to_trace_out.labels)
all_probs = all_probs.flat
N = len(wires)
ind = np.indices([self.cutoff] * N).reshape(N, -1).T
all_probs = OrderedDict((tuple(k), v) for k, v in zip(ind, all_probs))
return all_probs
_modules/pennylane_sf/remote
Download Python script
Download Notebook
View on GitHub