Hardware
Masao Tokunari e Tamiya Onodera (14 de junho de 2024)
Este curso é baseado num curso presencial ministrado na Universidade de Tóquio.
O PDF desta aula foi dividido em duas partes. Baixe a parte 1 e baixe a parte 2. Observe que alguns trechos de código podem ficar desatualizados, pois são imagens estáticas.
1. Introdução
Esta aula explora o hardware de computação quântica moderno.
Vamos começar verificando algumas versões e importando os pacotes relevantes.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import statistics
from qiskit_ibm_runtime import QiskitRuntimeService
2. Backend e Target
O Qiskit fornece uma API para obter informações, tanto estáticas quanto dinâmicas, sobre um dispositivo quântico. Usamos uma instância de Backend para fazer interface com um dispositivo, que inclui uma instância de Target, um modelo de máquina abstrato que resume os recursos pertinentes, como sua arquitetura de conjunto de instruções (ISA) e quaisquer propriedades ou restrições associadas a ela. Vamos usar essas instâncias de backend para obter algumas das informações que você vê na página Recursos de computação na Plataforma IBM Quantum®. Primeiro, criamos uma instância de backend para um dispositivo de interesse. A seguir, escolhemos "ibm_kyoto", "ibm_kawasaki" ou a máquina Eagle menos ocupada. Seu acesso a QPUs pode ser diferente; atualize o nome do backend adequadamente.
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'
Começamos com algumas informações básicas (estáticas) sobre o 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 Exercício
Tente obter as informações básicas sobre um dispositivo Heron, "ibm_strasbourg". Tente isso por conta própria, mas o código está logo abaixo para você conferir sua resposta.
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 Mapa de acoplamento
Agora vamos desenhar o mapa de acoplamento do dispositivo. Como você pode ver, os nós são qubits numerados. As arestas indicam os pares aos quais você pode aplicar diretamente a porta de entrelaçamento de 2 qubits. A topologia é chamada de "rede 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

3. Propriedades dos qubits
O dispositivo Eagle tem 127 qubits. Vamos obter as propriedades de alguns deles.
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)
Vamos calcular a mediana dos tempos T1 dos qubits. Compare o resultado com o exibido para o dispositivo na 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 Exercício
Calcule a mediana dos tempos T2 dos qubits. Tente por conta própria, mas o código foi adicionado abaixo para você conferir.
# 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 Erros de porta e de leitura
Agora nos voltamos para os erros de porta. Para começar, estudamos a estrutura de dados da instância de target. Trata-se de um dicionário cujas chaves são nomes de operações.
target = backend.target
target.keys()
dict_keys(['measure', 'id', 'sx', 'delay', 'x', 'for_loop', 'rz', 'if_else', 'ecr', 'reset', 'switch_case'])
Seus valores também são dicionários. Vamos observar alguns dos itens do valor (dicionário) para a operação '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)
Vamos fazer o mesmo para as operações 'ecr' e '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 você pode ver, os erros de leitura tendem a ser maiores do que os da operação de 2 qubits, que por sua vez tendem a ser maiores do que os da operação de 1 qubit.
Tendo entendido as estruturas de dados, estamos prontos para calcular os erros medianos das portas 'sx' e 'ecr'. Novamente, compare os resultados com os mostrados para o dispositivo na 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
Um recurso popular do Qiskit é sua capacidade de visualização. Ele inclui visualizadores de circuitos, de estados e distribuições, e de targets. Você já usou os dois primeiros nos notebooks anteriores. Vamos explorar algumas funcionalidades do visualizador de targets.
from qiskit.visualization import plot_gate_map
plot_gate_map(backend, font_size=14)

from qiskit.visualization import plot_error_map
plot_error_map(backend)

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