Пример #1
0
def circuit(X):
    # Create a parameter with an initial value of 2.
    params = [make_param(name='phi', constant=2.)]

    eng, q = sf.Engine(2)

    with eng:
        # Note that we are feeding 1-d tensors into gates, not scalars!
        Dgate(X[:, 0], 0.) | q[0]
        Dgate(X[:, 1], 0.) | q[1]
        BSgate(phi=params[0]) | (q[0], q[1])
        BSgate() | (q[0], q[1])

    # We have to tell the engine how big the batches (first dim of X) are
    # which we feed into gates
    num_inputs = X.get_shape().as_list()[0]
    state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs)

    # Define the output as the probability of measuring |0,2> as opposed to |2,0>
    p0 = state.fock_prob([0, 2])
    p1 = state.fock_prob([2, 0])
    normalization = p0 + p1 + 1e-10
    circuit_output = p1 / normalization

    return circuit_output
Пример #2
0
def qnn_layer(layer_number):
    with tf.name_scope('layer_{}'.format(layer_number)):
        BSgate(bs_variables[layer_number, 0, 0, 0], bs_variables[layer_number, 0, 0, 1]) \
        | (q[0], q[1])

        for i in range(mode_number):
            Rgate(phase_variables[layer_number, i, 0]) | q[i]

        for i in range(mode_number):
            Sgate(
                tf.clip_by_value(sq_magnitude_variables[layer_number, i],
                                 -sq_clip, sq_clip),
                sq_phase_variables[layer_number, i]) | q[i]

        BSgate(bs_variables[layer_number, 0, 1, 0], bs_variables[layer_number, 0, 1, 1]) \
        | (q[0], q[1])

        for i in range(mode_number):
            Rgate(phase_variables[layer_number, i, 1]) | q[i]

        for i in range(mode_number):
            Dgate(
                tf.clip_by_value(disp_magnitude_variables[layer_number, i],
                                 -disp_clip, disp_clip),
                disp_phase_variables[layer_number, i]) | q[i]

        for i in range(mode_number):
            Kgate(
                tf.clip_by_value(kerr_variables[layer_number, i], -kerr_clip,
                                 kerr_clip)) | q[i]
def layer(l):
    with tf.name_scope('layer_{}'.format(l)):
        BSgate(theta1[l], phi1[l]) | (q[0], q[1])
        Rgate(r1[l]) | q[0]
        Sgate(tf.clip_by_value(sqr1[l], -sq_clip, sq_clip), sqphi1[l]) | q[0]
        Sgate(tf.clip_by_value(sqr2[l], -sq_clip, sq_clip), sqphi2[l]) | q[1]
        BSgate(theta2[l], phi2[l]) | (q[0], q[1])
        Rgate(r2[l]) | q[0]
        Dgate(tf.clip_by_value(dr1[l], -disp_clip, disp_clip), dphi1[l]) | q[0]
        Dgate(tf.clip_by_value(dr2[l], -disp_clip, disp_clip), dphi2[l]) | q[1]
        Kgate(tf.clip_by_value(kappa1[l], -kerr_clip, kerr_clip)) | q[0]
        Kgate(tf.clip_by_value(kappa2[l], -kerr_clip, kerr_clip)) | q[1]
Пример #4
0
def circuit(X):
    params = [
        make_param(name='phi', constant=2.),
        make_param(name="theta", constant=1.),
        make_param(name="theta2", constant=.1),
        make_param(name="theta3", constant=.1),
        make_param(name="theta4", constant=.1),
        make_param(name="theta5", constant=.1),
        make_param(name="theta6", constant=.1),
        make_param(name="theta7", constant=.1),
        make_param(name="theta8", constant=.1),
        make_param(name="theta9", constant=.1),
    ]

    eng, q = sf.Engine(2)

    with eng:
        Dgate(X[:, 0], 0.) | q[0]
        Dgate(X[:, 1], 0.) | q[1]
        BSgate(phi=params[0]) | (q[0], q[1])
        BSgate() | (q[0], q[1])
        Vgate(params[1]) | q[0]
        Vgate(params[2]) | q[1]

    num_inputs = X.get_shape().as_list()[0]
    state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs)

    p0 = state.fock_prob([0, 2])
    p1 = state.fock_prob([2, 0])
    normalization = p0 + p1 + 1e-10
    output1 = p1 / normalization

    with eng:
        X1 = output1
        Dgate(X1, 0.) | q[0]
        Dgate(0., X1) | q[1]
        BSgate(phi=params[1]) | (q[0], q[1])
        BSgate() | (q[0], q[1])
        Vgate(params[5]) | q[0]
        Vgate(params[6]) | q[1]

    num_inputs1 = X1.get_shape().as_list()[0]
    state2 = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs1)

    p00 = state2.fock_prob([0, 2])
    p01 = state2.fock_prob([2, 0])
    normalization1 = p00 + p01 + 1e-10
    circuit_output = p01 / normalization1
    return circuit_output
Пример #5
0
    def single_input_circuit(x):

        eng.reset()
        with eng:
            Dgate(x[0], 0.) | q[0]
            Dgate(x[1], 0.) | q[1]
            BSgate(phi=params[0]) | (q[0], q[1])
            BSgate() | (q[0], q[1])
        state = eng.run('fock', cutoff_dim=10, eval=True)

        p0 = state.fock_prob([0, 2])
        p1 = state.fock_prob([2, 0])
        normalization = p0 + p1 + 1e-10
        output = p1 / normalization
        return output
def test_cov_is_pure():
    """Tests space unrolling when going into the Gaussian backend"""
    delays = [1, 6, 36]
    modes = 216
    angles = np.concatenate([
        generate_valid_bs_sequence(delays, modes),
        generate_valid_r_sequence(delays, modes)
    ])
    net = modes + sum(delays)
    d = len(delays)
    n, N = get_mode_indices(delays)
    prog = sf.TDMProgram([N])
    vac_modes = sum(delays)

    with prog.context(*angles) as (p, q):
        Sgate(0.8) | q[n[0]]
        for i in range(d):
            Rgate(p[i + d]) | q[n[i]]
            BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]])

    prog.space_unroll()

    eng = sf.Engine(backend="gaussian")
    results = eng.run(prog)
    cov = results.state.cov()
    mu = np.zeros(len(cov))
    mu_vac, cov_vac = reduced_state(mu, cov, list(range(vac_modes)))
    mu_comp, cov_comp = reduced_state(mu, cov, list(range(vac_modes, net)))
    assert np.allclose(cov_vac, 0.5 * (sf.hbar) * np.identity(2 * vac_modes))
    assert is_pure_cov(cov_comp, hbar=sf.hbar)
def test_is_permutation_when_angle_pi_on_two(delays, modes):
    """Checks that if all the beamsplitters are cross then the absolute value output matrix is a permutation matrix"""
    delays = list(delays)
    net = modes + sum(delays)
    angles = np.concatenate([
        generate_valid_bs_sequence(delays, modes),
        generate_valid_r_sequence(delays, modes)
    ])
    angles[0] = np.pi / 2 * np.random.randint(2, size=net)
    angles[1] = np.pi / 2 * np.random.randint(2, size=net)
    angles[2] = np.pi / 2 * np.random.randint(2, size=net)
    d = len(delays)
    n, N = get_mode_indices(delays)
    prog = sf.TDMProgram([N])
    vac_modes = sum(delays)

    with prog.context(*angles) as (p, q):
        for i in range(d):
            Rgate(p[i + d]) | q[n[i]]
            BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]])

    prog.space_unroll()

    compiled = prog.compile(compiler="passive")
    passive_elem = compiled.circuit[0]
    U = passive_elem.op.p[0]
    assert np.allclose(U @ U.T.conj(), np.identity(len(U)))
    assert np.allclose(list(map(max, np.abs(U))), 1.0)
def test_no_entanglement_between_padding_and_computational_modes(
        delays, modes):
    """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations"""
    delays = list(delays)
    angles = np.concatenate([
        generate_valid_bs_sequence(delays, modes),
        generate_valid_r_sequence(delays, modes)
    ])
    d = len(delays)
    n, N = get_mode_indices(delays)
    prog = sf.TDMProgram([N])
    vac_modes = sum(delays)

    with prog.context(*angles) as (p, q):
        for i in range(d):
            Rgate(p[i + d]) | q[n[i]]
            BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]])

    prog.space_unroll()

    compiled = prog.compile(compiler="passive")
    passive_elem = compiled.circuit[0]
    U = passive_elem.op.p[0]
    # Check that it is indeed the identity
    U_AA = U[:vac_modes, :vac_modes]
    U_AB = U[vac_modes:, :vac_modes]
    U_BA = U[:vac_modes, vac_modes:]
    U_BB = U[vac_modes:, vac_modes:]

    assert np.allclose(U_AA, np.identity(vac_modes))
    assert np.allclose(U_AB, 0)
    assert np.allclose(U_BA, 0)
    assert np.allclose(U_BB @ U_BB.T.conj(), np.identity(len(U_BB)))
Пример #9
0
    def decompose(self, reg):
        # make BS gate
        theta = self.layer['BS'][0]
        phi = self.layer['BS'][1]
        BS = BSgate(theta, phi)

        # make Kerr gate
        K = Kgate(self.layer['K'][0])

        # make rotation gate
        R = Rgate(self.layer['R'][0])

        cmds = []

        for i in range(self.num_layers):  #pylint: disable=unused-variable
            for q0, q1 in self.layer['BS'][2]:
                cmds.append(Command(BS, (reg[q0], reg[q1])))

            for mode in self.layer['K'][1]:
                cmds.append(Command(K, reg[mode]))

            for mode in self.layer['R'][1]:
                cmds.append(Command(R, reg[mode]))

        return cmds
Пример #10
0
    def single_input_circuit(x):

        eng.reset()
        with eng:
            Dgate(x[0], 0.) | q[0]
            Dgate(x[1], 0.) | q[1]
            BSgate(phi=params[0]) | (q[0], q[1])
            BSgate() | (q[0], q[1])
        state = eng.run('fock', cutoff_dim=10, eval=True)

        # Define the output as the probability of measuring |0,2> as opposed to |2,0>
        p0 = state.fock_prob([0, 2])
        p1 = state.fock_prob([2, 0])
        normalization = p0 + p1 + 1e-10
        output = p1 / normalization
        return output
Пример #11
0
def circuit(params, a, m, cutoff):
    """Runs the constrained variational circuit with specified parameters,
    returning the output fidelity to the requested ON state, as well as
    the post-selection probability.

    Args:
        params (list): list of gate parameters for the constrained
            variational quantum circuit. This should contain the following values
            in the following order:

            * ``'sq0' = (r, phi)``: the squeezing magnitude and phase on mode 0
            * ``'disp0' = (r, phi)``: the displacement magnitude and phase on mode 0
            * ``'sq1' = (r, phi)``: the squeezing magnitude and phase on mode 1
            * ``'disp1' = (r, phi)``: the displacement magnitude and phase on mode 1
            * ``'BS' = (theta, phi)``: the beamsplitter angles

        a (float): the ON state parameter
        m (int): the Fock state measurement to be post-selected
        cutoff (int): the Fock basis truncation

    Returns:
        tuple: a tuple containing the output fidelity to the target ON state,
            the probability of post-selection, the state norm before entering the beamsplitter,
            the state norm after exiting the beamsplitter, and the density matrix of the output state.
    """
    # define target state
    ONdm = on_state(a, cutoff)

    # unpack circuit parameters
    sq0_r, sq0_phi, disp0_r, disp0_phi, sq1_r, sq1_phi, disp1_r, disp1_phi, theta, phi = params

    # quantum circuit prior to entering the beamsplitter
    prog1 = sf.Program(2)
    with prog1.context as q1:
        Sgate(sq0_r, sq0_phi) | q1[0]
        Dgate(disp0_r, disp0_phi) | q1[0]
        Sgate(sq1_r, sq1_phi) | q1[1]
        Dgate(disp1_r, disp1_phi) | q1[1]

    eng = sf.Engine("fock", backend_options={"cutoff_dim": cutoff})
    stateIn = eng.run(prog1).state
    normIn = np.abs(stateIn.trace())

    # norm of output state and probability
    prog_BS = sf.Program(2)
    with prog_BS.context as q1:
        BSgate(theta, phi) | (q1[0], q1[1])

    stateOut = eng.run(prog_BS).state
    normOut = np.abs(stateOut.trace())
    rho = stateOut.dm()

    # probability of meausring m1 and m2
    prob = np.abs(np.trace(rho[m, m]))

    # output state
    rhoB = rho[m, m] / prob

    fidelity = np.abs(np.trace(np.einsum('ij,jk->ik', rhoB, ONdm)))
    return (fidelity, prob, normIn, normOut, rhoB)
Пример #12
0
 def test_is_channel_preparation(self, prog):
     """is_channel() returns False if preparations are present"""
     with prog.context as q:
         Sgate(0.4) | q[0]
         BSgate() | q
         Squeezed(0.4) | q[1]
     assert not utils.is_channel(prog)
Пример #13
0
 def test_is_unitary_no_channel(self, prog):
     """test that the is_unitary function returns True if no channels are present"""
     assert utils.is_unitary(prog)
     with prog.context as q:
         Sgate(0.4) | q[0]
         BSgate(0.4) | q
     assert utils.is_unitary(prog)
Пример #14
0
 def test_is_unitary_with_channel(self, prog):
     """test that the is_unitary function returns False if channels are present"""
     with prog.context as q:
         Sgate(0.4) | q[0]
         LossChannel(0.4) | q[0]
         BSgate(0.4) | q
     assert not utils.is_unitary(prog)
Пример #15
0
 def test_is_channel_no_measurement(self, prog):
     """test that the is_channel function returns True if no measurements are present"""
     assert utils.is_channel(prog)
     with prog.context as q:
         Sgate(0.4) | q[0]
         LossChannel(0.4) | q[0]
         BSgate(0.4) | q
     assert utils.is_channel(prog)
Пример #16
0
 def test_is_channel_measurement(self, prog):
     """is_channel() returns False if measurements are present"""
     with prog.context as q:
         Sgate(0.4) | q[0]
         BSgate() | q
         MeasureX | q[0]
         Sgate(0.4) | q[1]
     assert not utils.is_channel(prog)
Пример #17
0
def circuit(X):
    # Create a parameter with an initial value of 2.
    params = [make_param(name='phi', constant=2.), make_param(name="theta", constant=1.), make_param(name="theta2", constant=.1), make_param(name="theta3", constant=.1), 
              make_param(name="theta4", constant=.1), make_param(name="theta5", constant=.1),
              make_param(name="theta6", constant=.1), make_param(name="theta7", constant=.1),
              make_param(name="theta8", constant=.1), make_param(name="theta9", constant=.1),]

    eng, q = sf.Engine(2)

    with eng:
        # Note that we are feeding 1-d tensors into gates, not scalars!
        Dgate(X[:, 0], 0.) | q[0]
        Dgate(X[:, 1], 0.) | q[1]
        BSgate(phi=params[0]) | (q[0], q[1])
        BSgate() | (q[0], q[1])
        Vgate(params[1]) | q[0]
        Vgate(params[2]) | q[1]
Пример #18
0
    def layer(i, size):
        Rgate(rtheta_1[i, 0]) | (q[0])
        BSgate(phi_1[i, 0], 0) | (q[0], q[1])
        Rgate(rtheta_1[i, 2]) | (q[1])

        for j in range(size):
            Sgate(r[i, j]) | q[j]

        Rgate(rtheta_2[i, 0]) | (q[0])
        BSgate(phi_2[i, 0], 0) | (q[0], q[1])
        Rgate(rtheta_2[i, 2]) | (q[2])
        BSgate(phi_2[i, 2], theta_2[i, 3]) | (q[2], q[3])
        Rgate(rtheta_2[i, 1]) | (q[1])
        BSgate(phi_2[i, 1], 0) | (q[1], q[2])
        Rgate(rtheta_2[i, 0]) | (q[0])
        BSgate(phi_2[i, 0], 0) | (q[0], q[1])
        Rgate(rtheta_2[i, 0]) | (q[0])
        Rgate(rtheta_2[i, 1]) | (q[1])
        Rgate(rtheta_2[i, 2]) | (q[2])
        Rgate(rtheta_2[i, 3]) | (q[3])
        BSgate(phi_2[i, 2], 0) | (q[2], q[3])
        Rgate(rtheta_2[i, 2]) | (q[2])
        BSgate(phi_2[i, 1], 0) | (q[1], q[2])
        Rgate(rtheta_2[i, 1]) | (q[1])

        for j in range(size):
            Kgate(kappa[i, j]) | q[j]
Пример #19
0
    def test_is_channel_no_measurement(self):
        """test that the is_channel function returns True if no measurements are present"""
        eng, q = sf.Engine(2)

        assert utils.is_channel(eng)

        with eng:
            Sgate(0.4) | q[0]
            BSgate(0.4) | q

        assert utils.is_channel(eng)

        with eng:
            Sgate(0.4) | q[0]
            LossChannel(0.4) | q[0]
            BSgate(0.4) | q

        assert utils.is_channel(eng)
def circuit(A, n_qmodes, params):
    I = np.eye(2 * n_qmodes)

    X_top = np.hstack((np.zeros((n_qmodes, n_qmodes)), np.eye(n_qmodes)))
    X_bot = np.hstack((np.eye(n_qmodes), np.zeros((n_qmodes, n_qmodes))))

    X = np.vstack((X_top, X_bot))

    # c = 0
    # A = np.array([[c,-2,-10,1],
    #               [-2,c,1,5],
    #               [-10,1,c,-2],
    #               [1,5,-2,c]])
    zeros_4 = np.zeros((n_qmodes, n_qmodes))
    c_prim = 1
    A_prim = np.vstack((np.hstack((zeros_4, A)), np.hstack(
        (A, zeros_4)))) + np.eye(2 * n_qmodes) * c_prim

    d = 0.05
    # Cov = np.linalg.inv(I - X@(d*A)) - I/2
    Cov = np.linalg.inv(I - X @ (d * A_prim)) - I / 2

    eng, q = sf.Engine(n_qmodes)

    with eng:
        # beamsplitter array
        Gaussian(Cov) | q

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

        Dgate(params[4]) | q[0]
        Dgate(params[5]) | q[1]
        Dgate(params[6]) | q[2]
        Dgate(params[7]) | q[3]

        BSgate(params[8], params[9]) | (q[0], q[1])
        BSgate(params[10], params[11]) | (q[2], q[3])
        BSgate(params[12], params[13]) | (q[1], q[2])

    return eng, q
Пример #21
0
    def test_is_unitary_with_channel(self):
        """test that the is_unitary function returns False if channels are present"""
        eng, q = sf.Engine(2)

        with eng:
            Sgate(0.4) | q[0]
            LossChannel(0.4) | q[0]
            BSgate(0.4) | q

        assert not utils.is_unitary(eng)
Пример #22
0
def prog():
    """Program used for testing"""
    program = sf.Program(2)

    with program.context as q:
        Sgate(0.54, 0) | q[0]
        Sgate(0.54, 0) | q[1]
        BSgate(6.283, 0.6283) | (q[0], q[1])
        MeasureFock() | q

    return program
Пример #23
0
def circuit(X):
    phi = make_param('phi', constant=2.)

    eng, q = sf.Engine(2)

    with eng:
        Dgate(X[:, 0], 0.) | q[0]
        Dgate(X[:, 1], 0.) | q[1]
        BSgate(phi=phi) | (q[0], q[1])
        BSgate() | (q[0], q[1])

    num_inputs = X.get_shape().as_list()[0]
    state = eng.run('tf', cutoff_dim=10, eval=False, batch_size=num_inputs)

    p0 = state.fock_prob([0, 2])
    p1 = state.fock_prob([2, 0])
    normalisation = p0 + p1 + 1e-10
    circuit_output = p1 / normalisation

    return circuit_output
Пример #24
0
    def test_is_unitary_no_channel(self):
        """test that the is_unitary function returns True if no channels are present"""
        eng, q = sf.Engine(2)

        assert utils.is_unitary(eng)

        with eng:
            Sgate(0.4) | q[0]
            BSgate(0.4) | q

        assert utils.is_unitary(eng)
Пример #25
0
    def test_is_channel_measurement(self):
        """test that the is_channel function returns False if measurements
        or preparations are present"""
        eng, q = sf.Engine(2)

        with eng:
            Sgate(0.4) | q[0]
            BSgate() | q
            MeasureX | q[0]
            Sgate(0.4) | q[1]

        assert not utils.is_channel(eng)

        eng.reset()

        with eng:
            Sgate(0.4) | q[0]
            BSgate() | q
            Squeezed(0.4) | q[1]

        assert not utils.is_channel(eng)
Пример #26
0
    def test_beamsplitter(self, hbar):
        """Test beamsplitter produces correct cov and means"""

        H, t = beamsplitter(self.th, self.phi, hbar=hbar)
        resD, resV = self.H_circuit(H, t)

        gate = BSgate(self.th, self.phi)
        expD, expV = self.ref_circuit(gate)

        # test the covariance matrix
        assert np.allclose(resV, expV)
        # test the vector of means
        assert np.allclose(resD, expD)
Пример #27
0
    def test_beamsplitter(self):
        """Test beamsplitter produces correct cov and means"""
        self.eng.reset()

        H, t = beamsplitter(self.th, self.phi, hbar=self.hbar)
        resD, resV = self.H_circuit(H, t)

        gate = BSgate(self.th, self.phi)
        expD, expV = self.ref_circuit(gate)

        # test the covariance matrix
        self.assertTrue(np.allclose(resV, expV))
        # test the vector of means
        self.assertTrue(np.allclose(resD, expD))
Пример #28
0
def input_qnn_layer():
    with tf.name_scope('inputlayer'):
        Sgate(tf.clip_by_value(output_layer[:, 0], -sq_clip, sq_clip), output_layer[:, 1]) | q[0]
        Sgate(tf.clip_by_value(output_layer[:, 2], -sq_clip, sq_clip), output_layer[:, 3]) | q[1]

        BSgate(output_layer[:, 4], output_layer[:, 5]) | (q[0], q[1])

        Rgate(output_layer[:, 6]) | q[0]
        Rgate(output_layer[:, 7]) | q[1]

        Dgate(tf.clip_by_value(output_layer[:, 8], -disp_clip, disp_clip), output_layer[:, 9]) \
        | q[0]
        Dgate(tf.clip_by_value(output_layer[:, 10], -disp_clip, disp_clip), output_layer[:, 11]) \
        | q[0]

        Kgate(tf.clip_by_value(output_layer[:, 12], -kerr_clip, kerr_clip)) | q[0]
        Kgate(tf.clip_by_value(output_layer[:, 13], -kerr_clip, kerr_clip)) | q[0]
Пример #29
0
    def decompose(self, reg):
        # make BS gate
        theta = self.layer['BS'][0]
        phi = self.layer['BS'][1]
        BS = BSgate(theta, phi)

        # make cross-Kerr gate
        CK = None
        param = self.layer.get('CK', [0])[0]
        if param != 0:
            CK = CKgate(param)

        # make Kerr gate
        K = None
        param = self.layer.get('K', [0])[0]
        if param != 0:
            K = Kgate(param)

        # make rotation gate
        R = None
        param = self.layer.get('R', [0])[0]
        if param != 0:
            R = Rgate(param)

        cmds = []

        for i in range(self.num_layers):  #pylint: disable=unused-variable
            for q0, q1 in self.layer['BS'][2]:
                cmds.append(Command(BS, (reg[q0], reg[q1])))

            if CK is not None:
                for q0, q1 in self.layer['CK'][1]:
                    cmds.append(Command(CK, (reg[q0], reg[q1])))

            if K is not None:
                for mode in self.layer['K'][1]:
                    cmds.append(Command(K, reg[mode]))

            if R is not None:
                for mode in self.layer['R'][1]:
                    cmds.append(Command(R, reg[mode]))

        return cmds
Пример #30
0
def test_lossless_no_mixing_no_rotation_U(delays, modes):
    """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations"""
    delays = list(delays)
    angles = np.zeros([2 * len(delays), modes + sum(delays)])
    d = len(delays)
    n, N = get_mode_indices(delays)
    prog = sf.TDMProgram([N])

    with prog.context(*angles) as (p, q):
        for i in range(d):
            Rgate(p[i + d]) | q[n[i]]
            BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]])

    prog.space_unroll()

    compiled = prog.compile(compiler="passive")
    passive_elem = compiled.circuit[0]
    U = passive_elem.op.p[0]
    # Check that it is indeed the identity
    assert np.allclose(U, np.identity(len(U)))