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.