From e552d07278f30f9cee6e8a3c737bebb1de5b4554 Mon Sep 17 00:00:00 2001 From: Jeremy Swinarton Date: Thu, 29 Apr 2021 11:13:47 -0400 Subject: [PATCH] Change simulon target to simulon_gaussian (#576) **Context:** The Xanadu Quantum Cloud simulator, formerly called `simulon`, now has the target name `simulon_gaussian`. This change was made to support many different simulon types in the future. **Description of the Change:** - References to `simulon` in documentation and tests have been changed to `simulon_gaussian`. - A formatting change is made in `strawberryfields/backends/gaussianbackend/gaussiancircuit.py`. It appears that the black version on CI has been upgraded since the last commit, which now checks new elements in docstrings. This change has been made here instead of in a separate PR. **Benefits:** People reading the docs will not try to submit jobs to a simulator that doesn't exist :slightly_smiling_face: **Possible Drawbacks:** n/a **Related GitHub Issues:** n/a --- .github/CHANGELOG.md | 66 ++++++++++++------- doc/introduction/photonic_hardware.rst | 31 +++++---- strawberryfields/_version.py | 2 +- .../gaussianbackend/gaussiancircuit.py | 26 ++++---- tests/frontend/test_program.py | 6 +- 5 files changed, 76 insertions(+), 55 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index d12dbc4d7..50938f161 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,8 +1,24 @@ -# Release 0.18.0 (current release) +# Release 0.18.0-post1 (current release) + +

Documentation

+ +* References to the ``simulon`` simulator target have been rewritten to + ``simulon_gaussian`` to reflect changes made on the Xanadu Quantum Cloud. The + language has been modified to imply that multiple simulators could be + available on XQC. + [(#576)](https://github.com/XanaduAI/strawberryfields/pull/576) + +

Contributors

+ +This release contains contributions from (in alphabetical order): + +Jeremy Swinarton. + +# Release 0.18.0

New features since last release

-* Adds the Bosonic backend, which can simulate states represented as linear +* Adds the Bosonic backend, which can simulate states represented as linear combinations of Gaussian functions in phase space. [(#533)](https://github.com/XanaduAI/strawberryfields/pull/533) [(#538)](https://github.com/XanaduAI/strawberryfields/pull/538) @@ -10,14 +26,14 @@ [(#541)](https://github.com/XanaduAI/strawberryfields/pull/541) [(#546)](https://github.com/XanaduAI/strawberryfields/pull/546) [(#549)](https://github.com/XanaduAI/strawberryfields/pull/549) - - It can be regarded as a generalization of the Gaussian backend, since - transformations on states correspond to modifications of the means and - covariances of each Gaussian in the linear combination, along with changes to - the coefficients of the linear combination. Example states that can be - expressed using the new backend include all Gaussian, Gottesman-Kitaev-Preskill, + + It can be regarded as a generalization of the Gaussian backend, since + transformations on states correspond to modifications of the means and + covariances of each Gaussian in the linear combination, along with changes to + the coefficients of the linear combination. Example states that can be + expressed using the new backend include all Gaussian, Gottesman-Kitaev-Preskill, cat and Fock states. - + ```python prog = sf.Program(1) eng = sf.Engine('bosonic') @@ -41,27 +57,27 @@ GKP states are qubits, with the qubit state defined by: .. math:: \ket{\psi}\_{gkp} = \cos\frac{\theta}{2}\ket{0}\_{gkp} + e^{-i\phi}\sin\frac{\theta}{2}\ket{1}\_{gkp}, - + where the computational basis states are :math:`\ket{\mu}_{gkp} = \sum_{n} \ket{(2n+\mu)\sqrt{\pi\hbar}}_{q}`. - -* Adds the measurement-based squeezing gate `MSgate`; a new front-end operation + +* Adds the measurement-based squeezing gate `MSgate`; a new front-end operation for the Bosonic backend. [(#538)](https://github.com/XanaduAI/strawberryfields/pull/538) [(#539)](https://github.com/XanaduAI/strawberryfields/pull/539) [(#541)](https://github.com/XanaduAI/strawberryfields/pull/541) - - `MSgate` is an implementation of inline squeezing that can be performed by - interacting the target state with an ancillary squeezed vacuum state at a - beamsplitter, measuring the ancillary mode with homodyne, and then applying - a feed-forward displacement. The channel is implemented either on average - (as a Gaussian CPTP map) or in the single-shot implementation. If the + + `MSgate` is an implementation of inline squeezing that can be performed by + interacting the target state with an ancillary squeezed vacuum state at a + beamsplitter, measuring the ancillary mode with homodyne, and then applying + a feed-forward displacement. The channel is implemented either on average + (as a Gaussian CPTP map) or in the single-shot implementation. If the single-shot implementation is used, the measurement outcome of the ancillary mode is stored in the results object. - + ```python prog = sf.Program(1) eng = sf.Engine('bosonic') - + with prog.context as q: sf.ops.Catstate(alpha=2) | q r = 0.3 @@ -69,14 +85,14 @@ sf.ops.MSgate(r, phi=0, r_anc=1.2, eta_anc=1, avg=True) | q # Single-shot map sf.ops.MSgate(r, phi=0, r_anc=1.2, eta_anc=1, avg=False) | q - + results = eng.run(prog) ancilla_samples = results.ancilla_samples - + xvec = np.arange(-5, 5, 0.01) pvec = np.arange(-5, 5, 0.01) wigner = results.state.wigner(0, xvec, pvec) - + plt.contourf(xvec, pvec, wigner) plt.show() ``` @@ -138,7 +154,7 @@ instead of the incorrect version number `1.0.0`. [(#540)](https://github.com/XanaduAI/strawberryfields/pull/540) -* TDM programs now expect a flat (not nested) dictionary of `modes` in device +* TDM programs now expect a flat (not nested) dictionary of `modes` in device specifications obtained from the XQC platform API. [(#566)](https://github.com/XanaduAI/strawberryfields/pull/566) @@ -165,7 +181,7 @@ This release contains contributions from (in alphabetical order): J. Eli Bourassa, Guillaume Dauphinais, Ish Dhand, Theodor Isacsson, Josh Izaac, -Leonhard Neuhaus, Nicolás Quesada, Aaron Robertson, Krishna Kumar Sabapathy, +Leonhard Neuhaus, Nicolás Quesada, Aaron Robertson, Krishna Kumar Sabapathy, Jeremy Swinarton, Antal Száva, Ilan Tzitrin. # Release 0.17.0 diff --git a/doc/introduction/photonic_hardware.rst b/doc/introduction/photonic_hardware.rst index 744a0fe79..a16b42e32 100644 --- a/doc/introduction/photonic_hardware.rst +++ b/doc/introduction/photonic_hardware.rst @@ -133,26 +133,31 @@ You can also omit the ``--output`` parameter to print the result to the screen. Cloud simulator --------------- -In addition to submitting jobs to be run on quantum hardware, it is also possible to run jobs on -the Xanadu Quantum Cloud simulator Simulon. The process is very similar to running jobs on hardware. You -will need to configure your account, as described above, and submit a job via the ``RemoteEngine``, -using ``"simulon"`` as the target instead of a specific chip: - ->>> eng = sf.RemoteEngine("simulon") +In addition to submitting jobs to be run on quantum hardware, it is also +possible to run jobs on cloud simulators (which we refer to as "simulons") via +the Xanadu Quantum Cloud. The process is very similar to running jobs on +hardware. You will need to configure your account, as described above, and +submit a job via the ``RemoteEngine``, using a simulator as the target instead +of a specific chip: + +>>> eng = sf.RemoteEngine("simulon_gaussian") >>> result = eng.run(prog) -Simulon jobs can also be submitted asynchronously using ``eng.run_async``, or by submitting a -Blackbird script with the ``target`` set to ``simulon`` in the Blackbird header. +Simulator jobs can also be submitted asynchronously using ``eng.run_async``, or +by submitting a Blackbird script with the ``target`` set to a simulator target +in the Blackbird header. See the `Submitting jobs on hardware`_ section above for more details. .. note:: - Simulon runs on the ``gaussian`` backend (see :ref:`simulating_your_program`) and thus only supports - Gaussian operations, including homodyne and heterodyne measurements, as well terminal - Fock measurements. Note that there are limits to how many measurements a circuit can have depending - on the type of measurement. These can be retrieved by calling ``engine.device_spec.modes`` with - ``engine = sf.RemoteEngine("simulon")``. + The ``simulon_gaussian`` simulator runs on the ``gaussian`` backend (see + :ref:`simulating_your_program`) and thus only supports Gaussian operations, + including homodyne and heterodyne measurements, as well terminal Fock + measurements. Note that there are limits to how many measurements a circuit + can have depending on the type of measurement. These can be retrieved by + calling ``engine.device_spec.modes`` with ``engine = + sf.RemoteEngine("simulon_gaussian")``. Tutorials --------- diff --git a/strawberryfields/_version.py b/strawberryfields/_version.py index b53bcaaab..5493e7c92 100644 --- a/strawberryfields/_version.py +++ b/strawberryfields/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.18.0" +__version__ = "0.18.0-post1" diff --git a/strawberryfields/backends/gaussianbackend/gaussiancircuit.py b/strawberryfields/backends/gaussianbackend/gaussiancircuit.py index 894da35ed..11e5f642a 100644 --- a/strawberryfields/backends/gaussianbackend/gaussiancircuit.py +++ b/strawberryfields/backends/gaussianbackend/gaussiancircuit.py @@ -77,7 +77,7 @@ def add_mode(self, n=1): self.nlen = newnlen def del_mode(self, modes): - """ delete mode from the circuit""" + """delete mode from the circuit""" if isinstance(modes, int): modes = [modes] @@ -110,7 +110,7 @@ def get_modes(self): return [x for x in self.active if x is not None] def displace(self, r, phi, i): - """ Implements a displacement operation by the complex number `beta = r * np.exp(1j * phi)` in mode i""" + """Implements a displacement operation by the complex number `beta = r * np.exp(1j * phi)` in mode i""" # Update displacement of mode i by the complex amount bet if self.active[i] is None: raise ValueError("Cannot displace mode, mode does not exist") @@ -118,7 +118,7 @@ def displace(self, r, phi, i): self.mean[i] += r * np.exp(1j * phi) def squeeze(self, r, phi, k): - """ Implements a squeezing operation in mode k by the amount z = r*exp(1j*phi).""" + """Implements a squeezing operation in mode k by the amount z = r*exp(1j*phi).""" if self.active[k] is None: raise ValueError("Cannot squeeze mode, mode does not exist") @@ -158,7 +158,7 @@ def squeeze(self, r, phi, k): self.mmat[:, k] = self.mmat[k] def phase_shift(self, phi, k): - """ Implements a phase shift in mode k by the amount phi.""" + """Implements a phase shift in mode k by the amount phi.""" if self.active[k] is None: raise ValueError("Cannot phase shift mode, mode does not exist") @@ -181,7 +181,7 @@ def phase_shift(self, phi, k): self.mmat[:, k] = self.mmat[k] def beamsplitter(self, theta, phi, k, l): - """ Implements a beam splitter operation between modes k and l by the amount theta, phi""" + """Implements a beam splitter operation between modes k and l by the amount theta, phi""" if self.active[k] is None or self.active[l] is None: raise ValueError("Cannot perform beamsplitter, mode(s) do not exist") @@ -358,7 +358,7 @@ def fromscovmat(self, V, modes=None): self.mmat[rows, cols] = 0.25 * (A - C + 1j * (B + Bt)) def qmat(self, modes=None): - """ Construct the covariance matrix for the Q function""" + """Construct the covariance matrix for the Q function""" if modes is None: modes = list(range(self.nlen)) @@ -382,7 +382,7 @@ def qmat(self, modes=None): return sigmaq def fidelity_coherent(self, alpha, modes=None): - """ Returns a function that evaluates the Q function of the given state """ + """Returns a function that evaluates the Q function of the given state""" if modes is None: modes = list(range(self.nlen)) @@ -404,7 +404,7 @@ def fidelity_vacuum(self, modes=None): return self.fidelity_coherent(alpha) def Amat(self): - """ Constructs the A matrix from Hamilton's paper""" + """Constructs the A matrix from Hamilton's paper""" ######### this needs to be conjugated sigmaq = ( np.concatenate( @@ -446,12 +446,12 @@ def thermal_loss(self, T, nbar, k): self.nmat += (1 - T) * nbar def init_thermal(self, population, mode): - """ Initializes a state of mode in a thermal state with the given population""" + """Initializes a state of mode in a thermal state with the given population""" self.loss(0.0, mode) self.nmat[mode][mode] = population def is_vacuum(self, tol=0.0): - """ Checks if the state is vacuum by calculating its fidelity with vacuum """ + """Checks if the state is vacuum by calculating its fidelity with vacuum""" fid = self.fidelity_vacuum() return np.abs(fid - 1) <= tol @@ -495,7 +495,7 @@ def homodyne(self, n, shots=1, eps=0.0002): return res def post_select_homodyne(self, n, val, eps=0.0002): - """ Performs a homodyne measurement but postelecting on the value vals for mode n """ + """Performs a homodyne measurement but postelecting on the value vals for mode n""" if self.active[n] is None: raise ValueError("Cannot apply homodyne measurement, mode does not exist") covmat = np.diag(np.array([eps ** 2, 1.0 / eps ** 2])) @@ -517,7 +517,7 @@ def post_select_homodyne(self, n, val, eps=0.0002): return val def post_select_heterodyne(self, n, alpha_val): - """ Performs a homodyne measurement but postelecting on the value vals for mode n """ + """Performs a homodyne measurement but postelecting on the value vals for mode n""" if self.active[n] is None: raise ValueError("Cannot apply heterodyne measurement, mode does not exist") @@ -539,7 +539,7 @@ def post_select_heterodyne(self, n, alpha_val): return alpha_val def apply_u(self, U): - """ Transforms the state according to the linear optical unitary that maps a[i] \to U[i, j]^*a[j]""" + """Transforms the state according to the linear optical unitary that maps a[i] \to U[i, j]^*a[j]""" self.mean = np.dot(np.conj(U), self.mean) self.nmat = np.dot(np.dot(U, self.nmat), np.conj(np.transpose(U))) self.mmat = np.dot(np.dot(np.conj(U), self.mmat), np.conj(np.transpose(U))) diff --git a/tests/frontend/test_program.py b/tests/frontend/test_program.py index f1125633a..f82e7b784 100644 --- a/tests/frontend/test_program.py +++ b/tests/frontend/test_program.py @@ -240,7 +240,7 @@ def test_assert_max_number_of_measurements(self, measure_op, measure_name): }, "layout": None, "gate_parameters": {}, "compiler": [None] } - spec = sf.api.DeviceSpec(target="simulon", connection=None, spec=device_dict) + spec = sf.api.DeviceSpec(target="simulon_gaussian", connection=None, spec=device_dict) prog = sf.Program(3) with prog.context as q: @@ -256,7 +256,7 @@ def test_assert_max_number_of_measurements_wrong_entry(self): """Check that the correct error is raised when calling `prog.assert_number_of_measurements` with the incorrect type of device spec mode entry.""" device_dict = {"modes": 2, "layout": None, "gate_parameters": None, "compiler": [None]} - spec = sf.api.DeviceSpec(target="simulon", connection=None, spec=device_dict) + spec = sf.api.DeviceSpec(target="simulon_gaussian", connection=None, spec=device_dict) prog = sf.Program(3) with prog.context as q: @@ -500,7 +500,7 @@ class DummyCompiler(Compiler): }, "layout": None, "gate_parameters": {}, "compiler": [None] } - spec = sf.api.DeviceSpec(target="simulon", connection=None, spec=device_dict) + spec = sf.api.DeviceSpec(target="simulon_gaussian", connection=None, spec=device_dict) prog = sf.Program(3) with prog.context as q: