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)
예제 #2
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 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)))
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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)))
예제 #10
0
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
예제 #11
0
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