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)
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)
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