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]}
Example #9
0
    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
Example #14
0
    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)))
Example #19
0
    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