def _monte_carlo_dfe(program: Program, qubits: Sequence[int], in_states: list, n_terms: int,
                     benchmarker: BenchmarkConnection) -> ExperimentSetting:
    """Yield experiments over itertools.product(in_paulis).

    Used as a helper function for generate_monte_carlo_xxx_dfe_experiment routines.

    :param program: A program comprised of clifford gates
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``.
    :param in_states: Use these single-qubit Pauli operators in every itertools.product()
        to generate an exhaustive list of DFE experiments.
    :param n_terms: Number of preparation and measurement settings to be chosen at random
    :return: experiment setting iterator
    :rtype: ``ExperimentSetting``
    """
    all_st_inds = np.random.randint(len(in_states), size=(n_terms, len(qubits)))
    for st_inds in all_st_inds:
        i_st = functools.reduce(mul, (in_states[si](qubits[i])
                                      for i, si in enumerate(st_inds)
                                      if in_states[si] is not None), TensorProductState())

        # TODO: we should not pick a new one, we should just return a trivial experiment
        while len(i_st) == 0:
            # pick a new one
            second_try_st_inds = np.random.randint(len(in_states), size=len(qubits))
            i_st = functools.reduce(mul, (in_states[si](qubits[i])
                                          for i, si in enumerate(second_try_st_inds)
                                          if in_states[si] is not None), TensorProductState())

        yield ExperimentSetting(
            in_state=i_st,
            out_operator=benchmarker.apply_clifford_to_pauli(program, _state_to_pauli(i_st)),
        )
def _exhaustive_dfe(program: Program, qubits: Sequence[int], in_states,
                    benchmarker: BenchmarkConnection) -> ExperimentSetting:
    """Yield experiments over itertools.product(in_paulis).

    Used as a helper function for generate_exhaustive_xxx_dfe_experiment routines.

    :param program: A program comprised of clifford gates
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``.
    :param in_states: Use these single-qubit Pauli operators in every itertools.product()
        to generate an exhaustive list of DFE experiments.
    :return: experiment setting iterator
    :rtype: ``ExperimentSetting``
    """
    n_qubits = len(qubits)
    for i_states in itertools.product(in_states, repeat=n_qubits):
        i_st = functools.reduce(mul, (op(q) for op, q in zip(i_states, qubits) if op is not None),
                                TensorProductState())

        if len(i_st) == 0:
            continue

        yield ExperimentSetting(
            in_state=i_st,
            out_operator=benchmarker.apply_clifford_to_pauli(program, _state_to_pauli(i_st)),
        )
def generate_exhaustive_state_dfe_experiment(
        benchmarker: BenchmarkConnection, program: Program,
        qubits: list) -> ObservablesExperiment:
    """
    Estimate state fidelity by exhaustive direct fidelity estimation.

    This leads to a quadratic reduction in overhead w.r.t. state tomography for
    fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param benchmarker: object returned from pyquil.api.get_benchmarker() used to conjugate each
        Pauli by the Clifford program
    :param program: A program comprised of Clifford group gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :return: an ObservablesExperiment that constitutes a state DFE experiment.
    """
    # measure all of the traceless combinations of I and Z on the qubits conjugated by the ideal
    # Clifford state preparation program. The in_state is all the all zero state since this is
    # the assumed initialization of the state preparation.
    settings = [
        ExperimentSetting(in_state=zeros_state(qubits),
                          observable=benchmarker.apply_clifford_to_pauli(
                              program, iz_pauli))
        for iz_pauli in all_traceless_pauli_z_terms(qubits)
    ]
    return ObservablesExperiment(settings, program=program)
def generate_monte_carlo_state_dfe_experiment(benchmarker: BenchmarkConnection, program: Program,
                                              qubits: List[int], n_terms=200) \
        -> ObservablesExperiment:
    """
    Estimate state fidelity by sampled direct fidelity estimation.

    This leads to constant overhead (w.r.t. number of qubits) fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param program: A program comprised of clifford gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :param benchmarker: The `BenchmarkConnection` object used to design experiments
    :param n_terms: Number of randomly chosen observables to measure. This number should be a
        constant less than ``2**len(qubits)``, otherwise ``exhaustive_state_dfe`` is more efficient.
    :return: an ObservablesExperiment that constitutes a state DFE experiment.
    """
    # pick n_terms different random combinations of I and Z on the qubits
    rand_iz_paulis = np.random.choice(['I', 'Z'], size=(n_terms, len(qubits)))

    settings = []
    for iz_pauli in rand_iz_paulis:
        # sample a new state if this one is all identity
        while 'Z' not in iz_pauli:
            iz_pauli = np.random.choice(['I', 'Z'], size=len(qubits))
        # conjugate the non-trivial iz Pauli by the ideal state prep program
        obs = benchmarker.apply_clifford_to_pauli(
            program, str_to_pauli_term(''.join(iz_pauli)))
        settings.append(ExperimentSetting(zeros_state(qubits), obs))

    return ObservablesExperiment(settings, program=program)
예제 #5
0
def test_local_conjugate_request(benchmarker):
    config = PyquilConfig()
    if config.compiler_url is not None:
        cxn = BenchmarkConnection(endpoint=config.compiler_url)
        response = cxn.apply_clifford_to_pauli(Program("H 0"), PauliTerm("X", 0, 1.0))
        assert isinstance(response, PauliTerm)
        assert str(response) == "(1+0j)*Z0"
def generate_exhaustive_process_dfe_experiment(
        benchmarker: BenchmarkConnection, program: Program,
        qubits: list) -> ObservablesExperiment:
    """
    Estimate process fidelity by exhaustive direct fidelity estimation (DFE).

    This leads to a quadratic reduction in overhead w.r.t. process tomography for
    fidelity estimation.

    The algorithm is due to:

    .. [DFE1]  Practical Characterization of Quantum Devices without Tomography.
            Silva et al.
            PRL 107, 210404 (2011).
            https://doi.org/10.1103/PhysRevLett.107.210404
            https://arxiv.org/abs/1104.3835

    .. [DFE2]  Direct Fidelity Estimation from Few Pauli Measurements.
            Flammia and Liu.
            PRL 106, 230501 (2011).
            https://doi.org/10.1103/PhysRevLett.106.230501
            https://arxiv.org/abs/1104.4695

    :param benchmarker: object returned from pyquil.api.get_benchmarker() used to conjugate each
        Pauli by the Clifford program
    :param program: A program comprised of Clifford group gates that defines the process for
        which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :return: an ObservablesExperiment that constitutes a process DFE experiment.
    """
    settings = []
    # generate all n-qubit pauli strings but skip the first all identity term
    for pauli_labels in [
            ''.join(x) for x in itertools.product('IXYZ', repeat=len(qubits))
    ][1:]:
        # calculate the appropriate output pauli from applying the ideal program to the Pauli
        observable = benchmarker.apply_clifford_to_pauli(
            program, str_to_pauli_term(pauli_labels, qubits))
        # keep track of non-identity terms that may have a sign contribution
        non_identity_idx = [0 if label == 'I' else 1 for label in pauli_labels]
        # now replace the identities with Z terms, so they can be decomposed into Z eigenstates
        state_labels = [
            'Z' if label == 'I' else label for label in pauli_labels
        ]

        # loop over the ±1 eigenstates of each Pauli
        for eigenstate in itertools.product([0, 1], repeat=len(qubits)):
            in_state = TensorProductState(
                _OneQState(l, s, q)
                for l, s, q in zip(state_labels, eigenstate, qubits))
            # make the observable negative if the in_state is a negative eigenstate
            sign_contribution = (-1)**np.dot(eigenstate, non_identity_idx)
            settings.append(
                ExperimentSetting(in_state=in_state,
                                  observable=observable * sign_contribution))
    return ObservablesExperiment(settings, program=program)
def generate_monte_carlo_process_dfe_experiment(benchmarker: BenchmarkConnection, program: Program,
                                                qubits: List[int], n_terms: int = 200) \
        -> ObservablesExperiment:
    """
    Estimate process fidelity by randomly sampled direct fidelity estimation.

    This leads to constant overhead (w.r.t. number of qubits) fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param program: A program comprised of Clifford group gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :param benchmarker: The `BenchmarkConnection` object used to design experiments
    :param n_terms: Number of randomly chosen observables to measure. This number should be a
        constant less than ``2**len(qubits)``, otherwise ``exhaustive_process_dfe`` is more
        efficient.
    :return: an ObservablesExperiment that constitutes a process DFE experiment.
    """
    single_q_paulis = ['I', 'X', 'Y', 'Z']
    # pick n_terms different random combinations of I, X, Y, Z on the qubits
    rand_paulis = np.random.randint(len(single_q_paulis),
                                    size=(n_terms, len(qubits)))

    settings = []
    for pauli_idxs in rand_paulis:
        # sample a new state if this one is all identity
        while sum(pauli_idxs) == 0:
            pauli_idxs = np.random.randint(len(single_q_paulis),
                                           size=len(qubits))
        # convert from indices to string
        pauli_str = ''.join([single_q_paulis[idx] for idx in pauli_idxs])
        # convert from string to PauliTerm on appropriate qubits
        pauli = str_to_pauli_term(pauli_str, qubits)
        # calculate the appropriate output pauli from applying the ideal program to the Pauli
        observable = benchmarker.apply_clifford_to_pauli(program, pauli)

        # now replace the identities with Z terms, so they can be decomposed into Z eigenstates
        state_labels = ['Z' if label == 'I' else label for label in pauli_str]

        # randomly pick between ±1 eigenstates of each Pauli
        eigenstate = np.random.randint(2, size=len(qubits))
        in_state = TensorProductState(
            _OneQState(l, s, q)
            for l, s, q in zip(state_labels, eigenstate, qubits))
        # make the observable negative if the in_state is a negative eigenstate
        # only keep track of minus eigenstates associated to non-identity Paulis, i.e. idx >= 1
        sign_contribution = (-1)**np.dot(eigenstate,
                                         [min(1, idx) for idx in pauli_idxs])
        settings.append(
            ExperimentSetting(in_state=in_state,
                              observable=observable * sign_contribution))

    return ObservablesExperiment(settings, program=program)
예제 #8
0
def benchmarker(client_configuration: QCSClientConfiguration):
    bm = BenchmarkConnection(timeout=2,
                             client_configuration=client_configuration)
    bm.apply_clifford_to_pauli(Program(I(0)), sX(0))
    return bm