def test_measure_arg_postselect(self): """Test measurement with argument and postselection converts""" # create a test program prog = Program(1) with prog.context as q: ops.MeasureHomodyne(0.43, select=0.543) | q[0] bb = io.to_blackbird(prog) expected = { "op": "MeasureHomodyne", "modes": [0], "args": [], "kwargs": { "phi": 0.43, "select": 0.543 }, } assert bb.operations[0] == expected # repeat with kwargs only prog = Program(1) with prog.context as q: ops.MeasureHomodyne(phi=0.43, select=0.543) | q[0] bb = io.to_blackbird(prog) assert bb.operations[0] == expected
def test_two_dimensional_cluster_denmark(): """ Two-dimensional temporal-mode cluster state as demonstrated in https://arxiv.org/pdf/1906.08709 """ np.random.seed(42) sq_r = 3 delay1 = 1 # number of timebins in the short delay line delay2 = 12 # number of timebins in the long delay line n = 200 # number of timebins shots = 10 # first half of cluster state measured in X, second half in P theta_A = [0] * int(n / 2) + [np.pi / 2] * int( n / 2) # measurement angles for detector A theta_B = theta_A # measurement angles for detector B # 2D cluster prog = tdmprogram.TDMProgram([1, delay2 + delay1 + 1]) with prog.context(theta_A, theta_B, shift="default") as (p, q): ops.Sgate(sq_r, 0) | q[0] ops.Sgate(sq_r, 0) | q[delay2 + delay1 + 1] ops.Rgate(np.pi / 2) | q[delay2 + delay1 + 1] ops.BSgate(np.pi / 4, np.pi) | (q[delay2 + delay1 + 1], q[0]) ops.BSgate(np.pi / 4, np.pi) | (q[delay2 + delay1], q[0]) ops.BSgate(np.pi / 4, np.pi) | (q[delay1], q[0]) ops.MeasureHomodyne(p[1]) | q[0] ops.MeasureHomodyne(p[0]) | q[delay1] eng = sf.Engine("gaussian") result = eng.run(prog, shots=shots) reshaped_samples = result.samples for sh in range(shots): X_A = reshaped_samples[sh][0][:n // 2] # X samples from detector A P_A = reshaped_samples[sh][0][n // 2:] # P samples from detector A X_B = reshaped_samples[sh][1][:n // 2] # X samples from detector B P_B = reshaped_samples[sh][1][n // 2:] # P samples from detector B # nullifiers defined in https://arxiv.org/pdf/1906.08709.pdf, Eqs. (1) and (2) N = delay2 ntot = len(X_A) - delay2 - 1 nX = np.array([ X_A[k] + X_B[k] - X_A[k + 1] - X_B[k + 1] - X_A[k + N] + X_B[k + N] - X_A[k + N + 1] + X_B[k + N + 1] for k in range(ntot) ]) nP = np.array([ P_A[k] + P_B[k] + P_A[k + 1] + P_B[k + 1] - P_A[k + N] + P_B[k + N] + P_A[k + N + 1] - P_B[k + N + 1] for k in range(ntot) ]) nXvar = np.var(nX) nPvar = np.var(nP) assert np.allclose(nXvar, 4 * sf.hbar * np.exp(-2 * sq_r), rtol=5 / np.sqrt(ntot)) assert np.allclose(nPvar, 4 * sf.hbar * np.exp(-2 * sq_r), rtol=5 / np.sqrt(ntot))
def test_one_dimensional_cluster_tokyo(): """ One-dimensional temporal-mode cluster state as demonstrated in https://aip.scitation.org/doi/pdf/10.1063/1.4962732 """ np.random.seed(42) sq_r = 5 n = 10 # for an n-mode cluster state shots = 3 # first half of cluster state measured in X, second half in P theta1 = [0] * int(n / 2) + [np.pi / 2] * int( n / 2) # measurement angles for detector A theta2 = theta1 # measurement angles for detector B prog = tdmprogram.TDMProgram(N=[1, 2]) with prog.context(theta1, theta2, shift="default") as (p, q): ops.Sgate(sq_r, 0) | q[0] ops.Sgate(sq_r, 0) | q[2] ops.Rgate(np.pi / 2) | q[0] ops.BSgate(np.pi / 4) | (q[0], q[2]) ops.BSgate(np.pi / 4) | (q[0], q[1]) ops.MeasureHomodyne(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[1] eng = sf.Engine("gaussian") result = eng.run(prog, shots=shots) reshaped_samples = result.samples for sh in range(shots): X_A = reshaped_samples[sh][0][:n // 2] # X samples from detector A P_A = reshaped_samples[sh][0][n // 2:] # P samples from detector A X_B = reshaped_samples[sh][1][:n // 2] # X samples from detector B P_B = reshaped_samples[sh][1][n // 2:] # P samples from detector B # nullifiers defined in https://aip.scitation.org/doi/pdf/10.1063/1.4962732, Eqs. (1a) and (1b) ntot = len(X_A) - 1 nX = np.array( [X_A[i] + X_B[i] + X_A[i + 1] - X_B[i + 1] for i in range(ntot)]) nP = np.array( [P_A[i] + P_B[i] - P_A[i + 1] + P_B[i + 1] for i in range(ntot)]) nXvar = np.var(nX) nPvar = np.var(nP) assert np.allclose(nXvar, 2 * sf.hbar * np.exp(-2 * sq_r), rtol=5 / np.sqrt(n)) assert np.allclose(nPvar, 2 * sf.hbar * np.exp(-2 * sq_r), rtol=5 / np.sqrt(n))
def test_teleportation_fidelity(self, setup_eng): """Test teleportation algorithm gives correct fid when using operations""" eng, prog = setup_eng(3) with prog.context as q: prepare_state(0.5 + 0.2j) | q[0] entangle_states() | (q[1], q[2]) ops.BSgate(np.pi / 4, 0) | (q[0], q[1]) ops.MeasureHomodyne(0, select=0) | q[0] ops.MeasureHomodyne(np.pi / 2, select=0) | q[1] state = eng.run(prog).state fidelity = state.fidelity_coherent([0, 0, 0.5 + 0.2j]) assert np.allclose(fidelity, 1, atol=0.1, rtol=0)
def test_delays_tdmprogram_with_several_spatial_modes(self): """Test that error is raised when calculating the delays of a TDM program with more than one spatial mode""" prog = TDMProgram(N=[1, 2]) with prog.context([0, np.pi / 2], [0, np.pi / 2]) as (p, q): ops.Sgate(4) | q[0] ops.Sgate(4) | q[2] ops.MeasureHomodyne(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[1] with pytest.raises( NotImplementedError, match="Calculating delays for programs with more than one spatial mode is not implemented.", ): prog.get_delays()
def test_cropping_tdmprogram_with_several_spatial_modes(self): """Test that error is raised when cropping samples from a TDM program with more than one spatial mode""" prog = TDMProgram(N=[1, 2]) with prog.context([0, np.pi / 2], [0, np.pi / 2]) as (p, q): ops.Sgate(4) | q[0] ops.Sgate(4) | q[2] ops.MeasureHomodyne(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[1] with pytest.raises( NotImplementedError, match="Cropping vacuum modes for programs with more than one spatial mode is not implemented.", ): prog.get_crop_value()
def test_unroll_shots(self): """Test unrolling program several times using different number of shots.""" n = 2 shots = 2 prog = sf.TDMProgram(N=2) with prog.context([0] * n, [0] * n, [0] * n) as (p, q): ops.Sgate(0.5643, 0) | q[1] ops.BSgate(p[0]) | (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] prog_length = len(prog.circuit) assert prog_length == 4 prog.unroll(shots=shots) assert len(prog.circuit) == n * shots * prog_length # unroll once more with the same number of shots to cover caching prog.unroll(shots=shots) assert len(prog.circuit) == n * shots * prog_length # unroll once more with a different number of shots shots = 3 prog.unroll(shots=shots) assert len(prog.circuit) == n * shots * prog_length prog.roll() assert len(prog.circuit) == prog_length
def test_homodyne_measurement_vacuum_phi(self, setup_eng, tol): """Homodyne measurements leave the mode in the vacuum state""" eng, prog = setup_eng(2) with prog.context as q: ops.Coherent(a, b) | q[0] ops.MeasureHomodyne(c) | q[0] eng.run(prog) assert np.all(eng.backend.is_vacuum(tol))
def test_teleportation_fidelity(self, setup_eng, pure): """Test teleportation algorithm gives correct fid when using operations""" eng, q = setup_eng(3) tol = 0.1 cutoff = 10 eng.reset(pure=pure, cutoff_dim=cutoff) # overwrite default cutoff with eng: prepare_state(0.5 + 0.2j) | q[0] entangle_states() | (q[1], q[2]) ops.BSgate(np.pi / 4, 0) | (q[0], q[1]) ops.MeasureHomodyne(0, select=0) | q[0] ops.MeasureHomodyne(np.pi / 2, select=0) | q[1] state = eng.run() fidelity = state.fidelity_coherent([0, 0, 0.5 + 0.2j]) assert np.allclose(fidelity, 1, atol=0.1, rtol=0)
def test_shots_default(self): """Test that default shots (1) is used""" prog = sf.TDMProgram(2) eng = sf.Engine("gaussian") with prog.context([1, 2], [3, 4]) as (p, q): ops.Sgate(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[0] results = eng.run(prog) assert results.samples.shape[0] == 1
def test_measure_arg(self): """Test measurement with argument converts""" # create a test program sf_prog = Program(1) with sf_prog.context as q: ops.MeasureHomodyne(0.43) | q[0] xir_prog = io.to_xir(sf_prog) expected = [("MeasureHomodyne", {"phi": 0.43}, (0,))] assert [(stmt.name, stmt.params, stmt.wires) for stmt in xir_prog.statements] == expected
def test_shots_run_options(self): """Test that run_options takes precedence over default""" prog = sf.TDMProgram(2) eng = sf.Engine("gaussian") with prog.context([1, 2], [3, 4]) as (p, q): ops.Sgate(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[0] prog.run_options = {"shots": 5} results = eng.run(prog) assert results.samples.shape[0] == 5
def test_generate_code_tdm(self): """Test generating code for a TDM program with an engine""" prog = sf.TDMProgram(N=[2, 3]) eng = sf.Engine("gaussian") with prog.context([np.pi, 3 * np.pi / 2, 0], [1, 0.5, np.pi], [0, 0, 0]) as (p, q): ops.Sgate(0.123, np.pi / 4) | q[2] ops.BSgate(p[0]) | (q[1], q[2]) ops.Rgate(p[1]) | q[2] ops.MeasureHomodyne(p[0]) | q[0] ops.MeasureHomodyne(p[2]) | q[2] results = eng.run(prog) code = io.generate_code(prog, eng) code_list = code.split("\n") expected = prog_txt_tdm.split("\n") for i, row in enumerate(code_list): assert row == expected[i]
def test_measure_arg_postselect(self): """Test measurement with argument and postselection converts""" # create a test program sf_prog = Program(1) with sf_prog.context as q: ops.MeasureHomodyne(0.43, select=0.543) | q[0] xir_prog = io.to_xir(sf_prog) expected = [("MeasureHomodyne", {"phi": 0.43, "select": 0.543}, (0,))] assert [(stmt.name, stmt.params, stmt.wires) for stmt in xir_prog.statements] == expected # repeat with kwargs only sf_prog = Program(1) with sf_prog.context as q: ops.MeasureHomodyne(phi=0.43, select=0.543) | q[0] xir_prog = io.to_xir(sf_prog) assert [(stmt.name, stmt.params, stmt.wires) for stmt in xir_prog.statements] == expected
def test_single_parameter_list_program(): """Test that a TDMProgram with a single parameter list works.""" prog = sf.TDMProgram(2) eng = sf.Engine("gaussian") with prog.context([1, 2]) as (p, q): ops.Sgate(p[0]) | q[0] ops.MeasureHomodyne(p[0]) | q[0] eng.run(prog) assert isinstance(prog.loop_vars, Iterable) assert prog.parameters == {'p0': [1, 2]}
def compile_test_program(device, args=(-1, 1, 2, 3)): """Compiles a test program with the given gate arguments.""" alpha = [args[1]] beta = [args[2]] gamma = [args[3]] prog = tdmprogram.TDMProgram(N=2) with prog.context(alpha, beta, gamma) as (p, q): ops.Sgate(args[0]) | q[ 1] # Note that the Sgate has a second parameter that is non-zero ops.Rgate(p[0]) | q[0] ops.BSgate(p[1]) | (q[0], q[1]) ops.MeasureHomodyne(p[2]) | q[0] prog.compile(device=device, compiler=device.compiler)
def test_shots_passed(self): """Test that shots supplied via eng.run takes precedence over run_options and that run_options isn't changed""" prog = sf.TDMProgram(2) eng = sf.Engine("gaussian") with prog.context([1, 2], [3, 4]) as (p, q): ops.Sgate(p[0]) | q[0] ops.MeasureHomodyne(p[1]) | q[0] prog.run_options = {"shots": 5} results = eng.run(prog, shots=2) assert results.samples.shape[0] == 2 assert prog.run_options["shots"] == 5
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_tdm_program(self): prog = TDMProgram(2) with prog.context([1, 2], [3, 4], [5, 6]) as (p, q): ops.Sgate(0.7, 0) | q[1] ops.BSgate(p[0]) | (q[0], q[1]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] bb = io.to_blackbird(prog) assert bb.operations[0] == { 'kwargs': {}, 'args': [0.7, 0], 'op': 'Sgate', 'modes': [1] } assert bb.operations[1] == { 'kwargs': {}, 'args': ['p0', 0.0], 'op': 'BSgate', 'modes': [0, 1] } assert bb.operations[2] == { 'kwargs': {}, 'args': ['p1'], 'op': 'Rgate', 'modes': [1] } assert bb.operations[3] == { 'kwargs': { 'phi': 'p2' }, 'args': [], 'op': 'MeasureHomodyne', 'modes': [0] } assert bb.programtype == { 'name': 'tdm', 'options': { 'temporal_modes': 2 } } assert list(bb._var.keys()) == ["p0", "p1", "p2"] assert np.all(bb._var["p0"] == np.array([[1, 2]])) assert np.all(bb._var["p1"] == np.array([[3, 4]])) assert np.all(bb._var["p2"] == np.array([[5, 6]])) assert bb.modes == [0, 1]
def prog(): prog = Program(4, name="test_program") with prog.context as q: # state preparation ops.Vac | q[1] ops.Squeezed(0.12) | q[2] # one mode gates ops.Sgate(1) | q[0] ops.Dgate(np.abs(0.54 + 0.5j), np.angle(0.54 + 0.5j)) | q[1] # two mode gates ops.S2gate(0.543, -0.12) | (q[0], q[3]) # decomposition ops.Interferometer(U) | q # measurement ops.MeasureX | q[0] ops.MeasureHomodyne(0.43, select=0.32) | q[2] ops.MeasureHomodyne(phi=0.43, select=0.32) | q[2] return prog
def test_passing_list_of_tdmprograms(self): """Test that error is raised when passing a list containing TDM programs""" prog = tdmprogram.TDMProgram(N=2) with prog.context([1, 1], [1, 1], [1, 1]) as (p, q): ops.Sgate(0, 0) | q[1] ops.BSgate(p[0]) | (q[0], q[1]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] eng = sf.Engine("gaussian") with pytest.raises( NotImplementedError, match="Lists of TDM programs are not currently supported"): eng.run([prog, prog])
def test_tdm_program(self): """Test TDM program converts properly""" prog = TDMProgram(2) with prog.context([1, 2], [3, 4], [5, 6]) as (p, q): ops.Sgate(0.7, 0) | q[1] ops.BSgate(p[0]) | (q[0], q[1]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] bb = io.to_blackbird(prog) assert bb.operations[0] == { "kwargs": {}, "args": [0.7, 0], "op": "Sgate", "modes": [1] } assert bb.operations[1] == { "kwargs": {}, "args": ["p0", 0.0], "op": "BSgate", "modes": [0, 1], } assert bb.operations[2] == { "kwargs": {}, "args": ["p1"], "op": "Rgate", "modes": [1] } assert bb.operations[3] == { "kwargs": {}, "args": ["p2"], "op": "MeasureHomodyne", "modes": [0], } assert bb.programtype == { "name": "tdm", "options": { "temporal_modes": 2 } } assert list(bb._var.keys()) == ["p0", "p1", "p2"] assert np.all(bb._var["p0"] == np.array([[1, 2]])) assert np.all(bb._var["p1"] == np.array([[3, 4]])) assert np.all(bb._var["p2"] == np.array([[5, 6]])) assert bb.modes == {0, 1}
def test_measure_arg(self): """Test measurement with argument converts""" # create a test program prog = Program(1) with prog.context as q: ops.MeasureHomodyne(0.43) | q[0] bb = io.to_blackbird(prog) expected = { "op": "MeasureHomodyne", "modes": [0], "args": [0.43], "kwargs": {}, } assert bb.operations[0] == expected
def test_tdm_wrong_modes(self): """Test the correct error is raised when the tdm circuit registers don't match the device spec""" sq_r = 0.5643 c = 2 alpha = [np.pi / 4, 0] * c phi = [0, np.pi / 2] * c theta = [0, 0] + [np.pi / 2, np.pi / 2] prog = tdmprogram.TDMProgram(N=2) with prog.context(alpha, phi, theta) as (p, q): ops.Sgate(sq_r) | q[1] ops.BSgate(p[0]) | (q[0], q[1]) # The order should be (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] eng = sf.Engine("gaussian") with pytest.raises(sf.program_utils.CircuitError, match="due to incompatible mode ordering."): prog.compile(device=device, compiler="TD2")
def test_shots_with_timebins_non_multiple_of_concurrent_modes(self): """Test that multiple shots work when having the number of timebins be a non-multiple of the number of concurrent modes""" theta = [0] * 3 shots = 2 prog = sf.TDMProgram(N=2) with prog.context(theta) as (p, q): ops.Xgate(50) | q[1] ops.MeasureHomodyne(p[0]) | q[0] eng = sf.Engine("gaussian") res = eng.run(prog, shots=shots) samples = res.samples expected = np.array([[[0, 50, 50]], [[50, 50, 50]]]) assert np.allclose(samples, expected, atol=4)
def test_tdm_wrong_parameter_second_argument(self): """Test the correct error is raised when the tdm circuit explicit parameters are not within the allowed ranges""" sq_r = 0.5643 c = 2 alpha = [np.pi / 4, 0] * c phi = [0, np.pi / 2] * c theta = [0, 0] + [np.pi / 2, np.pi / 2] prog = tdmprogram.TDMProgram(N=2) with prog.context(alpha, phi, theta) as (p, q): ops.Sgate(sq_r, 0.4) | q[ 1] # Note that the Sgate has a second parameter that is non-zero ops.BSgate(p[0]) | (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] eng = sf.Engine("gaussian") with pytest.raises(sf.program_utils.CircuitError, match="due to incompatible parameter."): prog.compile(device=device, compiler="TD2")
def singleloop_program(r, alpha, phi, theta): """Single delay loop with program. Args: r (float): squeezing parameter alpha (Sequence[float]): beamsplitter angles phi (Sequence[float]): rotation angles theta (Sequence[float]): homodyne measurement angles Returns: (array): homodyne samples from the single loop simulation """ prog = tdmprogram.TDMProgram(N=2) with prog.context(alpha, phi, theta) as (p, q): ops.Sgate(r, 0) | q[1] ops.BSgate(p[0]) | (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] return prog
def test_unroll(self): """Test unrolling program.""" n = 2 prog = sf.TDMProgram(N=2) with prog.context([0] * n, [0] * n, [0] * n) as (p, q): ops.Sgate(0.5643, 0) | q[1] ops.BSgate(p[0]) | (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] prog_length = len(prog.circuit) assert prog_length == 4 prog.unroll() assert len(prog.circuit) == n * prog_length prog.roll() assert len(prog.circuit) == prog_length
def test_tdm_wrong_modes(self): """Test the correct error is raised when the tdm circuit registers don't match the device spec""" sq_r = 0.5643 c = 2 alpha = [np.pi / 4, 0] * c phi = [0, np.pi / 2] * c theta = [0.0, 0.0] + [np.pi / 2, np.pi / 2] prog = TDMProgram(N=2) with prog.context(alpha, phi, theta) as (p, q): ops.Sgate(sq_r) | q[1] ops.BSgate(p[0]) | (q[0], q[1]) # The order should be (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] reset_compiler(self.target) with pytest.raises(CircuitError, match=f"cannot be used with the compiler 'TDM"): prog.compile(device=self.device, compiler="TDM")
def test_tdm_wrong_layout(self): """Test the correct error is raised when the tdm circuit gates don't match the device spec""" sq_r = 0.5643 c = 2 alpha = [np.pi / 4, 0] * c phi = [0, np.pi / 2] * c theta = [0, 0] + [np.pi / 2, np.pi / 2] prog = tdmprogram.TDMProgram(N=2) with prog.context(alpha, phi, theta) as (p, q): ops.Dgate(sq_r) | q[1] # Here one should have an Sgate ops.BSgate(p[0]) | (q[1], q[0]) ops.Rgate(p[1]) | q[1] ops.MeasureHomodyne(p[2]) | q[0] eng = sf.Engine("gaussian") with pytest.raises( sf.program_utils.CircuitError, match="The gates or the order of gates used in the Program", ): prog.compile(device=device, compiler="TD2")