Пример #1
0
    def test_interferometers(self, chip, tol):
        """Test that the compilation correctly decomposes the interferometer using
        the rectangular_symmetric mesh"""
        prog = sf.Program(12)
        U = random_interferometer(6)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[6])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[7])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[8])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[9])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[4], q[10])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[5], q[11])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5])
            ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11])
            ops.MeasureFock() | q

        res = prog.compile(chip.short_name)

        expected = sf.Program(12)

        with expected.context as q:
            for i, j in np.arange(12).reshape(2, -1).T:
                ops.S2gate(SQ_AMPLITUDE, 0) | (q[i], q[j])

            ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[0], q[1], q[2], q[3], q[4], q[5])
            ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[6], q[7], q[8], q[9], q[10], q[11])
            ops.MeasureFock() | q

        expected = expected.compile(DummyCircuit())

        assert program_equivalence(res, expected, atol=tol)
Пример #2
0
    def test_unitaries_do_not_match(self):
        """Test exception raised if the unitary applied to modes [0, 1, 2, 3] is
        different to the unitary applied to modes [4, 5, 6, 7]"""
        prog = sf.Program(8)
        U = random_interferometer(4)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[7])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3])
            ops.Interferometer(U) | (q[4], q[5], q[6], q[7])
            ops.BSgate() | (q[2], q[3])
            ops.MeasureFock() | q

        with pytest.raises(CircuitError, match="must be identical to interferometer"):
            res = prog.compile("X8_01")
Пример #3
0
    def test_s2gate_repeated_modes_half_squeezing(self, num_pairs):
        """Test that squeezing gates are correctly merged"""
        prog = sf.Program(2 * num_pairs)
        U = random_interferometer(num_pairs)

        with prog.context as q:

            ops.S2gate(SQ_AMPLITUDE / 2) | (q[0], q[0 + num_pairs])
            for i in range(1, num_pairs):
                ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs])
            ops.S2gate(SQ_AMPLITUDE / 2) | (q[0], q[0 + num_pairs])
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs))
            ops.Interferometer(U) | tuple(
                q[i] for i in range(num_pairs, 2 * num_pairs))
            ops.MeasureFock() | q

        res = prog.compile(compiler="Xunitary")
        assert np.allclose(res.circuit[0].op.p[0], SQ_AMPLITUDE)
    def test_wrong_squeezing_phase(self):
        """Test error is raised when the phase of S2gate is not zero"""
        prog = sf.Program(2)
        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE, 137) | (q[0], q[1])
            ops.MeasureFock() | q

        with pytest.raises(CircuitError, match="Incorrect phase value"):
            res = prog.compile("Xunitary")
    def test_operation_before_squeezing(self):
        """Test error is raised when an operation is passed before the S2gates"""
        prog = sf.Program(2)
        with prog.context as q:
            ops.BSgate() | (q[0], q[1])
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[1])
            ops.MeasureFock() | q

        with pytest.raises(CircuitError, match="There can be no operations before the S2gates."):
            prog.compile("Xunitary")
Пример #6
0
    def test_assert_max_number_of_measurements_wrong_entry(self):
        """Check that the correct error is raised when calling `prog.assert_number_of_measurements`
        with the incorrect type of device spec mode entry."""
        device_dict = {
            "target": "simulon_gaussian",
            "modes": 2,
            "layout": "",
            "gate_parameters": {},
            "compiler": ["gaussian"],
        }
        spec = sf.DeviceSpec(spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(KeyError, match="Expected keys for the maximum allowed number of PNR"):
            prog.assert_max_number_of_measurements(spec)
Пример #7
0
    def test_incorrect_s2gate_params(self):
        """Test exceptions raised if S2gates have illegal parameters"""
        prog = sf.Program(8)
        U = random_interferometer(4)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[4])
            ops.S2gate(0) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE + 0.1) | (q[3], q[7])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3])
            ops.Interferometer(U) | (q[4], q[5], q[6], q[7])
            ops.MeasureFock() | q

        with pytest.raises(
                CircuitError,
                match=
                r"Incorrect squeezing value\(s\) \(r, phi\)={\(1.1, 0.0\)}"):
            res = prog.compile("X8_01")
Пример #8
0
    def test_incorrect_modes(self):
        """Test that an exception is raised if the circuit spec
        is called with the incorrect number of modes"""
        class DummyCircuit(CircuitSpecs):
            """A circuit with 2 modes"""
            modes = 2
            remote = False
            local = True
            interactive = True
            primitives = {'S2gate', 'Interferometer'}
            decompositions = set()

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(program.CircuitError, match="requires 3 modes"):
            new_prog = prog.compile(target=DummyCircuit())
Пример #9
0
    def test_validate_gate_parameters_sf_program_not_compiled_no_device(self):
        """Test the ``.sf.program_utils.validate_gate_parameters`` function with a not compiled ``sf.Program``
        when no device is passed"""
        mock_prog = sf.Program(2)
        with mock_prog.context as q:
            ops.S2gate(1) | q

        with pytest.raises(ValueError,
                           match="device is required to validate the circuit"):
            validate_gate_parameters(mock_prog)
Пример #10
0
    def test_incorrect_modes(self):
        """Test that an exception is raised if the compiler
        is called with a device spec with an incorrect number of modes"""

        class DummyCircuit(Compiler):
            """A circuit with 2 modes"""
            interactive = True
            primitives = {'S2gate', 'Interferometer'}
            decompositions = set()

        device_dict = {"modes": 2, "layout": None, "gate_parameters": None, "compiler": [None]}
        spec = sf.api.DeviceSpec(target=None, connection=None, spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(program.CircuitError, match="program contains 3 modes, but the device 'None' only supports a 2-mode program"):
            new_prog = prog.compile(device=spec, compiler=DummyCircuit())
Пример #11
0
    def test_disconnected_circuit(self):
        """Test the detection of a disconnected circuit."""
        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | q[0:2]
            ops.Dgate(1.0)  | q[2]
            ops.MeasureFock() | q[0:2]
            ops.MeasureX | q[2]

        with pytest.warns(UserWarning, match='The circuit consists of 2 disconnected components.'):
            new_prog = prog.compile(target='fock')
 def test_not_all_modes_measured(self, num_pairs):
     """Test exceptions raised if not all modes are measured"""
     prog = sf.Program(2 * num_pairs)
     U = random_interferometer(num_pairs)
     with prog.context as q:
         for i in range(num_pairs):
             ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs])
         ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs))
         ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs))
         ops.MeasureFock() | (q[0], q[num_pairs])
     with pytest.raises(CircuitError, match="All modes must be measured"):
         prog.compile("Xunitary")
Пример #13
0
    def test_assert_max_number_of_measurements_wrong_entry(self):
        """Check that the correct error is raised when calling `prog.assert_number_of_measurements`
        with the incorrect type of device spec mode entry."""
        device_dict = {
            "modes": 2,
            "layout": None,
            "gate_parameters": None,
            "compiler": [None]
        }
        spec = sf.api.DeviceSpec(target="simulon_gaussian",
                                 connection=None,
                                 spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(KeyError,
                           match="Have you specified the correct target?"):
            prog.assert_max_number_of_measurements(spec)
Пример #14
0
    def test_s2gate_repeated_modes_wrong_phase(self, num_pairs):
        """Test that an error is raised if there is an attempt to merge squeezing gates
        with different phase values"""
        prog = sf.Program(2 * num_pairs)
        U = random_interferometer(num_pairs)

        with prog.context as q:

            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[0 + num_pairs])
            for i in range(1, num_pairs):
                ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs])
            ops.S2gate(SQ_AMPLITUDE / 2, 0.5) | (q[0], q[0 + num_pairs])
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs))
            ops.Interferometer(U) | tuple(
                q[i] for i in range(num_pairs, 2 * num_pairs))
            ops.MeasureFock() | q

        with pytest.raises(
                CircuitError,
                match="Cannot merge S2gates with different phase values"):
            prog.compile(compiler="Xunitary")
    def test_gates_compile(self):
        """Test that combinations of MZgates, Rgates, and BSgates
        correctly compile."""
        prog = sf.Program(8)

        def unitary(q):
            ops.MZgate(0.5, 0.1) | (q[0], q[1])
            ops.BSgate(0.1, 0.2) | (q[1], q[2])
            ops.Rgate(0.4) | q[0]

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[7])

            unitary(q[:4])
            unitary(q[4:])
            ops.MeasureFock() | q

        prog.compile("Xunitary")
Пример #16
0
    def test_no_s2gates(self, chip, tol):
        """Test identity S2gates are inserted when no S2gates
        are provided."""
        prog = sf.Program(12)
        U = random_interferometer(6)

        with prog.context as q:
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5])
            ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11])
            ops.MeasureFock() | q

        expected = sf.Program(12)

        with expected.context as q:
            ops.S2gate(0) | (q[0], q[6])
            ops.S2gate(0) | (q[1], q[7])
            ops.S2gate(0) | (q[2], q[8])
            ops.S2gate(0) | (q[3], q[9])
            ops.S2gate(0) | (q[4], q[10])
            ops.S2gate(0) | (q[5], q[11])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5])
            ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11])
            ops.MeasureFock() | q

        res = prog.compile(chip.short_name)
        expected = expected.compile(chip.short_name)
        assert program_equivalence(res, expected, atol=tol)
    def test_mach_zehnder_interferometers(self, tol):
        """Test Mach-Zehnder gates correctly compile"""
        prog = sf.Program(4)
        phi = 0.543
        theta = -1.654

        with prog.context as q:
            ops.S2gate(0.5) | (q[0], q[2])
            ops.S2gate(0.5) | (q[3], q[1])
            ops.MZgate(phi, theta) | (q[0], q[1])
            ops.MZgate(phi, theta) | (q[2], q[3])
            ops.MeasureFock() | q

        res = prog.compile("chip0")

        expected = sf.Program(4)

        with expected.context as q:
            ops.S2gate(0.5, 0) | (q[0], q[2])
            ops.S2gate(0.5, 0) | (q[1], q[3])

            # corresponds to MZgate(phi, theta) on modes [0, 1]
            ops.Rgate(np.mod(phi, 2 * np.pi)) | q[0]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(np.mod(theta, 2 * np.pi)) | q[0]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(0) | q[0]
            ops.Rgate(0) | q[1]

            # corresponds to MZgate(phi, theta) on modes [2, 3]
            ops.Rgate(np.mod(phi, 2 * np.pi)) | q[2]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[2], q[3])
            ops.Rgate(np.mod(theta, 2 * np.pi)) | q[2]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[2], q[3])
            ops.Rgate(0) | q[2]
            ops.Rgate(0) | q[3]

            ops.MeasureFock() | (q[0], q[3], q[1], q[2])

        assert program_equivalence(res, expected, atol=tol)
Пример #18
0
    def test_missing_s2gates(self, tol):
        """Test identity S2gates are inserted when some (but not all)
        S2gates are included."""
        prog = sf.Program(8)
        U = random_interferometer(4)

        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[5])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[7])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3])
            ops.Interferometer(U) | (q[4], q[5], q[6], q[7])
            ops.MeasureFock() | q

        expected = sf.Program(8)

        with expected.context as q:
            ops.S2gate(0) | (q[0], q[4])
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[5])
            ops.S2gate(0) | (q[2], q[6])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[7])
            ops.Interferometer(U) | (q[0], q[1], q[2], q[3])
            ops.Interferometer(U) | (q[4], q[5], q[6], q[7])
            ops.MeasureFock() | q

        res = prog.compile("X8_01")
        expected = expected.compile("X8_01")
        assert program_equivalence(res, expected, atol=tol)
    def test_missing_s2gates(self, num_pairs, tol):
        """Test identity S2gates are inserted when some (but not all)
        S2gates are included."""
        prog = sf.Program(2 * num_pairs)
        U = random_interferometer(num_pairs)
        assert num_pairs > 3
        with prog.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[num_pairs + 1])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[num_pairs + 3])
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs))
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs))
            ops.MeasureFock() | q

        expected = sf.Program(2 * num_pairs)

        with expected.context as q:
            ops.S2gate(SQ_AMPLITUDE) | (q[1], q[num_pairs + 1])
            ops.S2gate(0) | (q[0], q[num_pairs + 0])
            ops.S2gate(SQ_AMPLITUDE) | (q[3], q[num_pairs + 3])
            ops.S2gate(0) | (q[2], q[num_pairs + 2])
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs))
            ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs))
            ops.MeasureFock() | q

        res = prog.compile("Xunitary")
        expected = expected.compile("Xunitary")
        assert program_equivalence(res, expected, atol=tol)
Пример #20
0
    def test_assert_number_of_modes(self):
        """Check that the correct error is raised when calling `prog.assert_number_of_modes`
        with the incorrect number of modes."""
        device_dict = {
            "target": "abc",
            "modes": 2,
            "layout": "",
            "gate_parameters": {},
            "compiler": ["DummyCompiler"],
        }
        spec = sf.DeviceSpec(spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(
            program.CircuitError,
            match="program contains 3 modes, but the device 'abc' only supports a 2-mode program",
        ):
            prog.assert_number_of_modes(spec)
    def test_no_unitary(self, tol):
        """Test compilation works with no unitary provided"""
        prog = sf.Program(4)

        with prog.context as q:
            ops.S2gate(0.5) | (q[0], q[2])
            ops.S2gate(0.5) | (q[1], q[3])
            ops.MeasureFock() | q

        res = prog.compile("chip0")

        for cmd in res.circuit:
            print(cmd)

        expected = sf.Program(4)

        with expected.context as q:
            ops.S2gate(0.5, 0) | (q[0], q[2])
            ops.S2gate(0.5, 0) | (q[1], q[3])

            # corresponds to an identity on modes [0, 1]
            ops.Rgate(0) | q[0]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(np.pi) | q[0]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(np.pi) | q[0]
            ops.Rgate(0) | q[1]

            # corresponds to an identity on modes [2, 3]
            ops.Rgate(0) | q[2]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[2], q[3])
            ops.Rgate(np.pi) | q[2]
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[3], q[2])
            ops.Rgate(np.pi) | q[2]
            ops.Rgate(0) | q[3]

            ops.MeasureFock() | q

        assert program_equivalence(res, expected, atol=tol)
Пример #22
0
    def test_no_default_compiler(self):
        """Test that an exception is raised if the DeviceSpec has no compilers
        specified (and thus no default compiler)"""

        device_dict = {
            "modes": 3,
            "layout": None,
            "gate_parameters": None,
            "compiler": [None]
        }
        spec = sf.api.DeviceSpec(target="dummy_target",
                                 connection=None,
                                 spec=device_dict)

        prog = sf.Program(3)
        with prog.context as q:
            ops.S2gate(0.6) | [q[0], q[1]]
            ops.S2gate(0.6) | [q[1], q[2]]

        with pytest.raises(program.CircuitError,
                           match="does not specify a compiler."):
            new_prog = prog.compile(device=spec)
    def test_50_50_BSgate(self, tol):
        """Test 50-50 BSgates correctly compile"""
        prog = sf.Program(4)

        with prog.context as q:
            ops.S2gate(0.5) | (q[0], q[2])
            ops.S2gate(0.5) | (q[1], q[3])
            ops.BSgate() | (q[0], q[1])
            ops.BSgate() | (q[2], q[3])
            ops.MeasureFock() | q

        res = prog.compile("chip0")

        expected = sf.Program(4)

        with expected.context as q:
            ops.S2gate(0.5, 0) | (q[0], q[2])
            ops.S2gate(0.5, 0) | (q[1], q[3])

            # corresponds to BSgate() on modes [0, 1]
            ops.Rgate(0) | (q[0])
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(3 * np.pi / 2) | (q[0])
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[0], q[1])
            ops.Rgate(3 * np.pi / 4) | (q[0])
            ops.Rgate(-np.pi / 4) | (q[1])

            # corresponds to BSgate() on modes [2, 3]
            ops.Rgate(0) | (q[2])
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[2], q[3])
            ops.Rgate(3 * np.pi / 2) | (q[2])
            ops.BSgate(np.pi / 4, np.pi / 2) | (q[2], q[3])
            ops.Rgate(3 * np.pi / 4) | (q[2])
            ops.Rgate(-np.pi / 4) | (q[3])

            ops.MeasureFock() | q

        assert program_equivalence(res, expected, atol=tol)
Пример #24
0
    def test_unitary_too_large(self, chip):
        """Test exception raised if the unitary is applied to more
        than just modes [0, 1, 2, 3] and [4, 5, 6, 7]."""
        prog = sf.Program(12)
        U = random_interferometer(12)

        with prog.context as q:
            for i, j in np.arange(12).reshape(2, -1).T:
                ops.S2gate(SQ_AMPLITUDE, 0) | (q[i], q[j])
            ops.Interferometer(U) | q
            ops.MeasureFock() | q

        with pytest.raises(CircuitError, match="must be applied separately"):
            res = prog.compile(chip.short_name)
    def test_heterodyne(self, setup_eng, tol):
        """Test that heterodyne detection on a TMS state
        returns post-selected value."""
        alpha = 0.43 - 0.12j
        r = 5

        eng, prog = setup_eng(2)

        with prog.context as q:
            ops.S2gate(r) | q
            ops.MeasureHeterodyne(select=alpha) | q[0]

        eng.run(prog)
        assert np.allclose(q[0].val, alpha, atol=tol, rtol=0)
    def test_homodyne(self, setup_eng, tol):
        """Test that homodyne detection on a TMS state
        returns post-selected value."""
        x = 0.2
        r = 5

        eng, prog = setup_eng(2)

        with prog.context as q:
            ops.S2gate(r) | q
            ops.MeasureHomodyne(0, select=x) | q[0]

        eng.run(prog)
        assert np.allclose(q[0].val, x, atol=tol, rtol=0)
    def test_two_mode_gates_from_operators(self, drawer):
        prog = sf.Program(3)

        with prog.context as q:
            ops.CXgate(1) | (q[0], q[1])
            ops.CZgate(1) | (q[0], q[1])
            ops.BSgate(1) | (q[0], q[1])
            ops.S2gate(1) | (q[0], q[1])
            ops.CKgate(1) | (q[0], q[1])

        for op in prog.circuit:
            method, mode = drawer._gate_from_operator(op)
            assert callable(method) and hasattr(drawer, method.__name__)
            assert mode == 2
    def test_two_mode_gates_from_operators(self, drawer):
        eng, q = sf.Engine(3)

        with eng:
            ops.CXgate(1) | (q[0], q[1])
            ops.CZgate(1) | (q[0], q[1])
            ops.BSgate(1) | (q[0], q[1])
            ops.S2gate(1) | (q[0], q[1])
            ops.CKgate(1) | (q[0], q[1])

        for op in eng.cmd_queue:
            method, mode = drawer._gate_from_operator(op)
            assert callable(method) and hasattr(drawer, method.__name__)
            assert mode == 2
    def test_unitary_too_large(self, num_pairs):
        """Test exception raised if the unitary is applied to more
        than just modes [0, 1, 2, 3, ..., num_pairs-1] and [num_pairs, num_pairs+1, ..., 2*num_pairs-1]."""
        prog = sf.Program(2 * num_pairs)
        U = random_interferometer(2 * num_pairs)

        with prog.context as q:
            for i in range(0, num_pairs):
                ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs])
            ops.Interferometer(U) | q
            ops.MeasureFock() | q

        with pytest.raises(CircuitError, match="The applied unitary cannot mix between the modes"):
            res = prog.compile("Xunitary")
    def test_incorrect_s2gates(self):
        """Test exceptions raised if S2gates do not appear on correct modes"""
        prog = sf.Program(4)
        U = random_interferometer(2)

        with prog.context as q:
            ops.S2gate(0.5) | (q[0], q[2])
            ops.Interferometer(U) | (q[0], q[1])
            ops.Interferometer(U) | (q[2], q[3])
            ops.MeasureFock() | q

        with pytest.raises(CircuitError,
                           match="S2gates do not appear on the correct modes"):
            res = prog.compile("chip0")