def test_interferometers(self, num_pairs, tol): """Test that the compilation correctly decomposes the interferometer using the rectangular_symmetric mesh""" prog = sf.Program(2 * num_pairs) U = random_interferometer(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) | 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="Xcov") expected = sf.Program(2 * num_pairs) with expected.context as q: for i in range(0, num_pairs): ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs]) ops.Interferometer( U, mesh="rectangular_symmetric", drop_identity=False) | tuple( q[i] for i in range(num_pairs)) ops.Interferometer( U, mesh="rectangular_symmetric", drop_identity=False) | tuple( q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q expected = expected.compile(compiler=DummyCircuit()) # Note that since DummyCircuit() has a hard coded limit of 8 modes we only check for this number assert program_equivalence(res, expected, atol=tol, compare_params=False)
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(compiler="Xcov") expected = expected.compile(compiler="Xcov") assert program_equivalence(res, expected, atol=tol)
def test_no_s2gates(self, num_pairs, tol): """Test identity S2gates are inserted when no S2gates are provided.""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) with prog.context as q: 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: for i in range(num_pairs): ops.S2gate(0) | (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 res = prog.compile(compiler="Xcov") expected = expected.compile(compiler="Xcov") assert program_equivalence(res, expected, atol=tol)
def test_no_s2gates(self, num_pairs, tol): """Test identity S2gates are inserted when no S2gates are provided.""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) with prog.context as q: 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: for i in range(num_pairs): ops.S2gate(0) | (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 # with pytest.raises(CircuitError, match="There can be no operations before the S2gates."): res = prog.compile(compiler="Xunitary") # with pytest.raises(CircuitError, match="There can be no operations before the S2gates."): expected = expected.compile(compiler="Xunitary") assert program_equivalence(res, expected, atol=tol)
def test_exact_template(self, tol): """Test compilation works for the exact circuit""" bb = blackbird.loads(X8_CIRCUIT) bb = bb( squeezing_amplitude_0=SQ_AMPLITUDE, squeezing_amplitude_1=SQ_AMPLITUDE, squeezing_amplitude_2=SQ_AMPLITUDE, squeezing_amplitude_3=SQ_AMPLITUDE, phase_0=0, phase_1=1, phase_2=2, phase_3=3, phase_4=4, phase_5=5, phase_6=6, phase_7=7, phase_8=8, phase_9=9, phase_10=10, phase_11=11, final_phase_0=1.24, final_phase_1=-0.54, final_phase_2=4.12, final_phase_3=0, final_phase_4=1.24, final_phase_5=-0.54, final_phase_6=4.12, final_phase_7=0, ) expected = to_program(bb) res = expected.compile(compiler="Xcov") assert program_equivalence(res, expected, atol=tol, compare_params=False)
def test_no_unitary(self, tol): """Test compilation works with no unitary provided""" prog = sf.Program(8) 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]) ops.MeasureFock() | q res = prog.compile(compiler="Xcov") expected = sf.Program(8) with expected.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]) # corresponds to an identity on modes [0, 1, 2, 3] # This can be easily seen from below by noting that: # MZ(pi, pi) = R(0) = I # MZ(pi, 0) @ MZ(pi, 0) = I # [R(pi) \otimes I] @ MZ(pi, 0) = I ops.MZgate(np.pi, 0) | (q[0], q[1]) ops.MZgate(np.pi, 0) | (q[2], q[3]) ops.MZgate(np.pi, np.pi) | (q[1], q[2]) ops.MZgate(np.pi, np.pi) | (q[0], q[1]) ops.MZgate(np.pi, 0) | (q[2], q[3]) ops.MZgate(np.pi, np.pi) | (q[1], q[2]) ops.Rgate(np.pi) | (q[0]) ops.Rgate(0) | (q[1]) ops.Rgate(0) | (q[2]) ops.Rgate(0) | (q[3]) # corresponds to an identity on modes [4, 5, 6, 7] ops.MZgate(np.pi, 0) | (q[4], q[5]) ops.MZgate(np.pi, 0) | (q[6], q[7]) ops.MZgate(np.pi, np.pi) | (q[5], q[6]) ops.MZgate(np.pi, np.pi) | (q[4], q[5]) ops.MZgate(np.pi, 0) | (q[6], q[7]) ops.MZgate(np.pi, np.pi) | (q[5], q[6]) ops.Rgate(np.pi) | (q[4]) ops.Rgate(0) | (q[5]) ops.Rgate(0) | (q[6]) ops.Rgate(0) | (q[7]) ops.MeasureFock() | q assert program_equivalence(res, expected, atol=tol, compare_params=False) # double check that the applied symplectic is correct # remove the Fock measurements res.circuit = res.circuit[:-1] # extract the Gaussian symplectic matrix O = res.compile(compiler="gaussian_unitary").circuit[0].op.p[0] # construct the expected symplectic matrix corresponding # to just the initial two mode squeeze gates S = two_mode_squeezing(SQ_AMPLITUDE, 0) num_modes = 8 expected = np.identity(2 * num_modes) for i in range(num_modes // 2): expected = expand(S, [i, i + num_modes // 2], num_modes) @ expected # Note that the comparison has to be made at the level of covariance matrices # Not at the level of symplectic matrices assert np.allclose(O @ O.T, expected @ expected.T, atol=tol)
def test_exact_template(self, tol): """Test compilation works for the exact circuit""" params = generate_X8_params(1, 0.3) prog = X8_device.create_program(**params) prog2 = prog.compile(device=X8_device, compiler="Xstrict") assert program_equivalence(prog, prog2, atol=tol, rtol=0)