def runJob(self, eng): num_subsystem = 8 prog = sf.Program(num_subsystem, name="remote_job") U = random_interferometer(4) with prog.context as q: # Initial squeezed states # Allowed values are r=1.0 or r=0.0 ops.S2gate(1.0) | (q[0], q[4]) ops.S2gate(1.0) | (q[1], q[5]) ops.S2gate(1.0) | (q[3], q[7]) # Interferometer on the signal modes (0-3) ops.Interferometer(U) | (q[0], q[1], q[2], q[3]) ops.BSgate(0.543, 0.123) | (q[2], q[0]) ops.Rgate(0.453) | q[1] ops.MZgate(0.65, -0.54) | (q[2], q[3]) # *Same* interferometer on the idler modes (4-7) ops.Interferometer(U) | (q[4], q[5], q[6], q[7]) ops.BSgate(0.543, 0.123) | (q[6], q[4]) ops.Rgate(0.453) | q[5] ops.MZgate(0.65, -0.54) | (q[6], q[7]) ops.MeasureFock() | q eng = eng results =eng.run(prog, shots=10) # state = results.state # measurements = results.samples return results.samples
def test_run_optimizations(self): """Test that circuit is optimized when optimize is True""" class DummyCompiler(Compiler): """A circuit with 2 modes""" interactive = True primitives = {"Rgate"} decompositions = set() device_dict = { "target": "dummy_target", "modes": 3, "layout": "", "gate_parameters": {}, "compiler": ["gaussian"], } prog = sf.Program(3) with prog.context as q: ops.Rgate(0.3) | q[0] ops.Rgate(0.4) | q[0] new_prog = prog.compile( compiler=DummyCompiler(), optimize=True, ) assert new_prog.circuit[0].__str__() == "Rgate(0.7) | (q[0])"
def test_run_optimizations(self): """Test that circuit is optimized when optimize is True""" class DummyCircuit(Compiler): """A circuit with 2 modes""" interactive = True primitives = {'Rgate'} decompositions = set() 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.Rgate(0.3) | q[0] ops.Rgate(0.4) | q[0] new_prog = prog.compile( compiler=DummyCircuit(), optimize=True, ) assert new_prog.circuit[0].__str__() == "Rgate(0.7) | (q[0])"
def get_unitary(gate_args, device, phi_loop=None, delays=[1, 6, 36]): """Computes the unitary corresponding to a given set of gates Args: gate_args (_type_): dictionary with the collected arguments for squeezing gate, phase gates and beamsplitter gates device (sf.Device): the Borealis device phi_loop (list, optional): list containing the three loop offsets. Defaults to None. delays (list, optional): the delay applied by each loop in time bins. Returns: np.ndarray: a unitary matrix """ args_list = to_args_list(gate_args, device) n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*args_list) as (p, q): for i in range(len(delays)): ops.Rgate(p[2 * i + 1]) | q[n[i]] ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i]], q[n[i + 1]]) if phi_loop is not None: ops.Rgate(phi_loop[i]) | q[n[i]] prog.space_unroll() prog = prog.compile(compiler="passive") # unitary matrix assert prog.circuit U = prog.circuit[0].op.p[0] return U
def prog(): """Program fixture.""" program = Program(8) with program.context as q: ops.Rgate(0.5) | q[0] ops.Rgate(0.5) | q[4] ops.MeasureFock() | q return program
def test_all_passive_gates(hbar, tol): """test that all gates run and do not cause anything to crash""" eng = sf.LocalEngine(backend="gaussian") circuit = sf.Program(4) with circuit.context as q: for i in range(4): ops.Sgate(1, 0.3) | q[i] ops.Rgate(np.pi) | q[0] ops.PassiveChannel(np.ones((2, 2))) | (q[1], q[2]) ops.LossChannel(0.9) | q[1] ops.MZgate(0.25 * np.pi, 0) | (q[2], q[3]) ops.PassiveChannel(np.array([[0.83]])) | q[0] ops.sMZgate(0.11, -2.1) | (q[0], q[3]) ops.Interferometer(np.array([[np.exp(1j * 2)]])) | q[1] ops.BSgate(0.8, 0.4) | (q[1], q[3]) ops.Interferometer(0.5**0.5 * np.fft.fft(np.eye(2))) | (q[0], q[2]) ops.PassiveChannel(0.1 * np.ones((3, 3))) | (q[3], q[1], q[0]) cov = eng.run(circuit).state.cov() circuit = sf.Program(4) with circuit.context as q: ops.Rgate(np.pi) | q[0] ops.PassiveChannel(np.ones((2, 2))) | (q[1], q[2]) ops.LossChannel(0.9) | q[1] ops.MZgate(0.25 * np.pi, 0) | (q[2], q[3]) ops.PassiveChannel(np.array([[0.83]])) | q[0] ops.sMZgate(0.11, -2.1) | (q[0], q[3]) ops.Interferometer(np.array([[np.exp(1j * 2)]])) | q[1] ops.BSgate(0.8, 0.4) | (q[1], q[3]) ops.Interferometer(0.5**0.5 * np.fft.fft(np.eye(2))) | (q[0], q[2]) ops.PassiveChannel(0.1 * np.ones((3, 3))) | (q[3], q[1], q[0]) compiled_circuit = circuit.compile(compiler="passive") T = compiled_circuit.circuit[0].op.p[0] S_sq = np.eye(8, dtype=np.complex128) r = 1 phi = 0.3 for i in range(4): S_sq[i, i] = np.cosh(r) - np.sinh(r) * np.cos(phi) S_sq[i, i + 4] = -np.sinh(r) * np.sin(phi) S_sq[i + 4, i] = -np.sinh(r) * np.sin(phi) S_sq[i + 4, i + 4] = np.cosh(r) + np.sinh(r) * np.cos(phi) cov_sq = (hbar / 2) * S_sq @ S_sq.T mu = np.zeros(8) P = interferometer(T) L = (hbar / 2) * (np.eye(P.shape[0]) - P @ P.T) cov2 = P @ cov_sq @ P.T + L assert np.allclose(cov, cov2, atol=tol, rtol=0)
def test_CZgate_decomp_equal(self, setup_eng, s, tol): """Tests that the CZgate gives the same transformation as its decomposition.""" eng, prog = setup_eng(2) with prog.context as q: ops.CZgate(s) | q # run decomposition with reversed arguments ops.Rgate(-np.pi / 2) | q[1] ops.CXgate(-s) | q ops.Rgate(np.pi / 2) | q[1] eng.run(prog) assert np.all(eng.backend.is_vacuum(tol))
def test_apply_history(self, setup_eng, pure): """Tests the reapply history argument works correctly with a backend""" eng, q = setup_eng(2) a = 0.23 r = 0.1 def inspect(): res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_applied(print_fn) return res with eng: ops.Dgate(a) | q[0] ops.Sgate(r) | q[1] state1 = eng.run() expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), ] assert inspect() == expected # reset backend, but reapply history eng.backend.reset(pure=pure) state2 = eng.run(apply_history=True) assert inspect() == expected assert state1 == state2 # append more commands to the same backend with eng: ops.Rgate(r) | q[0] state3 = eng.run() expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Run 1:", "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected assert not state2 == state3 # reset backend, but reapply history eng.backend.reset(pure=pure) state4 = eng.run(apply_history=True) expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected assert state3 == state4
def test_invalid_decompositions(self, monkeypatch): """Test that an exception is raised if the device spec requests a decomposition that doesn't exist""" class DummyDevice(DeviceSpecs): modes = None remote = False local = True interactive = True primitives = {'Rgate', 'Interferometer'} decompositions = {'Rgate': {}} dev = DummyDevice() prog = sf.Program(3) U = np.array([[0, 1], [1, 0]]) with prog.context as q: ops.Rgate(0.6) | q[0] ops.Interferometer(U) | [q[0], q[1]] with monkeypatch.context() as m: # monkeypatch our DummyDevice into the # backend database db = {'dummy': DummyDevice} m.setattr("strawberryfields.devicespecs.backend_specs", db) with pytest.raises(NotImplementedError, match="No decomposition available: Rgate"): new_prog = prog.compile(backend='dummy')
def test_program_subroutine(self, setup_eng, tol): """Simple quantum program with a subroutine and references.""" eng, prog = setup_eng(2) # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(0.7 * np.pi, np.pi / 2) R = ops.Rgate(np.pi / 3) def subroutine(a, b): """Subroutine for the quantum program""" R | a BS | (a, b) R.H | a # main program with prog.context as q: # get register references alice, bob = q ops.All(ops.Vacuum()) | (alice, bob) D | alice subroutine(alice, bob) BS | (alice, bob) subroutine(bob, alice) state = eng.run(prog).state # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0)
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 borealis_program_with_offsets(gate_args): delays = [1, 6, 36] cert = borealis_device.certificate or {"loop_phases": []} loop_phases = cert["loop_phases"] n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*gate_args) as (p, q): ops.Sgate(p[0]) | q[n[0]] for i in range(len(delays)): ops.Rgate(p[2 * i + 1]) | q[n[i]] ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]]) ops.Rgate(loop_phases[i]) | q[n[i]] ops.MeasureFock() | q[0] return prog
def get_photon_number_moments(gate_args, device, phi_loop=None, delays=[1, 6, 36]): """Computes first and second moment of the photon number distribution obtained from a single Args: gate_args (_type_): dictionary with the collected arguments for squeezing gate, phase gates and beamsplitter gates device (sf.Device): the Borealis device phi_loop (list, optional): list containing the three loop offsets. Defaults to None. delays (list, optional): the delay applied by each loop in time bins. Returns: tuple: two np.ndarrays with the mean photon numbers and photon-number covariance matrix, respectively """ args_list = to_args_list(gate_args, device) n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*args_list) as (p, q): ops.Sgate(p[0]) | q[n[0]] for i in range(len(delays)): ops.Rgate(p[2 * i + 1]) | q[n[i]] ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i]], q[n[i + 1]]) if phi_loop is not None: ops.Rgate(phi_loop[i]) | q[n[i]] prog.space_unroll() eng = sf.Engine("gaussian") results = eng.run(prog) assert isinstance(results.state, BaseGaussianState) # quadrature mean vector and covariance matrix mu = results.state.means() cov_q = results.state.cov() # photon-number mean vector and covariance matrix mean_n = photon_number_mean_vector(mu, cov_q) cov_n = photon_number_covmat(mu, cov_q) return mean_n, cov_n
def test_checkpoints(self, setup_eng, tol): """Test history checkpoints work when creating and deleting modes.""" eng, q = setup_eng(2) alice, bob = q # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with eng: D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Del | bob D.H | charlie ops.MeasureX | charlie eng.optimize() state = eng.run() # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0) def check_reg(self, expected_n=None): """Compare Engine.register with the mode list returned by the backend. They should always be in agreement after Engine.run(), Engine.reset_queue() and Engine.reset(). """ rr = eng.register modes = eng.backend.get_modes() # number of elements assert len(rr) == len(modes) if expected_n is not None: assert len(rr) == expected_n # check indices match assert np.all([r.ind for r in rr] == modes) # activity assert np.all([r.active for r in rr]) # check that reset() works check_reg(1) eng.reset() new_reg = eng.register # original number of modes assert len(new_reg) == len(q) # the regrefs are reset as well assert np.all([r.val is None for r in new_reg]) check_reg(2)
def test_GBS_compile_ops_after_measure(self): """Tests that GBS compilation fails when there are operations following a Fock measurement.""" prog = sf.Program(2) with prog.context as q: ops.MeasureFock() | q ops.Rgate(1.0) | q[0] with pytest.raises(program.CircuitError, match="Operations following the Fock measurements."): prog.compile('gbs')
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_apply_history(self, eng): """Tests the reapply history argument""" a = 0.23 r = 0.1 def inspect(): res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_applied(print_fn) return res p1 = sf.Program(2) with p1.context as q: ops.Dgate(a) | q[1] ops.Sgate(r) | q[1] eng.run(p1) expected = [ "Run 0:", "Dgate({}, 0) | (q[1])".format(a), "Sgate({}, 0) | (q[1])".format(r), ] assert inspect() == expected # run the program again eng.reset() eng.run(p1) assert inspect() == expected # apply more commands to the same backend p2 = sf.Program(2) with p2.context as q: ops.Rgate(r) | q[1] eng.run(p2) expected = [ "Run 0:", "Dgate({}, 0) | (q[1])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Run 1:", "Rgate({}) | (q[1])".format(r), ] assert inspect() == expected # reapply history eng.reset() eng.run([p1, p2]) #expected = [ # "Run 0:", # "Dgate({}, 0) | (q[1])".format(a), # "Sgate({}, 0) | (q[1])".format(r), # "Rgate({}) | (q[1])".format(r), #] assert inspect() == expected
def test_apply_history(self, backend): """Tests the reapply history argument""" eng, q = sf.Engine(2) a = 0.23 r = 0.1 def inspect(): res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_applied(print_fn) return res with eng: ops.Dgate(a) | q[0] ops.Sgate(r) | q[1] eng.run(backend) expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), ] assert inspect() == expected # reapply history state2 = eng.run(apply_history=True) assert inspect() == expected # append more commands to the same backend with eng: ops.Rgate(r) | q[0] eng.run() expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Run 1:", "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected # reapply history eng.run(apply_history=True) expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected
def test_print_commands(self, eng, prog): """Program.print and Engine.print_applied return correct strings.""" prog = sf.Program(2) # store the result of the print command in list res res = [] # use a print function that simply appends the operation # name to the results list print_fn = lambda x: res.append(x.__str__()) # prog should now be empty prog.print(print_fn) assert res == [] # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with prog.context as q: alice, bob = q D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob.par).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] prog.print(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1)", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(q1, 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected # NOTE optimization can change gate order result = eng.run(prog, compile_options={'optimize': False}) res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
def test_print_commands(self, backend): """Test print_queue and print_applied returns correct strings.""" eng, q = sf.Engine(2) # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) # get register references alice, bob = q with eng: D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_queue(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1) ", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(RR(q[1]), 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected state = eng.run(backend) # queue should now be empty res = [] eng.print_queue(print_fn) assert res == [] # print_applied should now not be empty res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
def test_subsystems(self, setup_eng, tol): """Check that the backend keeps in sync with the program when creating and deleting modes.""" null = sf.Program(2) # empty program eng, prog = setup_eng(2) # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with prog.context as q: alice, bob = q D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Del | bob D.H | charlie ops.MeasureX | charlie def check_reg(p, expected_n=None): """Compare Program.register with the mode list returned by the backend. They should always be in agreement after Engine.run() and Engine.reset(). """ rr = p.register modes = eng.backend.get_modes() # number of elements assert len(rr) == len(modes) if expected_n is not None: assert len(rr) == expected_n # check indices match assert np.all([r.ind for r in rr] == modes) # activity assert np.all([r.active for r in rr]) state = eng.run(null) check_reg(null, 2) state = eng.run(prog).state check_reg(prog, 1) # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0) # check that reset() works eng.reset() # the regrefs are reset as well assert np.all([r.val is None for r in prog.register])
def test_checkpoints(self, backend): """Test history checkpoints work when creating and deleting modes.""" eng, q = sf.Engine(2) alice, bob = q # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with eng: D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Del | bob D.H | charlie ops.MeasureX | charlie eng.optimize() eng.run(backend) assert not alice.active assert charlie.active # check that reset_queue() restores the latest RegRef checkpoint # (created by eng.run() above) eng.reset_queue() assert not alice.active assert charlie.active with eng: diana, = ops.New(1) ops.Del | charlie assert not charlie.active assert diana.active eng.reset_queue() assert charlie.active assert not diana.active eng.reset() new_reg = eng.register # original number of modes assert len(new_reg) == len(q) # the regrefs are reset as well assert np.all([r.val is None for r in new_reg])
def test_print_commands(self, eng, prog): """Program.print and Engine.print_applied return correct strings.""" prog = sf.Program(2) res = [] print_fn = lambda x: res.append(x.__str__()) # prog should now be empty prog.print(print_fn) assert res == [] # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with prog.context as q: alice, bob = q D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] prog.print(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1)", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(RR(q[1]), 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected state = eng.run( prog, compile=False ) # FIXME optimization can change gate order, however this is not a good way of avoiding it res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
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_reset(self, eng, prog): """Running independent programs with an engine reset in between.""" assert not eng.run_progs eng.run(prog) assert len(eng.run_progs) == 1 eng.reset() assert not eng.run_progs p2 = sf.Program(3) with p2.context as q: ops.Rgate(1.0) | q[2] eng.run(p2) assert len(eng.run_progs) == 1
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 borealis_program(gate_args): delays = [1, 6, 36] n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*gate_args) as (p, q): ops.Sgate(p[0]) | q[n[0]] for i in range(len(delays)): ops.Rgate(p[2 * i + 1]) | q[n[i]] ops.BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]]) ops.MeasureFock() | q[0] return prog
def interferometer(params, q): """Parameterised interferometer acting on ``N`` modes. Args: params (list[float]): list of length ``max(1, N-1) + (N-1)*N`` parameters. * The first ``N(N-1)/2`` parameters correspond to the beamsplitter angles * The second ``N(N-1)/2`` parameters correspond to the beamsplitter phases * The final ``N-1`` parameters correspond to local rotation on the first N-1 modes q (list[RegRef]): list of Strawberry Fields quantum registers the interferometer is to be applied to """ N = len(q) theta = params[:N * (N - 1) // 2] phi = params[N * (N - 1) // 2:N * (N - 1)] rphi = params[-N + 1:] if N == 1: # the interferometer is a single rotation ops.Rgate(rphi[0]) | q[0] return n = 0 # keep track of free parameters # Apply the rectangular beamsplitter array # The array depth is N for l in range(N): for k, (q1, q2) in enumerate(zip(q[:-1], q[1:])): # skip even or odd pairs depending on layer if (l + k) % 2 != 1: ops.BSgate(theta[n], phi[n]) | (q1, q2) n += 1 # apply the final local phase shifts to all modes except the last one for i in range(max(1, N - 1)): ops.Rgate(rphi[i]) | q[i]
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 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])