def test_cov_is_pure(): """Tests space unrolling when going into the Gaussian backend""" delays = [1, 6, 36] modes = 216 angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) net = modes + sum(delays) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): Sgate(0.8) | q[n[0]] for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() eng = sf.Engine(backend="gaussian") results = eng.run(prog) cov = results.state.cov() mu = np.zeros(len(cov)) mu_vac, cov_vac = reduced_state(mu, cov, list(range(vac_modes))) mu_comp, cov_comp = reduced_state(mu, cov, list(range(vac_modes, net))) assert np.allclose(cov_vac, 0.5 * (sf.hbar) * np.identity(2 * vac_modes)) assert is_pure_cov(cov_comp, hbar=sf.hbar)
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 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 test_is_permutation_when_angle_pi_on_two(delays, modes): """Checks that if all the beamsplitters are cross then the absolute value output matrix is a permutation matrix""" delays = list(delays) net = modes + sum(delays) angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) angles[0] = np.pi / 2 * np.random.randint(2, size=net) angles[1] = np.pi / 2 * np.random.randint(2, size=net) angles[2] = np.pi / 2 * np.random.randint(2, size=net) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] assert np.allclose(U @ U.T.conj(), np.identity(len(U))) assert np.allclose(list(map(max, np.abs(U))), 1.0)
def test_no_entanglement_between_padding_and_computational_modes( delays, modes): """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations""" delays = list(delays) angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) vac_modes = sum(delays) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] # Check that it is indeed the identity U_AA = U[:vac_modes, :vac_modes] U_AB = U[vac_modes:, :vac_modes] U_BA = U[:vac_modes, vac_modes:] U_BB = U[vac_modes:, vac_modes:] assert np.allclose(U_AA, np.identity(vac_modes)) assert np.allclose(U_AB, 0) assert np.allclose(U_BA, 0) assert np.allclose(U_BB @ U_BB.T.conj(), np.identity(len(U_BB)))
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_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_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 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 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_delay_loops(delays): """Test that program calculates correctly the delays present in the circuit for a TDM program.""" gate_args, _ = random_gate_args(delays, num_modes=216) n, N = get_mode_indices(delays) prog = sf.TDMProgram(N=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] assert prog.get_delays() == delays
def test_crop_value(delays): """Test that program calculates correctly the the number of vacuum modes arriving at the detector before the first computational mode""" gate_args, expected_crop_value = random_gate_args(delays, num_modes=216) 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] assert prog.get_crop_value() == expected_crop_value
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 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 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 multiple_loops_program(delays, gate_args): """Multiple loops program. This function automatically creates a multiple loop program given a list of delays and the corresponding gate arguments. Args: delays (list): a list with the given delay for each loop in the program gate_args (list[array]): a list containing the parameters of the gates """ 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.MeasureX | 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_lossless_no_mixing_no_rotation_U(delays, modes): """Test that the U matrix is the identity if there is no beamsplitter mixing and no rotations""" delays = list(delays) angles = np.zeros([2 * len(delays), modes + sum(delays)]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) with prog.context(*angles) as (p, q): for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) prog.space_unroll() compiled = prog.compile(compiler="passive") passive_elem = compiled.circuit[0] U = passive_elem.op.p[0] # Check that it is indeed the identity assert np.allclose(U, np.identity(len(U)))
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_locking_when_unrolling(self, space_unrolled, start_locked): """Test that a locked program can be (un)rolled with the locking intact.""" prog = sf.TDMProgram(N=2) with prog.context([0, 0], [0, 0], [0, 0]) 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] if start_locked: prog.lock() assert prog.locked == start_locked if space_unrolled: prog.space_unroll() else: prog.unroll() assert prog.locked == start_locked
def test_space_unrolling(): """Tests that space-unrolling works and that it can be done twice""" delays = [1, 6, 36] modes = 216 angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) with prog.context(*angles) as (p, q): Sgate(0.8) | q[n[0]] for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) assert prog.is_unrolled == False prog.space_unroll() assert prog.timebins == 259 vac_modes = prog.concurr_modes - 1 assert prog.num_subsystems == prog.timebins + vac_modes # check that the number of gates are correct. assert [isinstance(cmd.op, Sgate) for cmd in prog.circuit].count(True) == prog.timebins assert [isinstance(cmd.op, Rgate) for cmd in prog.circuit].count(True) == 259 * len(delays) assert [isinstance(cmd.op, BSgate) for cmd in prog.circuit].count(True) == 259 * len(delays) prog.space_unroll() # space-unroll the program twice to check that it works assert prog.is_unrolled == True
def test_rolling_space_unrolled(): """Tests that rolling a space-unrolled circuit works""" delays = [1, 6, 36] modes = 216 angles = np.concatenate([ generate_valid_bs_sequence(delays, modes), generate_valid_r_sequence(delays, modes) ]) d = len(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram([N]) with prog.context(*angles) as (p, q): Sgate(0.8) | q[n[0]] for i in range(d): Rgate(p[i + d]) | q[n[i]] BSgate(p[i], np.pi / 2) | (q[n[i + 1]], q[n[i]]) rolled_circuit = prog.circuit.copy() num_subsystems_pre_roll = prog.num_subsystems init_num_subsystems_pre_roll = prog.init_num_subsystems assert prog.is_unrolled == False # space-unroll the program prog.space_unroll() assert prog.is_unrolled == True # roll the program back up prog.roll() assert prog.is_unrolled == False assert prog.num_subsystems == num_subsystems_pre_roll assert prog.init_num_subsystems == init_num_subsystems_pre_roll assert len(prog.circuit) == len(rolled_circuit) assert prog.circuit == rolled_circuit
if __name__ == "__main__": # connect to the remote engine and obtain a ``device`` object eng = sf.RemoteEngine("borealis") device = eng.device # create a list of list of gate arguments for a GBS instance gate_args_list = borealis_gbs(device, modes=288, squeezing="high") # create a Strawberry Fields program delays = [1, 6, 36] vac_modes = sum(delays) n, N = get_mode_indices(delays) prog = sf.TDMProgram(N) with prog.context(*gate_args_list) as (p, q): Sgate(p[0]) | q[n[0]] for i in range(len(delays)): Rgate(p[2 * i + 1]) | q[n[i]] BSgate(p[2 * i + 2], np.pi / 2) | (q[n[i + 1]], q[n[i]]) MeasureFock() | q[0] # define number of shots and submit job to hardware shots = 250_000 results = eng.run(prog, shots=shots, crop=True) # the GBS samples samples = results.samples # plot the estimated simulation times of the experimental samples