Pular para o conteúdo principal

Hardware

nota

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 — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
import statistics

from qiskit_ibm_runtime import QiskitRuntimeService

2. Backend e Target

O Qiskit oferece uma API para obter informações, tanto estáticas quanto dinâmicas, sobre um dispositivo quântico. Usamos uma instância de Backend para interagir com um dispositivo, que inclui uma instância de Target — um modelo de máquina abstrata que resume as características relevantes, 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 de Recursos de computação na IBM Quantum® Platform. Primeiro, criamos uma instância de backend para um dispositivo de interesse. No exemplo 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 conforme necessário.

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 por conta própria, mas o código foi adicionado abaixo para você conferir.

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

Saída da célula de código anterior

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 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 compreendido as estruturas de dados, estamos prontos para calcular os erros medianos para as portas 'sx' e 'ecr'. Mais uma vez, compare os resultados com os exibidos 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)

Saída da célula de código anterior

from qiskit.visualization import plot_error_map

plot_error_map(backend)

Saída da célula de código anterior

# Check Qiskit version
import qiskit

qiskit.__version__
'2.0.2'