Pular para o conteúdo principal

Mitigação de erros com a função IBM Circuit

Nota

As Funções Qiskit são um recurso experimental disponível apenas para usuários do IBM Quantum® Premium Plan, Flex Plan e On-Prem (via IBM Quantum Platform API) Plan. Elas estão em status de lançamento de pré-visualização e sujeitas a alterações.

Estimativa de uso: 26 minutos em um processador Eagle (NOTA: Isso é apenas uma estimativa. Seu tempo de execução pode variar.) Este tutorial apresenta um exemplo de construção e execução de um fluxo de trabalho usando a função IBM Circuit. Esta função recebe Primitive Unified Blocs (PUBs) como entradas e retorna valores de expectativa com mitigação de erros como saídas. Ela fornece um pipeline automatizado e personalizado para otimizar circuitos e executar em hardware quântico, permitindo que os pesquisadores se concentrem na descoberta de algoritmos e aplicações.

Visite a documentação para uma introdução às Funções Qiskit e aprenda como começar com a função IBM Circuit.

Contexto

Este tutorial considera um circuito geral de evolução temporal Trotterizado eficiente em hardware para o modelo de Ising de campo transversal 2D e calcula a magnetização global. Tal circuito é útil em diferentes domínios de aplicação, como física da matéria condensada, química e aprendizado de máquina. Para mais informações sobre a estrutura deste modelo, consulte Nature 618, 500–505 (2023).

A função IBM Circuit combina capacidades do serviço de transpilador Qiskit e do Qiskit Runtime Estimator para fornecer uma interface simplificada para executar circuitos. A função realiza transpilação, supressão de erros, mitigação de erros e execução de circuitos dentro de um único serviço gerenciado, permitindo que nos concentremos em mapear o problema para circuitos em vez de construir cada etapa do padrão nós mesmos.

Requisitos

Antes de iniciar este tutorial, certifique-se de ter o seguinte instalado:

  • Qiskit SDK v1.2 ou posterior (pip install qiskit)
  • Qiskit Runtime v0.28 ou posterior (pip install qiskit-ibm-runtime)
  • IBM Qiskit Functions Catalog client v0.0.0 ou posterior (pip install qiskit-ibm-catalog)
  • Qiskit Aer v0.15.0 ou posterior (pip install qiskit-aer)

Configuração

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog qiskit-ibm-runtime rustworkx
import rustworkx
from collections import defaultdict
from numpy import pi, mean

from qiskit_ibm_runtime import QiskitRuntimeService

from qiskit_ibm_catalog import QiskitFunctionsCatalog

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp

Passo 1: Mapear entradas clássicas para um problema quântico

  • Entrada: Parâmetros para criar o circuito quântico
  • Saída: Circuito abstrato e observáveis

Construir o circuito

O circuito que criaremos é um circuito de evolução temporal Trotterizado eficiente em hardware para o modelo de Ising de campo transversal 2D. Começamos selecionando um backend. As propriedades deste backend (ou seja, seu mapa de acoplamento) serão usadas para definir o problema quântico e garantir que ele seja eficiente em hardware.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)

Em seguida, obtemos o mapa de acoplamento do backend.

coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
layer_couplings = defaultdict(list)

Queremos ter cuidado em como projetamos as camadas do nosso circuito. Faremos isso colorindo as arestas do mapa de acoplamento (ou seja, agrupando as arestas disjuntas) e usaremos essa coloração para posicionar as portas de forma mais eficiente no circuito. Isso levará a um circuito mais raso com camadas de portas que podem ser executadas simultaneamente no hardware.

edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)

for edge_idx, color in edge_coloring.items():
layer_couplings[color].append(
coupling_graph.get_edge_endpoints_by_index(edge_idx)
)
layer_couplings = [
sorted(layer_couplings[i]) for i in sorted(layer_couplings.keys())
]

Em seguida, escrevemos uma função auxiliar simples que implementa o circuito de evolução temporal Trotterizado eficiente em hardware para o modelo de Ising de campo transversal 2D usando a coloração de arestas obtida acima.

def construct_trotter_circuit(
num_qubits: int,
num_trotter_steps: int,
layer_couplings: list,
barrier: bool = True,
) -> QuantumCircuit:
theta, phi = Parameter("theta"), Parameter("phi")
circuit = QuantumCircuit(num_qubits)

for _ in range(num_trotter_steps):
circuit.rx(theta, range(num_qubits))
for layer in layer_couplings:
for edge in layer:
if edge[0] < num_qubits and edge[1] < num_qubits:
circuit.rzz(phi, edge[0], edge[1])
if barrier:
circuit.barrier()

return circuit

Vamos escolher o número de qubits e passos de Trotter e então construir o circuito.

num_qubits = 100
num_trotter_steps = 2

circuit = construct_trotter_circuit(
num_qubits, num_trotter_steps, layer_couplings
)
circuit.draw("mpl", fold=-1)

Output of the previous code cell

Para comparar a qualidade da execução, precisamos compará-la com o resultado ideal. O circuito escolhido está além da simulação clássica de força bruta. Então, fixamos os parâmetros de todas as portas Rx no circuito em 00, e os de todas as portas Rzz em π\pi. Isso torna o circuito Clifford, o que possibilita realizar a simulação ideal e obter o resultado ideal para comparação. Neste caso, sabemos que o resultado será 1.0.

parameters = [0, pi]

Construir o observável

Primeiro, calculamos a magnetização global ao longo de z^\hat{z} para o problema de NN qubits: Mz=i=1NZi/NM_z = \sum_{i=1}^N \langle Z_i \rangle / N. Isso requer primeiro calcular a magnetização de sítio único Zi\langle Z_i \rangle para cada qubit ii, que é definida no código a seguir.

observables = []
for i in range(num_qubits):
obs = "I" * (i) + "Z" + "I" * (num_qubits - i - 1)
observables.append(SparsePauliOp(obs))

print(observables[0])
SparsePauliOp(['ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])

Passos 2 e 3: Otimizar problema para execução em hardware quântico e executar com a função IBM Circuit

  • Entrada: Circuito abstrato e observáveis
  • Saída: Valores de expectativa mitigados

Agora, podemos passar o circuito abstrato e os observáveis para a função IBM Circuit. Ela cuidará da transpilação e execução em hardware quântico para nós e retornará valores de expectativa mitigados. Primeiro, carregamos a função do IBM Qiskit Functions Catalog.

catalog = QiskitFunctionsCatalog(
token="<YOUR_API_KEY>"
) # Use the 44-character API_KEY you created and saved from the IBM Quantum Platform Home dashboard
function = catalog.load("ibm/circuit-function")

A função IBM Circuit recebe pubs, backend_name, bem como entradas opcionais para configurar transpilação, mitigação de erros, etc. Criamos o pub a partir do circuito abstrato, observáveis e parâmetros do circuito. O nome do backend deve ser especificado como uma string.

pubs = [(circuit, observables, parameters)]
backend_name = backend.name

Também podemos configurar as options para transpilação, supressão de erros e mitigação de erros. Configurações padrão serão usadas se não desejarmos especificá-las. A função IBM Circuit vem com opções comumente usadas para optimization_level, que controla quanto de otimização de circuito realizar, e mitigation_level, que especifica quanta supressão e mitigação de erros aplicar. Observe que o mitigation_level da função IBM Circuit é distinto do resilience_level usado no Qiskit Runtime Estimator. Para uma descrição detalhada dessas opções comumente usadas, bem como outras opções avançadas, visite a documentação da função IBM Circuit.

Neste tutorial, definiremos o default_precision, optimization_level: 3 e mitigation_level: 3, que ativarão o gate twirling e a Zero Noise Extrapolation (ZNE) via Probabilistic Error Amplification (PEA) além das configurações padrão de nível 1.

options = {
"default_precision": 0.011,
"optimization_level": 3,
"mitigation_level": 3,
}

Com as entradas especificadas, enviamos o job para a função IBM Circuit para otimização e execução.

job = function.run(backend_name=backend_name, pubs=pubs, options=options)

Passo 4: Pós-processar e retornar resultado no formato clássico desejado

  • Entrada: Resultados da função IBM Circuit
  • Saída: Magnetização global

Calcular a magnetização global

O resultado da execução da função tem o mesmo formato que o Estimator.

result = job.result()[0]

Obtemos os valores de expectativa mitigados e não mitigados deste resultado. Esses valores de expectativa representam a magnetização de sítio único ao longo da direção z^\hat{z}. Calculamos a média deles para chegar à magnetização global e comparamos com o valor ideal de 1.0 para esta instância do problema.

mitigated_expvals = result.data.evs
magnetization_mitigated = mean(mitigated_expvals)

print("mitigated:", magnetization_mitigated)

unmitigated_expvals = [
result.data.evs_extrapolated[i][0][1] for i in range(num_qubits)
]
magnetization_unmitigated = mean(unmitigated_expvals)

print("unmitigated:", magnetization_unmitigated)
mitigated: 0.9749883476088692
unmitigated: 0.7832977198447583

Pesquisa do tutorial

Por favor, responda a esta breve pesquisa para fornecer feedback sobre este tutorial. Suas percepções nos ajudarão a melhorar nossas ofertas de conteúdo e experiência do usuário.

Link para a pesquisa