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 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_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 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 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 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_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_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
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
return fig, ax 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