Skip to content

Release 0.19.0

Compare
Choose a tag to compare
@thisac thisac released this 23 Sep 22:21
· 66 commits to master since this release
3c8b509

New features since last release

  • Compact decompositions as described in https://arxiv.org/abs/2104.07561, (rectangular_compact and triangular_compact) are now available in the sf.decompositions module, and as options in the Interferometer 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, into GaussianTransform and Dgate operations. It utilizes the existing GaussianUnitary 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 the gaussian 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 single PassiveChannel. (#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 replace tensorflow.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 the gaussian 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 the bosonic backend. (#618)

Bug fixes

  • Fixes an unexpected behaviour that can result in increasing memory usage due to sympy.lambdify caching too much data using linecache. (#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, where timebins % 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 in scipy.special.factorial, when calling sf.apps.similarity.orbit_cardinality. (#628)

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)

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.