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)
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")
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")
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)
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")
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())
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)
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())
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")
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)
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")
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)
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)
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)
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)
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")