Compilng in SF into elementary gates

Hi there,

I am confused how SF works in this field. My code is here:

from strawberryfields.ops import *
import strawberryfields as sf
import numpy as np

########PART1##########

params = np.array([7.21918270e-02, 1.55960277e-01, 1.21196523e-01,
                   -3.60478170e-01, 6.60343841e-02, -1.56058113e+00,
                   -6.81921893e-01, -2.56065964e-01, -4.90345374e-02,
                   2.09721495e+00, -6.87432735e-01, -2.30891734e+00,
                   1.77080752e-03, 3.58678515e-01])

circuit = sf.Program(2)
with circuit.context as q:
    BSgate(params[0], params[1]) | (q[0], q[1])
    Rgate(params[2]) | q[0]

    Sgate(params[3]) | q[0]
    Sgate(params[4]) | q[1]

    BSgate(params[5], params[6]) | (q[0], q[1])
    Rgate(params[7]) | q[0]
    Dgate(params[8], params[10]) | q[0]
    Dgate(params[9], params[11]) | q[1]

    BSgate(params[0], params[1]) | (q[0], q[1])
    Rgate(params[2]) | q[0]

    Sgate(params[3]) | q[0]
    Sgate(params[4]) | q[1]

    BSgate(params[5], params[6]) | (q[0], q[1])
    Rgate(params[7]) | q[0]
    Dgate(params[8], params[10]) | q[0]
    Dgate(params[9], params[11]) | q[1]

comp = sf.compilers.GaussianUnitary()
# comp = sf.compilers.GaussianMerge()
comp.gaussian_ops = [
    "Dgate",
    "BSgate",
    "Sgate",
    "Rgate",
]
comp.decompositions = "Gaussian"
comp.primitives = {
    "Dgate",
    "Sgate",
    "Rgate",
    "BSgate",
}
compiled_circuit = circuit.compile(compiler=comp)

compiled_circuit.print()

########PART2##########

prog = sf.Program(2)
with prog.context as q:
    GaussianTransform(np.array([[-1.3423, 0.1483, 0.0222, -0.0622],
                                       [-0.1654, -1.091, -0.0234, 0.2211],
                                       [0.3641, 0.0279, -0.7371, 0.1139],
                                       [0.1515, 0.4469, -0.0912, -0.9901]])) | (q[0], q[1])

compiled_circuit = prog.compile(compiler='Gaussian')
compiled_circuit.print()

The output of the first part is simply

GaussianTransform([[-1.3423  0.1483  0.0222 -0.0622]
 [-0.1654 -1.091  -0.0234  0.2211]
 [ 0.3641  0.0279 -0.7371  0.1139]
 [ 0.1515  0.4469 -0.0912 -0.9901]]) | (q[0], q[1])
Dgate(2.093, -1.84) | (q[0])
Dgate(2.219, -2.347) | (q[1])

Now, plugging the Gaussian transformation into a new circuit gives

ValueError: The input matrix is not symplectic

The first question is, how can it be that a Gaussian transformation is not symplectic (I also played around with the tolerance)? My second question is, given the Gaussian matrix is correct, on how to decompose it in SF into S, BS and R gates (Dagtes)? I thought Bloch-Messiah decomposition is used, but I cant’t use it in SF.

I apprechiate you help :slight_smile:

Hi @zephir !

So the issue here is that GaussianTransform requires that the input matrix S is already symplectic. In your case it looks like the matrix that you added is not symplectic, so that’s why you’re getting an error. See the example below where the matrix is indeed symplectic and you don’t get the error anymore.

The compilation step that you’re adding shows you the decomposition that you want to see. I hope this helps!

########PART2##########

prog = sf.Program(2)
with prog.context as q:
    GaussianTransform(sf.utils.random_symplectic(2)) | (q[0], q[1])

compiled_circuit = prog.compile(compiler='gaussian') # lowercase gaussian
compiled_circuit.print()
2 Likes

Dear @CatalinaAlbornoz ,
thank you for the quick response! But why does the first part generate a non-symplectic matrix out of Gaussian gates?

Best, Zephir

Hi @zephir ,

To be honest I’m not sure. Debugging this might take a while. Is this blocking work?
If necessary you can use the symplectic module in The Walrus. The Walrus is a library used under-the-hood by Strawberry Fields. You can get the matrices for different gates such as rotation, squeezing and beamsplitter, and you can use the is_symplectic function to check that it is indeed symplectic.

I hope this helps in the meantime and thanks again for pointing this out.