Source code for QCut.backend_utility
"""
Utility functions for running on real backends.
"""
from __future__ import annotations
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit_experiments.library import LocalReadoutError
[docs]
def transpile_experiments(experiment_circuits: list, backend) -> list:
"""
Transpile experiment circuits.
Args:
experiment_circuits: (list): Experiment circuits to be transpiled.
backend (str): Backend to transpile to.
Returns:
list: A list of transpiled experiment circuits.
"""
return [
[
transpile(circuit, backend, layout_method="sabre", optimization_level=3)
for circuit in circuit_group
]
for circuit_group in experiment_circuits
]
[docs]
def run_and_expectation_value(
circuit: QuantumCircuit, backend, observables: list, shots: int, mitigate=False
) -> tuple[dict, list]:
"""Run circuit and calculate expectation value.
Args:
circuit (QuantumCircuit): A quantum circuit.
backend: Backend to run circuit on.
observables (list): Observables to calculate expectation values for.
shots (int): Number of shots.
mitigate (bool): If True, use readout error mitigation.
Returns:
tuple: A tuple containing:
- dict: Counts from the circuit run.
- list: A list of expectation values.
"""
counts = run_on_backend(circuit, backend, shots)
if mitigate:
q = list(counts.keys())
qs = list(range(len(q[0])))
exp = LocalReadoutError(qs)
exp.analysis.set_options(verbose=False)
result = exp.run(backend)
mitigator = result.analysis_results("Local Readout Mitigator").value
mitigated_quasi_probs = mitigator.quasi_probabilities(counts)
probs_test = {
f"{int(old_key):0{len(qs)}b}"[::-1]: mitigated_quasi_probs[old_key] * shots
if mitigated_quasi_probs[old_key] > 0
else 0
for old_key in mitigated_quasi_probs
}
counts = probs_test
exps = expectation_values(counts, observables, shots)
return counts, exps
[docs]
def expectation_values(counts: dict, observables: list, shots: int) -> list:
"""Calculate expectation values.
Args:
counts (dict):
Counts obtained from circuit run, where keys are measurement outcomes and
values are the number of times each outcome was observed.
observables (list):
List of observables to calculate expectation values for. Each observable can
be an integer (index of a single qubit) or a list of integers
(indices of multiple qubits).
shots (int): Number of shots (total number of measurements).
Returns:
list: A list of expectation values for each observable.
"""
# Convert results to a list of dicts with measurement values and counts
measurements = [
{"meas": [1 if bit == "0" else -1 for bit in meas], "count": count}
for meas, count in counts.items()
]
# Initialize an array to store expectation values for each observable
exps = np.zeros(len(observables))
# Calculate expectation values
for measurement in measurements:
meas_values = measurement["meas"]
count = measurement["count"]
for idx, observable in enumerate(observables):
if isinstance(observable, int):
exps[idx] += meas_values[observable] * count
else:
exps[idx] += np.prod([meas_values[zi] for zi in observable]) * count
return np.array(exps) / shots
[docs]
def run_on_backend(circuit: QuantumCircuit, backend, shots: int) -> dict:
"""Run a quantum circuit on a specified backend.
Args:
circuit (QuantumCircuit): The quantum circuit to be executed.
backend (Backend): The backend to use for executing the circuit.
shots (int): The number of shots (repetitions) to run the circuit.
Returns:
dict: A dictionary of counts from the circuit run.
"""
job = backend.run(circuit, shots=shots)
result = job.result()
return result.get_counts()