Pular para o conteúdo principal
Este projeto está em fase beta. Encontrou um bug? Abra uma issue no GitHub. Tem uma pergunta ou ideia? Pergunte no Discussions — adoraríamos receber seu feedback!
Como está a qualidade da tradução nesta página?

Executar circuitos dinâmicos

Os circuitos dinâmicos são ferramentas poderosas com as quais você pode medir qubits no meio de uma execução de circuito quântico e depois realizar operações de lógica clássica dentro do circuito, com base no resultado dessas medições no meio do circuito. Esse processo também é conhecido como feedforward clássico. Embora ainda estejamos no início de entender como melhor aproveitar os circuitos dinâmicos, a comunidade de pesquisa quântica já identificou vários casos de uso, como os seguintes:

Essas melhorias trazidas pelos circuitos dinâmicos, no entanto, vêm com trade-offs. As medições no meio do circuito e as operações clássicas normalmente têm tempo de execução mais longo do que as Gates de dois Qubits, e esse aumento no tempo pode anular os benefícios da redução da profundidade do circuito. Portanto, reduzir a duração das medições no meio do circuito é uma área prioritária de melhoria à medida que o IBM Quantum® lança a nova versão dos circuitos dinâmicos. Para outras restrições ao usar circuitos dinâmicos, veja a tabela de compatibilidade de recursos do Estimator ou Sampler.

A especificação OpenQASM 3 define várias estruturas de fluxo de controle, mas o IBM Quantum Compute Service atualmente suporta apenas a instrução condicional if. No Qiskit SDK, isso corresponde ao método if_test em QuantumCircuit. Este método retorna um gerenciador de contexto e é normalmente usado em uma instrução with. Este guia descreve como usar esta instrução condicional.

nota

Os exemplos de código neste guia usam a instrução de medição padrão para medições de meio-Circuit. No entanto, é recomendado que você use a instrução MidCircuitMeasure em vez disso, se o Backend suportar. Veja a seção de medições de meio-Circuit para detalhes.

Versões dos pacotes

O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos usar essas versões ou versões mais recentes.

qiskit[all]~=2.6.1
qiskit-ibm-runtime~=0.50.0

Encontrar Backends que suportam circuitos dinâmicos​

Para encontrar todos os Backends que sua conta pode acessar e que suportam circuitos dinâmicos, execute código como o seguinte. Este exemplo assume que você salvou suas credenciais de login. Você também pode especificar credenciais explicitamente ao inicializar sua conta do IBM Quantum Compute Service. Isso permitiria que você visualizasse os Backends disponíveis em uma instância ou tipo de plano específico, por exemplo.

Notas
  • Os Backends disponíveis para a conta dependem da instância especificada nas credenciais.
  • A nova versão dos circuitos dinâmicos agora está disponível para todos os usuários em todos os Backends. Veja o anúncio para mais detalhes.
# 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")
# This cell is hidden from users. It hides all those "...instance was not set..." warnings.
import warnings

warnings.filterwarnings("ignore", message=".*Instance was not set*")
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_kingston')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_phoenix')>, <IBMBackend('ibm_pittsburgh')>]

Medições de meio-Circuit​

Antes do qiskit-ibm-runtime v0.43.0, measure era a única instrução de medição no Qiskit. As medições no meio do circuito, no entanto, têm requisitos de ajuste diferentes das medições terminais (medições que ocorrem no final de um circuito). Por exemplo, você precisa considerar a duração da instrução ao ajustar uma medição no meio do circuito porque instruções mais longas causam circuitos mais ruidosos. Você não precisa considerar a duração da instrução para medições terminais porque não há instruções após as medições terminais.

nota

A instrução MidCircuitMeasure mapeia para a instrução measure_2 relatada nas supported_instructions do Backend. No entanto, measure_2 não é suportado em todos os Backends. Use service.backends(filters=lambda b: "measure_2" in b.supported_instructions) para encontrar Backends que o suportam. Novas medições podem ser adicionadas no futuro, mas isso não é garantido.

Método MidCircuitMeasure​

No qiskit-ibm-runtime v0.43.0, a instrução MidCircuitMeasure foi introduzida. Como o nome sugere, é uma nova instrução de medição otimizada para o meio do circuito nas QPUs IBM®. Embora você possa usar QuantumCircuit.measure para uma medição no meio do circuito, por causa de seu design, MidCircuitMeasure é normalmente uma melhor escolha. Por exemplo, ele adiciona menos sobrecarga ao seu circuito do que ao usar QuantumCircuit.measure.

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure

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

circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
Notas importantes
  • Deve haver pelo menos um registrador clássico para usar medições.

  • A primitiva Sampler requer medições de Circuit. Você pode adicionar medições de Circuit com a primitiva Estimator, mas elas são ignoradas.

Store​

Com o qiskit-ibm-runtime versão 0.47.0 ou posterior, você pode usar a instrução store para salvar o resultado de uma expressão clássica, se essa expressão for usada repetidamente. As operações são automaticamente paralelizadas, tornando seu código significativamente mais eficiente em tempo de execução.

Para mais informações, consulte o guia de feedforward clássico e fluxo de controle.

nota

Quando você usa store para salvar um valor em um registrador clássico em um Backend real, esse valor só é salvo na memória durante a execução e não é copiado nem retornado no resultado do job.

Por exemplo, no código a seguir, temp tem o mesmo valor que creg durante a execução, e o if_test funciona conforme o esperado. Mas depois que o job termina, o BitArray temp retornado no resultado do job não contém o valor de creg. Ou seja, job.result()[0].data.temp é 0.

creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...

Exemplo completo​

O código a seguir cria e executa um Circuit dinâmico no hardware IBM®.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager

# Legacy server-side primitive before qiskit-ibm-runtime v0.50.0
# from qiskit_ibm_runtime import Sampler

# Client-side primitive introduced in qiskit-ibm-runtime v0.50.0
from qiskit_ibm_runtime.executor_sampler import Sampler

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

# Create a dynamic circuit

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)

# Convert to an ISA circuit for the given backend

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

# Generate samplers for backend targets
sampler = Sampler(backend)

# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: dbjeaj0nceqs73ajs65g (DONE)

Limitações do Quantum Compute​

Esteja ciente das seguintes restrições ao executar circuitos dinâmicos no Quantum Compute.

  • Devido à memória física limitada na eletrônica de controle, há também um limite no número de instruções if e no tamanho de seus operandos. Este limite é uma função do número de broadcasts e do número de bits de broadcast em um job (não em um circuito).

    Ao processar uma condição if, os dados de medição precisam ser transferidos para a lógica de controle para fazer essa avaliação. Um broadcast é uma transferência de dados clássicos únicos, e os bits de broadcast correspondem ao número de bits clássicos sendo transferidos. Considere o seguinte:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    No exemplo de código anterior, os dois primeiros objetos if_test em c0 são considerados um único broadcast porque o conteúdo de c0 não mudou e, portanto, não precisa passar por um novo broadcast. O if_test em c1 é um segundo broadcast. O primeiro faz o broadcast de todos os três bits em c0 e o segundo de apenas um bit, totalizando quatro bits de broadcast.

    Atualmente, se você transmitir 60 bits de cada vez, o job pode ter aproximadamente 300 broadcasts. Se você transmitir apenas um bit de cada vez, no entanto, o job pode ter 2400 broadcasts.

  • O operando usado em uma instrução if_test deve ter 32 ou menos bits. Portanto, se você estiver comparando um ClassicalRegister inteiro, o tamanho desse ClassicalRegister deve ser 32 ou menos bits. Se você estiver comparando apenas um único bit de um ClassicalRegister, no entanto, esse ClassicalRegister pode ser de qualquer tamanho (já que o operando é apenas um bit).

    Por exemplo, o bloco de código "Não válido" não funciona porque cr tem mais de 32 bits. Você pode, no entanto, usar um registrador clássico mais largo que 32 bits se estiver testando apenas um bit, como mostrado no bloco de código "Válido".

    cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • Condicionais aninhados não são permitidos. Por exemplo, o seguinte bloco de código não funcionará porque tem um if_test dentro de outro if_test:

    c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • Ter reset ou medições dentro de condicionais não é suportado.

  • Operações aritméticas não são suportadas.

  • Veja a tabela de recursos OpenQASM 3 para determinar quais recursos do OpenQASM 3 são suportados com Qiskit e Quantum Compute.

  • Quando o OpenQASM 3 (em vez de QuantumCircuit) é usado como formato de entrada para passar Circuits para as primitivas do IBM Quantum, apenas as instruções que podem ser carregadas no Qiskit são suportadas. Operações clássicas, por exemplo, não são suportadas porque não podem ser carregadas no Qiskit. Veja Importar um programa OpenQASM 3 para o Qiskit para mais informações.

  • As instruções for, while e switch não são suportadas.

Usar circuitos dinâmicos com Estimator​

Como o Estimator não suporta circuitos dinâmicos, você pode usar o Sampler e construir seus próprios Circuits de medição em vez disso.

Para replicar o comportamento do Estimator, siga este processo:

  1. Agrupe os termos de todos os observáveis em uma partição. Isso pode ser feito usando a API PauliList, por exemplo.
    nota

    Você pode usar o atributo primitivo BitArray para calcular os valores esperados dos observáveis fornecidos.

  2. Execute um circuito de mudança de base por partição (qualquer mudança de base que precise ser feita para cada partição). Veja o módulo measurement_bases do utilitário addon de bases de medição para mais informações. Para mais informações, veja a documentação do pacote de utilitários addon Qiskit.
  3. Combine (some) os resultados de todas as partições.

Restrições​

Revise qualquer Tabela de compatibilidade de recursos para entender as restrições ao usar circuitos dinâmicos. Observe que a compatibilidade de recursos não depende da primitiva.

Próximos passos​

Recomendações