Saltar al contenido principal

Hardware

nota

Masao Tokunari y Tamiya Onodera (14 de junio de 2024)

Este curso está basado en un curso en vivo en la Universidad de Tokio.

El PDF de la clase de esta lección se dividió en dos partes. Descargar parte 1 y Descargar parte 2. Ten en cuenta que algunos fragmentos de código pueden estar desactualizados, ya que son imágenes estáticas.

1. Introducción

Esta lección explora el hardware moderno de computación cuántica.

Comenzamos verificando algunas versiones e importando los paquetes relevantes.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
import statistics

from qiskit_ibm_runtime import QiskitRuntimeService

2. Backend y Target

Qiskit ofrece una API para obtener tanto información estática como dinámica sobre un dispositivo cuántico. Utilizamos una instancia de Backend como interfaz con un dispositivo. Esta contiene una instancia de Target – un modelo abstracto de máquina que resume las propiedades esenciales, como la Instruction Set Architecture (ISA) y las propiedades y restricciones asociadas. Utilicemos estas instancias de Backend para obtener parte de la información que ves en la página de Compute resources de la plataforma IBM Quantum®. Primero, creamos una instancia de Backend para un dispositivo de nuestra elección. En el siguiente ejemplo utilizamos "ibm_kyoto", "ibm_kawasaki" o la máquina Eagle menos ocupada. Tu acceso a QPUs puede variar – ajusta el nombre del backend en consecuencia.

service = QiskitRuntimeService()
# backend = service.backend("ibm_kawasaki") # an Eagle, if you have access to ibm_kawasaki
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
backend.name
'ibm_strasbourg'

Comencemos con alguna información básica (estática) sobre el dispositivo.

print(
f"""
{backend.name}, {backend.num_qubits} qubits
processor type = {backend.processor_type}
basis gates = {backend.basis_gates}
"""
)
ibm_strasbourg, 127 qubits
processor type = {'family': 'Eagle', 'revision': 3}
basis gates = ['ecr', 'id', 'rz', 'sx', 'x']

2.1 Ejercicio

Intenta obtener la información básica sobre un dispositivo Heron, "ibm_strasbourg". Pruébalo primero por tu cuenta – más abajo encontrarás un código de solución para comparar.

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}
"""
)
ibm_strasbourg, 133 qubits
processor type = {'family': 'Heron', 'revision': '1'}
basis gates = ['cz', 'id', 'rz', 'sx', 'x']

2.2 Coupling Map

Ahora dibujamos el mapa de acoplamiento del dispositivo. Como puedes ver, los nodos representan qubits numerados. Las aristas indican qué pares pueden conectarse directamente con la puerta de entrelazamiento de 2 qubits. La topología se denomina "red Heavy-Hex".

# 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 of the previous code cell

3. Propiedades de los qubits

El dispositivo Eagle tiene 127 qubits. Veamos las propiedades de algunos de ellos.

for qn in range(backend.num_qubits):
if qn >= 5:
break
print(f"{qn}: {backend.qubit_properties(qn)}")
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)

Calculemos la mediana de los tiempos T1 de los qubits. Compara el resultado con lo que se muestra para el dispositivo en la IBM Quantum Platform.

t1s = [backend.qubit_properties(qq).t1 for qq in range(backend.num_qubits)]
f"Median T1: {(statistics.median(t1s)*10**6):.2f} \u03bcs"
'Median T1: 285.43 μs'

3.1 Ejercicio

Calcula la mediana de los tiempos T2 de los qubits. Pruébalo primero por tu cuenta – más abajo encontrarás un código de solución para comparar.

# 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"
'Median T2: 173.10 μs'

3.2 Errores de puertas y de lectura

Ahora nos centramos en los errores de puertas. Primero examinamos la estructura de datos de la instancia Target. Es un diccionario cuyas claves son nombres de operaciones.

target = backend.target
target.keys()
dict_keys(['measure', 'id', 'sx', 'delay', 'x', 'for_loop', 'rz', 'if_else', 'ecr', 'reset', 'switch_case'])

Los valores también son diccionarios. Veamos algunas entradas del diccionario de valores para la operación 'sx'.

for i, qq in enumerate(target["sx"]):
if i >= 5:
break
print(i, qq, target["sx"][qq])
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)

Hagamos lo mismo para las operaciones 'ecr' y 'measure'.

for i, edge in enumerate(target["ecr"]):
if i >= 5:
break
print(i, edge, target["ecr"][edge])
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])
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)

Como se puede ver, los errores de lectura suelen ser mayores que los errores de las operaciones de 2 qubits, que a su vez son mayores que los errores de las operaciones de 1 qubit.

Ahora que entendemos las estructuras de datos, podemos calcular los errores medianos para las puertas 'sx' y 'ecr'. Compara nuevamente los resultados con la información del dispositivo en la IBM Quantum Platform.

sx_errors = [inst_prop.error for inst_prop in target["sx"].values()]
f"Median SX error: {(statistics.median(sx_errors)):.3e}"
'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}"
'Median ECR error: 6.895e-03'

4. Apéndice

Una característica especialmente popular de Qiskit es su capacidad de visualización. Incluye visualizadores de circuitos, visualizadores de estados y distribuciones, y un visualizador de Target. Los dos primeros ya los has utilizado en los Jupyter notebooks anteriores. Veamos ahora algunas funciones del visualizador de Target.

from qiskit.visualization import plot_gate_map

plot_gate_map(backend, font_size=14)

Output of the previous code cell

from qiskit.visualization import plot_error_map

plot_error_map(backend)

Output of the previous code cell

# Check Qiskit version
import qiskit

qiskit.__version__
'2.0.2'