Executar circuitos dinâmicos
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.4.0
qiskit-ibm-runtime~=0.46.1
Os circuitos dinâmicos são ferramentas poderosas com as quais você pode medir qubits no meio de uma execução de Circuit quântico e depois realizar operações de lógica clássica dentro do Circuit, com base no resultado dessas medições de meio-Circuit. Esse processo também é conhecido como retroalimentação clássica. Embora ainda estejamos nos primeiros dias 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:
- Preparação eficiente de estados quânticos, como estado GHZ, estado W (para mais informações sobre o estado W, consulte também "State preparation by shallow circuits using feed forward"), e uma ampla classe de estados de produto matricial
- Emaranhamento de longo alcance eficiente entre qubits no mesmo chip usando Circuits rasos
- Amostragem eficiente de circuitos do tipo IQP
Essas melhorias trazidas pelos circuitos dinâmicos, no entanto, vêm com trade-offs. As medições de meio-Circuit e as operações clássicas normalmente têm tempo de execução mais longo do que os Gates de dois Qubits, e esse aumento no tempo pode anular os benefícios da redução da profundidade do Circuit. Portanto, reduzir a duração das medições de meio-Circuit é uma área 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 Qiskit Runtime 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.
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.
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 de serviço do Qiskit Runtime. Isso permitiria que você visualizasse os Backends disponíveis em uma instância ou tipo de plano específico, por exemplo.
- 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 — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# 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_pittsburgh')>, <IBMBackend('ibm_kingston')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_boston')>]
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 de meio-Circuit, no entanto, têm requisitos de ajuste diferentes das medições terminais (medições que ocorrem no final de um Circuit). Por exemplo, você precisa considerar a duração da instrução ao ajustar uma medição de meio-Circuit porque instruções mais longas causam Circuits 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.
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 meio-Circuit nas QPUs IBM®. Embora você possa usar QuantumCircuit.measure para uma medição de meio-Circuit, por causa de seu design, MidCircuitMeasure é normalmente uma melhor escolha. Por exemplo, ele adiciona menos sobrecarga ao seu Circuit 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: ═══════════════════
- 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 retroalimentação clássica e fluxo de controle.
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 como esperado. Mas após o término do job, o BitArray de 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 SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager
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 = SamplerV2(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: d88cakp789is7391vq0g (DONE)
Limitações do Qiskit Runtime
Esteja ciente das seguintes restrições ao executar circuitos dinâmicos no Qiskit Runtime.
-
Devido à memória física limitada na eletrônica de controle, há também um limite no número de instruções
ife no tamanho de seus operandos. Este limite é uma função do número de broadcasts e do número de bits transmitidos em um job (não em um Circuit).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 transmitidos 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_testemc0são considerados um único broadcast porque o conteúdo dec0não mudou e, portanto, não precisa ser retransmitido. Oif_testemc1é um segundo broadcast. O primeiro transmite todos os três bits emc0e o segundo transmite apenas um bit, totalizando quatro bits transmitidos.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_testdeve ter 32 ou menos bits. Portanto, se você estiver comparando umClassicalRegisterinteiro, o tamanho desseClassicalRegisterdeve ser 32 ou menos bits. Se você estiver comparando apenas um único bit de umClassicalRegister, no entanto, esseClassicalRegisterpode 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
crtem 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".- Não válido
- Válido
cr = ClassicalRegister(50)qr = QuantumRegister(50)circuit = QuantumCircuit(qr, cr)...circ.measure(qr, cr)with circ.if_test((cr, 15)):...cr = ClassicalRegister(50)qr = QuantumRegister(50)circuit = QuantumCircuit(qr, cr)...circ.measure(qr, cr)with circ.if_test((cr[5], 1)):... -
Condicionais aninhados não são permitidos. Por exemplo, o seguinte bloco de código não funcionará porque tem um
if_testdentro de outroif_test:- Não válido
- Válido
c1 = ClassicalRegister(1, "c1")c2 = ClassicalRegister(2, "c2")...with circ.if_test((c1, 1)):with circ.if_test(c2, 1)):...cr = ClassicalRegister(2)...with circuit.if_test((cr, 0b11)):... -
Ter
resetou 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 no Qiskit e no Qiskit Runtime.
-
Quando o OpenQASM 3 (em vez de
QuantumCircuit) é usado como formato de entrada para passar Circuits para as primitivas do Qiskit Runtime, 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,whileeswitchnã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:
- Agrupe os termos de todos os observáveis em uma partição. Isso pode ser feito usando a API
PauliList, por exemplo.notaVocê pode usar o atributo primitivo
BitArraypara calcular os valores esperados dos observáveis fornecidos. - Execute um Circuit 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_basesdo utilitário de addon de Medição para mais informações. Para mais informações, veja a documentação do pacote de utilitários addon Qiskit. - Some novamente os resultados para cada partição.
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
- Aprenda como implementar desacoplamento dinâmico preciso usando stretch.
- Revise o guia de retroalimentação clássica e fluxo de controle.
- Use a visualização de agendamento de Circuit para depurar e otimizar seus circuitos dinâmicos.
- Nem todas as funções são compatíveis com circuitos dinâmicos. Verifique a seção de compatibilidade de recursos para Sampler ou Executor para detalhes.