Hardware
Masao Tokunari and Tamiya Onodera (14 June 2024)
This course is based on a live course delivered at the University of Tokyo. PDFs of lecture notes will be available soon. Note that some code snippets may become deprecated since these are static images.
1. Introduction
This lesson explores modern quantum computing hardware.
We will start by verifying some versions and importing some relevant packages.
import statistics
from qiskit_ibm_runtime import QiskitRuntimeService
2. Backend and Target
Qiskit provides an API to obtain the information, both static and dynamic, about a quantum device. We use a Backend instance to interface with a device, which includes a Target instance, an abstract machine model that summarizes the pertinent features such as its instruction set architecture (ISA) and any properties or constraints associated with it. Let us use these backend instances to get some of the information you see on the Compute resources page. at IBM Quantum® Platform. To begin with, we create a Backend instance for a device of interest. In the following, we pick "ibm_kyoto" , "ibm_kawasaki" or the least-busy Eagle machine. Your access to QPUs might differ; simply update the backend name accordingly.
service = QiskitRuntimeService()
# backend = service.backend("ibm_kawasaki") # an Eagle, if you have access to ibm_kawasaki
# backend = service.backend("ibm_kyoto") # an Eagle
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
backend.name
Output:
'ibm_strasbourg'
We start with some basic (static) information about the device.
print(
f"""
{backend.name}, {backend.num_qubits} qubits
processor type = {backend.processor_type}
basis gates = {backend.basis_gates}
"""
)
Output:
ibm_strasbourg, 127 qubits
processor type = {'family': 'Eagle', 'revision': 3}
basis gates = ['ecr', 'id', 'rz', 'sx', 'x']
2.1 Exercise
Try to get the basic information about a Heron device, "ibm_strasbourg". Try this on your own, but code has been added below for you to check yourself.
a_heron = service.backend("ibm_strasbourg") # a Heron
# your code here
print(
f"""
{backend.name}, {a_heron.num_qubits} qubits
processor type = {a_heron.processor_type}
basis gates = {a_heron.basis_gates}
"""
)
Output:
ibm_strasbourg, 133 qubits
processor type = {'family': 'Heron', 'revision': '1'}
basis gates = ['cz', 'id', 'rz', 'sx', 'x']
2.2 Coupling map
We now draw the coupling map of the device. As you can see, nodes are qubits which are numbered. Edges indicate pairs to which you can directly apply the 2-qubit entangling gate. The topology is called a "heavy-hex lattice".
# This function requires that Graphviz is installed. If you need to install Graphviz you can refer to:
# https://graphviz.org/download/#executable-packages for instructions.
try:
fig = backend.coupling_map.draw()
except RuntimeError as ex:
print(ex)
fig
Output:

3. Qubit properties
The Eagle device has 127 qubits. Let us obtain the properties of some of them.
for qn in range(backend.num_qubits):
if qn >= 5:
break
print(f"{qn}: {backend.qubit_properties(qn)}")
Output:
0: QubitProperties(t1=0.000183686508736532, t2=0.00023613944465408068, frequency=4832100227.116953)
1: QubitProperties(t1=0.00048794378526038294, t2=9.007098375327869e-05, frequency=4736264354.075363)
2: QubitProperties(t1=0.00021247781834456527, t2=7.81037910324034e-05, frequency=4859349851.150393)
3: QubitProperties(t1=0.0002936462084765663, t2=0.00011400214529510604, frequency=4679749549.503852)
4: QubitProperties(t1=0.00044229440258559125, t2=0.0003181648356339447, frequency=4845872064.050596)
Let us calculate the median of T1 times of the qubits. Please compare the result to the one shown for the device on IBM Quantum Platfrom.
t1s = [backend.qubit_properties(qq).t1 for qq in range(backend.num_qubits)]
f"Median T1: {(statistics.median(t1s)*10**6):.2f} \u03bcs"
Output:
'Median T1: 285.43 μs'
3.1 Exercise
Pease calculate the median of T2 times of the qubits. Try this on your own, but code has been added below for you to check yourself.
# Your code here
t2s = [backend.qubit_properties(qq).t2 for qq in range(backend.num_qubits)]
f"Median T2: {(statistics.median(t2s)*10**6):.2f} \u03bcs"
Output:
'Median T2: 173.10 μs'
3.2 Gate and readout errors
We now turn to gate errors. To begin with, we study the data structure of the target instance. It is a dictionary whose keys are operation names.
target = backend.target
target.keys()
Output:
dict_keys(['measure', 'id', 'sx', 'delay', 'x', 'for_loop', 'rz', 'if_else', 'ecr', 'reset', 'switch_case'])
Its values are also dictionaries. Let us look at some of the items of the value (dictionary) for the 'sx' operation.
for i, qq in enumerate(target["sx"]):
if i >= 5:
break
print(i, qq, target["sx"][qq])
Output:
0 (0,) InstructionProperties(duration=6e-08, error=0.0007401311759115297)
1 (1,) InstructionProperties(duration=6e-08, error=0.0003163759907528654)
2 (2,) InstructionProperties(duration=6e-08, error=0.0003183859004638003)
3 (3,) InstructionProperties(duration=6e-08, error=0.00042235914178831863)
4 (4,) InstructionProperties(duration=6e-08, error=0.011163151923589715)
Let us do the same for the 'ecr' and 'measure' operations.
for i, edge in enumerate(target["ecr"]):
if i >= 5:
break
print(i, edge, target["ecr"][edge])
Output:
0 (0, 14) InstructionProperties(duration=6.6e-07, error=0.01486295709788732)
1 (1, 0) InstructionProperties(duration=6.6e-07, error=0.015201590794522601)
2 (2, 1) InstructionProperties(duration=6.6e-07, error=0.00697838102630724)
3 (2, 3) InstructionProperties(duration=6.6e-07, error=0.008075067943986797)
4 (3, 4) InstructionProperties(duration=6.6e-07, error=0.0630164507876913)
for i, qq in enumerate(target["measure"]):
if i >= 5:
break
print(i, qq, target["measure"][qq])
Output:
0 (0,) InstructionProperties(duration=1.6e-06, error=0.0078125)
1 (1,) InstructionProperties(duration=1.6e-06, error=0.155029296875)
2 (2,) InstructionProperties(duration=1.6e-06, error=0.057373046875)
3 (3,) InstructionProperties(duration=1.6e-06, error=0.02880859375)
4 (4,) InstructionProperties(duration=1.6e-06, error=0.01318359375)
As you can see, the errors of readout tend to be larger than those of the 2-qubit operation, which in turn tend to be larger than the 1-qubit operation.
Having understood the data structures, we are ready to calculate the median errors for the 'sx' and the 'ecr' gates. Again, please compare the results with the ones shown for the device at the IBM Quantum Platfrom.
sx_errors = [inst_prop.error for inst_prop in target["sx"].values()]
f"Median SX error: {(statistics.median(sx_errors)):.3e}"
Output:
'Median SX error: 2.277e-04'
ecr_errors = [inst_prop.error for inst_prop in target["ecr"].values()]
f"Median ECR error: {(statistics.median(ecr_errors)):.3e}"
Output:
'Median ECR error: 6.895e-03'
4. Appendix
A popular feature of Qiskit is its visualization capability. It includes circuit visualizers, state and distribution visualizers, and target visualizer. You already used the first two in the previous jupyter notebooks. Let us use some capabilities of the target visualizer.
from qiskit.visualization import plot_gate_map
plot_gate_map(backend, font_size=14)
Output:

from qiskit.visualization import plot_error_map
plot_error_map(backend)
Output:

# Check Qiskit version
import qiskit
qiskit.__version__
Output:
'2.0.2'