Releases: XanaduAI/strawberryfields
Release 0.23.0-post1
Post release to address changes to the documentation.
Release 0.23.0
New features
-
Program Xanadu’s new Borealis hardware device via Strawberry Fields and Xanadu Cloud. (#714)
Borealis is a cloud-accessible photonic quantum computer, offering full programmability over all of its gates and capable of quantum advantage. Its hardware is based on time-domain multiplexing (TDM); a single squeezed-light source emits batches of 216 time-ordered squeezed-light pulses that interfere with one another with the help of optical delay loops, programmable beamsplitters, and phase shifters.
-
GBS data visualization functions are added. (#714)
-
A set of TDM compilers are added, including a Borealis compiler which compiles and validates programs against the hardware specification and calibration certificate. (#714)
-
A
remove_loss
utility function is added to theprogram_utils
module, allowing for the removal ofLossChannels
from Strawberry Fields programs. (#714) -
Cropping vacuum modes from TDM program results is now possible by passing
crop=True
as a run option. (#714)n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*gate_args) as (p, q): ops.Sgate(p[0]) | q[n[0]] for i in range(len(delays)): ops.Rgate(p[2 * i + 1]) | q[n[i]] ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]]) ops.MeasureX | q[0] eng = sf.Engine("gaussian") results = eng.run(prog, crop=True)
-
Resulting samples from TDM jobs return only the non-empty mode measurements when setting the
crop
option toTrue
in the programrun_options
or as a keyword argument in the enginerun
method. (#714) -
Realistic loss can be added to a Borealis circuit for local simulation execution. (#714)
compile_options = { "device": device, # hardware device object needed "realistic_loss": True, } eng = sf.Engine("gaussian") results = eng.run(prog, compile_options=compile_options)
-
Utility functions are added to allow for easier Borealis program and parameter creation. (#714)
-
Functions are added for analyzing GBS results for comparisons with classical simulations. (#714)
Improvements
-
A locked program can now be (un)rolled, and automatically restores the lock if previously in place. (#703)
-
Rolling and unrolling now only happens in place, and no longer returns the (un)rolled circuit. (#702)
-
Program.assert_number_of_modes
andProgram.assert_max_number_of_measurements
are combined into a singleassert_modes
method. (#709) -
Job results can now be retrieved without converting integers to
np.int64
objects by settinginteger_overflow_protection=False
(defaultTrue
) when running a program viaRemoteEngine.run()
. (#712) -
The TDM module is refactored to contain
program.py
, with theTDMProgram
class, andutils.py
, with various utility functions. (#714) -
The
Compiler
base class is updated to allow for setting a rigid circuit layout to validate a program during compilation. (#714) -
The
Compiler
base class now contains methods that can be overwritten to provide subclass compilers with loss-additions (e.g., to add realistic loss to a circuit) and program parameter updates. (#714)
Bug fixes
-
Trying to unroll an already unrolled program with a different number of shots works as expected. (#702)
-
Fixed bug with vacuum modes missing. (#702)
-
Validating parameters now works with nested parameter arrays. (#711)
-
Store correct rolled circuit before unrolling (fixes issue when rolled circuit has changed due to e.g., compilation). (#710)
Documentation
-
The centralized Xanadu Sphinx Theme is now used to style the Sphinx documentation. (#701)
-
The documentation on Gaussian circuit operations is fixed so that it's properly rendered. (#714)
Contributors
This release contains contributions from (in alphabetical order):
Mikhail Andrenkov, Sebastian Duque, Luke Helt, Theodor Isacsson, Josh Izaac, Fabian Laudenbach
Release 0.22.0
New features since last release
-
Device.layout
andDevice.gate_parameters
may now returnNone
. This can happen when a remote simulator device is used. (#661) -
A new interferometer decomposition method is implemented following the proposal of the paper Simple factorization of unitary transformations. (#665)
import numpy as np import strawberryfields as sf from strawberryfields import ops U = np.array([[-0.39302099+0.28732291j, 0.83734522+0.24866248j], [ 0.00769051+0.87345344j, -0.3847068 +0.29836325j]]) prog = sf.Program(2) with prog.context as q: ops.Interferometer(U, mesh="sun_compact") | q
-
A
Device.certificate
method is added which returns the hardware device certificate. (#679)>>> import strawberryfields as sf >>> eng = sf.RemoteEngine("X8") >>> print(eng.device.certificate) {'target': 'X8_01' ... }
-
Setting
shots=None
in the engine or program run options will not execute any measurements applied on the circuit. (#682)import strawberryfields as sf from strawberryfields import ops prog = sf.Program(1) eng = sf.Engine("gaussian") with prog.context as q: ops.Sgate(0.5) | q[0] ops.MeasureFock() | q results = eng.run(prog, shots=None) # samples will output an empty list [] print(results.samples) # the resulting Gaussian state is still accessible # via its vector of means and covariance matrix print(results.state.means()) print(results.state.cov())
-
There's a
program_equivalence
function instrawberryfields/program_utils.py
which checks Strawberry Fields programs for equivalence. (#686) -
An equality operator is implemented for
strawberryfields.Program
, checking that the exact same gates and respective parameters, are applied in order. (#686)import strawberryfields as sf from strawberryfields import ops prog_1 = sf.Program(1) prog_2 = sf.Program(1) with prog.context as q: ops.Sgate(0.42) | q[0] ops.MeasureFock() | q with prog.context as q: ops.Sgate(0.42) | q[0] ops.MeasureFock() | q assert prog_1 == prog_2
-
A
Program.equivalence
convenience method is added which calls theprogram_equivalence
utility function. (#686)prog_1 = sf.Program(1) prog_2 = sf.Program(1) with prog.context as q: ops.Sgate(1.1) | q[0] ops.MeasureFock() | q with prog.context as q: ops.Sgate(0.42) | q[0] ops.MeasureFock() | q assert prog_1.equivalence(prog_2, compare_params=False)
-
A
Device.validate_target
static method is added which checks that the target in the layout is the same as the target field in the specification. This check is also performed atDevice
initialization. (#687) -
Tests are run in random order and the seed for NumPy's and Python's random number generators are set by
pytest-randomly
. (#692) -
Adds support for Python 3.10. #695
Breaking Changes
-
DeviceSpec
is renamed toDevice
, which now also contains more than only the device specification. (#679)>>> import strawberryfields as sf >>> eng = sf.RemoteEngine("X8") >>> isinstance(eng.device, sf.Device) True >>> print(eng.device.target) X8_01
Bug fixes
-
It's now possible to show graphs using the plot apps layer when not run in notebooks. (#669)
-
program.compile
now raises an error if the device specification contains gate parameters but no circuit layout. Without a layout, the gate parameters cannot be validated against the device specification. (#661) -
The teleportation tutorial
examples/teleportation.py
now uses the correct value (nowphi = 0
instead ofphi = np.pi / 2
) for the phase shift of the beamsplitters. (#674) -
Program.compile()
returns a deep copy of the program attributes, except for the circuit and the register references. (#688)
Contributors
This release contains contributions from (in alphabetical order):
Sebastian Duque, Theodor Isacsson, Jon Schlipf, Hossein Seifoory
Release 0.21.0
New features since last release
-
A
Result.metadata
property is added to retrieve the metadata of a job result. (#663) -
A setter method for
Result.state
is added for setting a state for a local simulation if a state has not previously been set. (#663) -
Functions are now available to convert between XIR and Strawberry Fields programs. (#643)
For example,
prog = sf.Program(3) eng = sf.Engine("gaussian") with prog.context as q: ops.Sgate(0, 0) | q[0] ops.Sgate(1, 0) | q[1] ops.BSgate(0.45, 0.0) | (q[0], q[2]) ops.MeasureFock() | q[0] xir_prog = sf.io.to_xir(prog)
resulting in the following XIR script
>>> print(xir_prog.serialize()) Sgate(0, 0) | [0]; Sgate(1, 0) | [1]; BSgate(0.45, 0.0) | [0, 2]; MeasureFock | [0];
Bug fixes
-
The
TDMProgram.compile_info
andTDMProgram.target
fields are now set when aTDMProgram
is compiled using the "TDM" compiler. (#659) -
Updates
Program.assert_max_number_of_measurements
to expect the maximum number of measurements from the device specification as a flat dictionary entry instead of a nested one. (#662)"modes": { "pnr_max": 20, "homodyne_max": 1000, "heterodyne_max": 1000, }
instead of
"modes": { "max": { "pnr": 20, "homodyne": 1000, "heterodyne": 1000, } }
Documentation
- README has been ported to Markdown. (#664)
Contributors
This release contains contributions from (in alphabetical order):
Theodor Isacsson
Release 0.20.0
New features since last release
-
The generic multimode Gaussian gate
Ggate
is now available in thesf.ops
module with the backend choice oftf
. The N modeGgate
can be parametrized by a real symplectic matrixS
(size2N * 2N
) and a diplacement vectord
(sizeN
). You can also obtain the gradients of the Ggate gate via TensorFlow'stape.gradient
(#599) (#606)from thewalrus.random import random_symplectic num_mode = 2 cutoff = 10 S = tf.Variable(random_symplectic(num_mode)) d = tf.Variable(np.random.random(2 * num_mode)) eng = sf.Engine("tf", backend_options={"cutoff_dim": cutoff}) prog = sf.Program(2) with prog.context as q: sf.ops.Ggate(S, d) | (q[0], q[1]) state_out = eng.run(prog).state.ket()
Note that in order to update the parameter
S
by using its gradient, you cannot use gradient descent directly (as the unitary would not be symplectic after the update). Please use the functionsf.backends.tfbackend.update_symplectic
which is designed specifically for this purpose.def overlap_loss(state, objective): return -tf.abs(tf.reduce_sum(tf.math.conj(state) * objective)) ** 2 def norm_loss(state): return -tf.abs(tf.linalg.norm(state)) ** 2 def loss(state, objective): return overlap_loss(state, objective) + norm_loss(state) num_mode = 1 cutoff = 10 S = tf.Variable(random_symplectic(num_mode)) d = tf.Variable(np.random.random(2 * num_mode)) kappa = tf.Variable(0.3) objective = tf.Variable(np.eye(cutoff)[1], dtype=tf.complex64) adam = tf.keras.optimizers.Adam(learning_rate=0.01) eng = sf.Engine("tf", backend_options={"cutoff_dim": cutoff}) prog = sf.Program(1) with prog.context as q: sf.ops.Ggate(S, d) | q sf.ops.Kgate(kappa) | q loss_vals = [] for _ in range(200): with tf.GradientTape() as tape: state_out = eng.run(prog).state.ket() loss_val = loss(state_out, objective) eng.reset() grad_S, gradients_d, gradients_kappa = tape.gradient(loss_val, [S, d, kappa]) adam.apply_gradients(zip([gradients_d, gradients_kappa], [d, kappa])) update_symplectic(S, grad_S, lr=0.1) # update S here loss_vals.append(loss_val)
Breaking Changes
-
Complex parameters of the
Catstate
operation are expected in polar form as two separate real parameters. (#441) -
The
sf
CLI has been removed in favour of the Xanadu Cloud Client. (#642)-
Configuring account credentials using:
-
Strawberry Fields v0.19.0
$ sf configure --token "foo"
-
Strawberry Fields v0.20.0
$ xcc config set REFRESH_TOKEN "foo" Successfully updated REFRESH_TOKEN setting to 'foo'.
-
-
Verifying your connection to the Xanadu Cloud using:
-
Strawberry Fields v0.19.0
$ sf --ping You have successfully authenticated to the platform!
-
Strawberry Fields v0.20.0
$ xcc ping Successfully connected to the Xanadu Cloud.
-
-
Submitting a Blackbird circuit to the Xanadu Cloud using:
-
Strawberry Fields v0.19.0
$ # Version 0.19.0 $ sf run "foo.xbb" Executing program on remote hardware... 2021-11-02 03:04:05,06 - INFO - The device spec X8_01 has been successfully retrieved. 2021-11-02 03:04:05,07 - INFO - Compiling program for device X8_01 using compiler Xunitary. 2021-11-02 03:04:05,08 - INFO - Job b185a63c-f302-4adb-acf8-b6e4e413c11d was successfully submitted. 2021-11-02 03:04:05,09 - INFO - The remote job b185a63c-f302-4adb-acf8-b6e4e413c11d has been completed. [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]
-
Strawberry Fields v0.20.0
$ xcc job submit --name "bar" --target "X8_01" --circuit "$(cat foo.xbb)" { "id": "0b0f5a46-46d8-4157-8005-45a4764361ba", # Use this ID below. "name": "bar", "status": "open", "target": "X8_01", "language": "blackbird:1.0", "created_at": "2021-11-02 03:04:05,10", "finished_at": null, "running_time": null, "metadata": {} } $ xcc job get 0b0f5a46-46d8-4157-8005-45a4764361ba --result { "output": [ "[[0 0 0 0]\n[0 0 0 0]\n[0 0 0 0]\n[0 0 0 0]]" ] }
-
-
-
The
sf.api.Connection
class has been replaced with the xcc.Connection class. (#645)Previously, in Strawberry Fields v0.19.0, an
sf.RemoteEngine
can be instantiated with a custom Xanadu Cloud connection as follows:import strawberryfields as sf import strawberryfields.api connection = strawberryfields.api.Connection( token="Xanadu Cloud API key goes here", host="platform.strawberryfields.ai", port=443, use_ssl=True, ) engine = sf.RemoteEngine("X8", connection=connection)
In Strawberry Fields v0.20.0, the same result can be achieved using
import strawberryfields as sf import xcc connection = xcc.Connection( refresh_token="Xanadu Cloud API key goes here", # See "token" argument above. host="platform.strawberryfields.ai", port=443, tls=True, # See "use_ssl" argument above. ) engine = sf.RemoteEngine("X8", connection=connection)
-
The
sf.configuration
module has been replaced with the xcc.Settings class. (#649)This means that Xanadu Cloud credentials are now stored in exactly one location, the path to which depends on your operating system:
-
Windows:
C:\Users\%USERNAME%\AppData\Local\Xanadu\xanadu-cloud\.env
-
MacOS:
/home/$USER/Library/Application\ Support/xanadu-cloud/.env
-
Linux:
/home/$USER/.config/xanadu-cloud/.env
The format of the configuration file has also changed to .env and the names of some fields have been updated. For example,
# Strawberry Fields v0.19.0 (config.toml) [api] authentication_token = "Xanadu Cloud API key goes here" hostname = "platform.strawberryfields.ai" port = 443 use_ssl = true
is equivalent to
# Strawberry Fields v0.20.0 (.env) XANADU_CLOUD_REFRESH_TOKEN='Xanadu Cloud API key goes here' XANADU_CLOUD_HOST='platform.strawberryfields.ai' XANADU_CLOUD_PORT=443 XANADU_CLOUD_TLS=True
Similarly, the names of the configuration environment variables have changed from
# Strawberry Fields v0.19.0 export SF_API_AUTHENTICATION_TOKEN="Xanadu Cloud API key goes here" export SF_API_HOSTNAME="platform.strawberryfields.ai" export SF_API_PORT=443 export SF_API_USE_SSL=true
to
# Strawberry Fields v0.20.0 export XANADU_CLOUD_REFRESH_TOKEN="Xanadu Cloud API key goes here" export XANADU_CLOUD_HOST="platform.strawberryfields.ai" export XANADU_CLOUD_PORT=443 export XANADU_CLOUD_TLS=true
Finally,
strawberryfields.store_account()
has been replaced such that# Strawberry Fields v0.19.0 import strawberryfields as sf sf.store_account("Xanadu Cloud API key goes here")
becomes
# Strawberry Fields v0.20.0 import xcc xcc.Settings(REFRESH_TOKEN="Xanadu Cloud API key goes here").save()
-
-
The
sf.api.Job
class has been replaced with the xcc.Job class. (#650)A
Job
object is returned when running jobs asynchronously. In previous versions of Strawberry Fields (v0.19.0 and lower), theJob
object can be used as follows:>>> job = engine.run_async(program, shots=1) >>> job.status 'queued' >>> job.result InvalidJobOperationError >>> job.refresh() >>> job.status 'complete' >>> job.result [[0 1 0 2 1 0 0 0]]
In Strawberry Fields v0.20.0, the
Job
object works slightly differently:>>> job = engine.run_async(program, shots=1) >>> job.status 'queued' >>> job.wait() >>> job.status 'complete' >>> job.result {'output': [array([[0 1 0 2 1 0 0 0]])]}
The
job.wait()
method is a blocking method that will wait for the job to finish. Alternatively,job.clear()
can be called to clear the cache, allowingjob.status
to re-fetch the job status. -
The
sf.api.Result
class has been updated to support the Xanadu Cloud Client integration. (#651)While
Result.samples
should return the same type and shape as before, theResult.all_samples
property has been renamed toResult.samples_dict
and returns the samples as a dictionary with corresponding measured modes as keys.>>> res = eng.run(prog, shots=3) >>> res.samples array([[1, 0], [0, 1], [1, 1]]) >>> res.samples_dict {0: [np.array([1, 0, 1])], 1: [np.array([0, 1, 1])]} `...
Release 0.19.0
New features since last release
-
Compact decompositions as described in https://arxiv.org/abs/2104.07561, (
rectangular_compact
andtriangular_compact
) are now available in thesf.decompositions
module, and as options in theInterferometer
operation. (#584)This decomposition allows for lower depth photonic circuits in physical devices by applying two independent phase shifts in parallel inside each Mach-Zehnder interferometer.
rectangular_compact
reduces the layers of phase shifters from 2N+1 to N+2 for an N mode interferometer when compared to e.g.rectangular_MZ
.Example:
import numpy as np from strawberryfields import Program from strawberryfields.ops import Interferometer from scipy.stats import unitary_group M = 10 # generate a 10x10 Haar random unitary U = unitary_group.rvs(M) prog = Program(M) with prog.context as q: Interferometer(U, mesh='rectangular_compact') | q # check that applied unitary is correct compiled_circuit = prog.compile(compiler="gaussian_unitary") commands = compiled_circuit.circuit S = commands[0].op.p[0] # symplectic transformation Uout = S[:M,:M] + 1j * S[M:,:M] # unitary transformation print(np.allclose(U, Uout))
-
A new compiler,
GaussianMerge
, has been added. It is aimed at reducing calculation overhead for non-Gaussian circuits by minimizing the amount of Gaussian operations in a circuit, while retaining the same functionality. (#591)GaussianMerge
merges Gaussian operations, where allowed, intoGaussianTransform
andDgate
operations. It utilizes the existingGaussianUnitary
compiler to merge operations and Directed Acyclic Graphs to determine which operations can be merged.modes = 4 cutoff_dim = 6 # prepare an intial state with 4 photons in as many modes initial_state = np.zeros([cutoff_dim] * modes, dtype=complex) initial_state[1, 1, 1, 1] = 1 prog = sf.Program(4) with prog.context as q: ops.Ket(initial_state) | q # Initial state preparation # Gaussian Layer ops.S2gate(0.01, 0.01) | (q[0], q[1]) ops.BSgate(1.9, 1.7) | (q[1], q[2]) ops.BSgate(0.9, 0.2) | (q[0], q[1]) # Non-Gaussian Layer ops.Kgate(0.5) | q[3] ops.CKgate(0.7) | (q[2], q[3]) # Gaussian Layer ops.BSgate(1.0, 0.4) | (q[0], q[1]) ops.BSgate(2.0, 1.5) | (q[1], q[2]) ops.Dgate(0.01) | q[0] ops.Dgate(0.01) | q[0] ops.Sgate(0.01, 0.01) | q[1] # Non-Gaussian Layer ops.Vgate(0.5) | q[2] prog_merged = prog.compile(compiler="gaussian_merge")
-
A new operation,
PassiveChannel
has been added. It allows for arbitrary linear/passive transformations (i.e., any operation which is linear in creation operators). Currently only supported by thegaussian
backend. (#600)from strawberryfields.ops import PassiveChannel, Sgate import strawberryfields as sf from scipy.stats import unitary_group import numpy as np M = 4 circuit = sf.Program(M) U1 = unitary_group.rvs(M) U2 = unitary_group.rvs(M) losses = np.random.random(M) T = U2 @ np.diag(losses) @ U1 eng = sf.Engine(backend='gaussian') circuit = sf.Program(M) with circuit.context as q: for i in range(M): ops.Sgate(1) | q[i] ops.PassiveChannel(T) | q cov = eng.run(circuit).state.cov()
-
A new compiler,
passive
, allows for a circuit which only consists of passive elements to be compiled into a singlePassiveChannel
. (#600)from strawberryfields.ops import BSgate, LossChannel, Rgate import strawberryfields as sf circuit = sf.Program(2) with circuit.context as q: Rgate(np.pi) | q[0] BSgate(0.25 * np.pi, 0) | (q[0], q[1]) LossChannel(0.9) | q[1] compiled_circuit = circuit.compile(compiler="passive")
>>> print(compiled_circuit) PassiveChannel([[-0.7071+8.6596e-17j -0.7071+0.0000e+00j] [-0.6708+8.2152e-17j 0.6708+0.0000e+00j]]) | (q[0], q[1])
Improvements
-
backends/tfbackend/ops.py
is cleaned up to reduce line count, clarify function similarity across backend ops, and replacetensorflow.tensordot
with broadcasting. (#567) -
Support is added for using a
TDMProgram
to construct time-domain circuits with Fock measurements and multiple loops. (#601) -
measure_threshold
in thegaussian
backend now supports displaced Gaussian states. (#615) -
Speed improvements are addded to
gaussian_unitary
compiler. (#603) -
Adds native support in the Fock backend for the MZgate. (#610)
-
measure_threshold
is now supported in thebosonic
backend. (#618)
Bug fixes
-
Fixes an unexpected behaviour that can result in increasing memory usage due to
sympy.lambdify
caching too much data usinglinecache
. (#579) -
Keeps symbolic expressions when converting a Strawberry Fields circuit to a Blackbird program by storing them as
blackbird.RegRefTransforms
in the resulting Blackbird program. (#596) -
Fixes a bug in the validation step of
strawberryfields.tdm.TdmProgram.compile
which almost always used the wrong set of allowed gate parameter ranges to validate the parameters in a program. (#605) -
The correct samples are now returned when running a
TDMProgram
with several shots, wheretimebins % concurrent_modes != 0
. (#611) -
Fixes the formula used for sampling generaldyne outcomes in the gaussian backend. (#614)
-
Measurement arguments are now stored as non-keyword arguments, instead of keyword arguments, in the resulting Blackbird program when using the
io.to_blackbird()
converter function. (#622) -
Factorials of numbers larger than 170 are now calculated using long integer arithmetic, using the flag
exact=True
inscipy.special.factorial
, when callingsf.apps.similarity.orbit_cardinality
. (#628)
Documentation
- References to the
simulon
simulator target have been rewritten tosimulon_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)
Contributors
This release contains contributions from (in alphabetical order):
J. Eli Bourassa, Jake Bulmer, Sebastian Duque, Theodor Isacsson, Aaron Robertson, Jeremy Swinarton, Antal Száva, Federico Rueda, Yuan Yao.
Release 0.18.0-post1
Documentation
- References to the
simulon
simulator target have been rewritten tosimulon_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)
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 combinations of Gaussian functions in phase space. (#533) (#538) (#539) (#541) (#546) (#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,
cat and Fock states.prog = sf.Program(1) eng = sf.Engine('bosonic') with prog.context as q: sf.ops.GKP(epsilon=0.1) | q sf.ops.MeasureX | q results = eng.run(prog, shots=200) samples = results.samples[:, 0] plt.hist(samples, bins=100) plt.show()
-
Adds the
sf.ops.GKP
operation, which allows the Gottesman-Kitaev-Preskill state to be initialized on both the Bosonic and Fock backends. (#553) (#546)GKP states are qubits, with the qubit state defined by:
where the computational basis states are
-
Adds the measurement-based squeezing gate
MSgate
; a new front-end operation for the Bosonic backend. (#538) (#539) (#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 single-shot implementation is used, the measurement outcome of the ancillary mode is stored in the results object.prog = sf.Program(1) eng = sf.Engine('bosonic') with prog.context as q: sf.ops.Catstate(alpha=2) | q r = 0.3 # Average map 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()
-
The
tf
backend now accepts the Tensor DType as argument. (#562)Allows high cutoff dimension to give numerically correct calculations:
prog = sf.Program(2) eng = sf.Engine("tf", backend_options={"cutoff_dim": 50, "dtype": tf.complex128}) with prog.context as q: Sgate(0.8) | q[0] Sgate(0.8) | q[1] BSgate(0.5,0.5) | (q[0], q[1]) BSgate(0.5,0.5) | (q[0], q[1]) state = eng.run(prog).state N0, N0var = state.mean_photon(0) N1, N1var = state.mean_photon(1) print(N0) print(N1) print("analytical:", np.sinh(0.8)**2)
Improvements
-
Program compilation has been modified to support the XQC simulation service, Simulon. (#545)
-
The
sympmat
,rotation_matrix
, andhaar_measure
functions have been removed frombackends/shared_ops.py
. These functions are now imported from The Walrus. In addition, various outdated functionality from theshared_ops.py
file has been removed, including the caching of beamsplitter and squeezing pre-factors. (#560) (#558) -
Sample processing in the
TDMProgram
is now more efficient, by replacing calls topop
with fancy indexing. (#548) -
No
VisibleDeprecationWarning
is raised when using the statewigner
method. (#564) -
The backend utility module
shared_ops.py
has been removed, with all of its functionality now provided by The Walrus. (#573)
Breaking changes
- Removes support for Python 3.6. (#573)
Bug fixes
-
Connection
objects now send requests to the platform API at version0.2.0
instead of the incorrect version number1.0.0
. (#540) -
TDM programs now expect a flat (not nested) dictionary of
modes
in device specifications obtained from the XQC platform API. (#566) -
Fixes a bug in the
CatState
operation, whereby the operation would return incorrect results for a high cutoff value. (#557) (#556)
Documentation
-
The "Hardware" quickstart page has been renamed to "Xanadu Quantum Cloud" to encompass both hardware and cloud simulators. A new "Cloud simulator" entry has been added, describing how to submit programs to be executed via the XQC simulator. (#547)
-
Cleanup docs to make contribution easier. (#561)
-
Add development requirements and format script to make contribution easier. (#563)
Contributors
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, Jeremy Swinarton, Antal Száva, Ilan Tzitrin.
Release 0.17.0
New features since last release
-
TDMProgram
objects can now be compiled and submitted via the API. (#476) -
Wigner functions can be plotted directly via Strawberry Fields using Plot.ly. (#495)
prog = sf.Program(1) eng = sf.Engine('fock', backend_options={"cutoff_dim": 10}) with prog.context as q: gamma = 2 Vgate(gamma) | q[0] state = eng.run(prog).state xvec = np.arange(-4, 4, 0.01) pvec = np.arange(-4, 4, 0.01) mode = 0 sf.plot_wigner(state, mode, xvec, pvec, renderer="browser")
-
Fock state marginal probabilities can be plotted directly via Strawberry Fields using Plot.ly. (#510)
prog = sf.Program(1) eng = sf.Engine('fock', backend_options={"cutoff_dim":5}) with prog.context as q: Sgate(0.5) | q[0] state = eng.run(prog).state state.all_fock_probs() modes = [0] sf.plot_fock(state, modes, cutoff=5, renderer="browser")
-
Position and momentum quadrature probabilities can be plotted directly via Strawberry Fields using Plot.ly. (#510)
prog = sf.Program(1) eng = sf.Engine('fock', backend_options={"cutoff_dim":5}) with prog.context as q: Sgate(0.5) | q[0] state = eng.run(prog).state modes = [0] xvec = np.arange(-4, 4, 0.1) pvec = np.arange(-4, 4, 0.1) sf.plot_quad(state, modes, xvec, pvec, renderer="browser")
-
Strawberry Fields code can be generated from a program (and an engine) by calling
sf.io.generate_code(program, eng=engine)
. (#496)
Improvements
-
Connection
objects now send versioned requests to the platform API. (#512) -
TDMProgram
allows application of gates with more than one symbolic parameter. #492 -
The
copies
option, when constructing aTDMProgram
, has been removed. Instead, the number of copies of a TDM algorithm can now be set by passing theshots
keyword argument to theeng.run()
method. (#489)>>> with prog.context([1, 2], [3, 4]) as (p, q): ... ops.Sgate(0.7, 0) | q[1] ... ops.BSgate(p[0]) | (q[0], q[1]) ... ops.MeasureHomodyne(p[1]) | q[0] >>> eng = sf.Engine("gaussian") >>> results = eng.run(prog, shots=3)
Furthermore, the
TDMProgram.unrolled_circuit
attribute now only contains the single-shot unrolled circuit. Unrolling with multiple shots can still be specified via theunroll
method:TDMProgram.unroll(shots=60)
. -
The
Result.samples
returned by TDM programs has been updated to return samples of shape(shots, spatial modes, timebins)
instead of(shots, spatial modes * timebins)
. (#489) -
A sample post-processing function is added that allows users to move vacuum mode measurements from the first shots to the last shots, and potentially crop out the final shots containing these measurements. (#489)
-
pytest-randomly
is added to the SF tests. (#480) -
TDMProgram
objects can now be serialized into Blackbird scripts, and vice versa. (#476)
Breaking Changes
- Jobs are submitted to the Xanadu Quantum Cloud through a new OAuth based authentication flow using offline refresh tokens and access tokens. (#520)
Bug fixes
-
Fixes a bug where
Dgate
,Coherent
, andDisplacedSqueezed
do not support TensorFlow tensors if the tensor has an added dimension due to the existence of batching. (#507) -
Fixes an issue with
reshape_samples
where the samples were sometimes reshaped in the wrong way. (#489) -
The list of modes is now correctly added to the Blackbird program when using the
io.to_blackbird
function. (#476) -
Fixes a bug where printing the
Result
object containing samples from a time-domain job would result in an error. Printing the result object now correctly displays information about the results. (#493) -
Removes the
antlr4
requirement due to version conflicts. (#494) -
TDMProgram.run_options
is now correctly used when running a TDM program. (#500) -
Fixes a bug where a single parameter list passed to the
TDMProgram
context results in an error. (#503)
Documentation
-
TDMProgram
docstring is updated to make it clear that only Gaussian programs are allowed. (#519) -
Clarifies special cases for the
MZgate
in the docstring. (#479)
Contributors
This release contains contributions from (in alphabetical order):
Tom Bromley, Jack Brown, Theodor Isacsson, Josh Izaac, Fabian Laudenbach, Tim Leisti, Nicolas Quesada, Antal Száva.
Release 0.16.0
New features since last release
-
Adds the ability to construct time domain multiplexing algorithms via the new
sf.TDMProgram
class, for highly scalable simulation of Gaussian states. (#440)For example, creating and simulating a time domain program with 2 concurrent modes:
>>> import strawberryfields as sf >>> from strawberryfields import ops >>> prog = sf.TDMProgram(N=2) >>> with prog.context([1, 2], [3, 4], copies=3) as (p, q): ... ops.Sgate(0.7, 0) | q[1] ... ops.BSgate(p[0]) | (q[0], q[1]) ... ops.MeasureHomodyne(p[1]) | q[0] >>> eng = sf.Engine("gaussian") >>> results = eng.run(prog) >>> print(results.all_samples) {0: [array([1.26208025]), array([1.53910032]), array([-1.29648336]), array([0.75743215]), array([-0.17850101]), array([-1.44751996])]}
For more details, see the code documentation.
-
Adds the function
VibronicTransition
to theapps.qchem.vibronic
module. This function generates a custom Strawberry Fields operation for applying the Doktorov operator on a given state. (#451)>>> from strawberryfields.apps.qchem.vibronic import VibronicTransition >>> modes = 2 >>> p = sf.Program(modes) >>> with p.context as q: ... VibronicTransition(U1, r, U2, alpha) | q
-
Adds the
TimeEvolution
function to theapps.qchem.dynamics
module. This function generates a custom Strawberry Fields operation for applying a time evolution operator on a given state. (#455)>>> modes = 2 >>> p = sf.Program(modes) >>> with p.context as q: ... sf.ops.Fock(1) | q[0] ... sf.ops.Interferometer(Ul.T) | q ... TimeEvolution(w, t) | q ... sf.ops.Interferometer(Ul) | q
where
w
is the normal mode frequencies, andt
the time in femtoseconds. -
Molecular data and pre-generated samples for water and pyrrole have been added to the data module of the Applications layer of Strawberry Fields. For more details, please see the data module documentation (#463)
-
Adds the function
read_gamess
to the qchem module to extract the atomic coordinates, atomic masses, vibrational frequencies, and normal modes of a molecule from the output file of a vibrational frequency calculation performed with the GAMESS quantum chemistry package. (#460)>>> r, m, w, l = read_gamess('../BH_data.out') >>> r # atomic coordinates array([[0.0000000, 0.0000000, 0.0000000], [1.2536039, 0.0000000, 0.0000000]]) >>> m # atomic masses array([11.00931, 1.00782]) >>> w # vibrational frequencies array([19.74, 19.73, 0.00, 0.00, 0.00, 2320.32]) >>> l # normal modes array([[-0.0000000e+00, -7.5322000e-04, -8.7276210e-02, 0.0000000e+00, 8.2280900e-03, 9.5339055e-01], [-0.0000000e+00, -8.7276210e-02, 7.5322000e-04, 0.0000000e+00, 9.5339055e-01, -8.2280900e-03], [ 2.8846925e-01, -2.0000000e-08, 2.0000000e-08, 2.8846925e-01, -2.0000000e-08, 2.0000000e-08], [ 2.0000000e-08, 2.8846925e-01, -2.0000000e-08, 2.0000000e-08, 2.8846925e-01, -2.0000000e-08], [-2.0000000e-08, 2.0000000e-08, 2.8846925e-01, -2.0000000e-08, 2.0000000e-08, 2.8846925e-01], [-8.7279460e-02, 0.0000000e+00, 0.0000000e+00, 9.5342606e-01, -0.0000000e+00, -0.0000000e+00]])
Improvements
- When jobs submitted to the Xanadu Quantum Cloud are canceled, they will now display a
cancel_pending
JobStatus until the cancellation is confirmed. (#456)
Bug fixes
-
Fixed a bug where the function
reduced_dm
inbackends/tfbackend/states.py
gives the wrong output when passing it several modes. (#471) -
Fixed a bug in the function
reduced_density_matrix
inbackends/tfbackend/ops.py
which caused the wrong subsystems to be traced out. (#467) (#470) -
Fixed a bug where decompositions to Mach-Zehnder interferometers would return incorrect results on NumPy 1.19. (#473)
-
The Walrus version 0.14 introduced modified function names. Affected functions have been updated in Strawberry Fields to avoid deprecation warnings. (#472)
Documentation
-
Adds further testing and coverage descriptions to the developer documentation. This includes details regarding the Strawberry Fields test structure and test decorators. (#461)
-
Updates the minimum required version of TensorFlow in the development guide. (#468)
Contributors
This release contains contributions from (in alphabetical order):
Juan Miguel Arrazola, Tom Bromley, Theodor Isacsson, Josh Izaac, Soran Jahangiri, Nathan Killoran, Fabian Laudenbach, Nicolás Quesada, Antal Száva, Ilan Tzitrin.