Executar circuitos dinâmicos
Os circuitos dinâmicos são ferramentas poderosas que permitem medir qubits no meio da execução de um circuito quântico e, em seguida, realizar operações lógicas clássicas dentro do circuito, com base no resultado dessas medições realizadas no meio do circuito. Esse processo também é conhecido como feedforward clássico. Embora ainda estejamos nos estágios iniciais da compreensão sobre a melhor forma de aproveitar os circuitos dinâmicos, a comunidade de pesquisa quântica já identificou vários casos de uso, tais como os seguintes:
- Preparação eficiente de estados quânticos, tais como o estado GHZ, o estado W (para mais informações sobre o estado W, consulte também “Preparação de estados por circuitos rasos utilizando feed forward” ) e uma ampla classe de estados de produto matricial
- Entrelacamento eficiente de longo alcance entre qubits no mesmo chip por meio do uso de circuitos de profundidade reduzida
- Amostragem eficiente de circuitos do tipo IQP
Essas melhorias proporcionadas pelos circuitos dinâmicos, no entanto, vêm acompanhadas de desvantagens. As medições no meio do circuito e as operações clássicas costumam ter um tempo de execução mais longo do que as portas 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 da medição no meio do circuito é uma área prioritária de melhoria, à medida que a IBM Quantum® lança a nova versão dos circuitos dinâmicos. Para conhecer outras restrições ao usar circuitos dinâmicos, consulte a tabela de compatibilidade do Estimador ou do Amostrador.
A especificação do OpenQASM 3 define várias estruturas de fluxo de controle, mas o Qiskit Runtime atualmente suporta apenas a instrução if condicional. Em Qiskit SDK, isso corresponde ao método if_test em QuantumCircuit. Este método retorna um gerenciador de contexto e é normalmente usado em uma with instrução. Este guia descreve como usar essa instrução condicional.
Os exemplos de código neste guia utilizam a instrução de medição padrão para medições no meio do circuito. No entanto, recomenda-se que você use a MidCircuitMeasure instrução em vez disso, caso o backend seja compatível com ela. Consulte a seção “Medições no meio do circuito” para obter mais detalhes.
O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos usar essas versões ou versões mais recentes.
qiskit[all]~=2.5.0 qiskit-ibm-runtime~=0.47.0
Encontre backends que suportem circuitos dinâmicos
Para encontrar todos os backends aos quais sua conta tem acesso e que oferecem suporte a circuitos dinâmicos, execute um código como o seguinte. Este exemplo pressupõe que você tenha salvo suas credenciais de login. Você também pode especificar explicitamente as credenciais ao inicializar sua conta de serviço do Qiskit Runtime. Isso permitiria que você visualizasse os back-ends disponíveis em uma instância específica ou em um determinado tipo de plano, por exemplo.
- Os backends disponíveis para a conta dependem da instância especificada nas credenciais.
- A nova versão dos circuitos dinâmicos já está disponível para todos os usuários em todos os backends. Consulte o anúncio para obter mais detalhes.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)Output:
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_kingston')>]
Medições no meio do circuito
Antes do qiskit-ibm-runtimev0.43.0 , measure essa 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 nos terminais (medições realizadas no final de um circuito). Por exemplo, é preciso levar em conta a duração da instrução ao ajustar uma medição no meio do circuito, pois instruções mais longas geram mais ruído nos circuitos. Não é necessário levar em conta a duração das instruções nas medições finais, pois não há instruções após essas medições.
A MidCircuitMeasure instrução corresponde à measure_2 instrução relatada no arquivo. do backend supported_instructions. No entanto, não measure_2 é compatível com todos os backends. Use service.backends(filters=lambda b: "measure_2" in b.supported_instructions) para encontrar back-ends compatíveis. Novas medidas poderão ser adicionadas no futuro, mas isso não é garantido.
método MidCircuitMeasure
Em qiskit-ibm-runtime v0.43.0, a MidCircuitMeasure instrução foi apresentada. Como o nome sugere, trata-se de uma nova instrução de medição otimizada para execução em meio de circuito nas QPUs d IBM®. Embora seja possível usar QuantumCircuit.measure para uma medição no meio do circuito, devido ao seu design, MidCircuitMeasure é normalmente a melhor opção. Por exemplo, isso gera menos sobrecarga no seu circuito do que quando se usa 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))Output:
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
- É necessário haver pelo menos um registro clássico para utilizar as medições.
- A primitiva Sampler requer medições do circuito. É possível adicionar medidas de circuito com a primitiva Estimator, mas elas são ignoradas.
Store
A partir qiskit-ibm-runtime da versão 0.47.0, é possível usar a store instrução para salvar o resultado de uma expressão clássica, caso essa expressão venha a ser utilizada repetidamente. As operações são paralelizadas automaticamente, tornando seu código significativamente mais eficiente em tempo de execução.
Para obter mais informações, consulte o guia sobre alimentação direta clássica e fluxo de controle.
Quando você usa store para salvar um valor em um registro clássico em um backend real, esse valor é salvo apenas na memória durante a execução e não é copiado nem retornado no resultado da tarefa.
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 a conclusão da tarefa, o temp BitArray retornado no resultado da tarefa 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 circuito dinâmico em um hardware d 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()})"
)Output:
>>> Job ID: d9bjipbv6alc73ct567g (DONE)
Qiskit Runtime limitações
Esteja ciente das seguintes restrições ao executar circuitos dinâmicos no Qiskit Runtime.
-
Devido à memória física limitada dos componentes eletrônicos de controle, há também um limite para o número de
ifinstruções e o tamanho de seus operandos. Esse limite depende do número de transmissões e do número de bits transmitidos em um trabalho (não em um circuito).Ao processar uma
ifcondição, os dados de medição precisam ser transferidos para a lógica de controle a fim de realizar essa avaliação. Uma transmissão é uma transferência de dados clássicos únicos, e os bits transmitidos correspondem ao número de bits clássicos que estão 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
if_testobjetos emc0são considerados uma única transmissão, pois o conteúdo de nãoc0sofreu alterações e, portanto, não precisa ser retransmitido. A transmissãoif_testem questãoc1é uma segunda transmissão. O primeiro transmite os três bits,c0e o segundo transmite apenas um bit, totalizando quatro bits transmitidos.Atualmente, se você transmitir 60 bits por vez, o trabalho poderá ter aproximadamente 300 transmissões. No entanto, se você transmitir apenas um bit de cada vez, a tarefa poderá ter 2.400 transmissões.
-
O operando utilizado em uma
if_testinstrução deve ter 32 bits ou menos. Portanto, se você estiver comparando um inteiroClassicalRegister, o tamanho desse inteiroClassicalRegisterdeve ser de 32 bits ou menos. No entanto, se você estiver comparando apenas um único bit de umClassicalRegister, esseClassicalRegisterpode ter qualquer tamanho (já que o operando é de apenas um bit).Por exemplo, o bloco de código "Não válido" não funciona porque
crtem mais de 32 bits. No entanto, você pode usar um registro clássico com mais de 32 bits se estiver testando apenas um bit, conforme 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)): ...cr = ClassicalRegister(50) qr = QuantumRegister(50) circuit = QuantumCircuit(qr, cr) ... circ.measure(qr, cr) with circ.if_test((cr[5], 1)): ... -
Não são permitidas condições aninhadas. Por exemplo, o bloco de código a seguir não funcionará porque contém um
if_testdentro de outroif_test: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)): ... -
Não é permitido incluir
resetou medidas dentro de expressões condicionais. -
As operações aritméticas não são suportadas.
-
Consulte a tabela de recursos do
OpenQASM3 para determinar quais recursos doOpenQASM3 são compatíveis com o Qiskit e oQiskit Runtime. -
Quando se utiliza
OpenQASM 3(em vez deQuantumCircuit) como formato de entrada para passar circuitos para as primitivas d Qiskit Runtime, apenas são suportadas as instruções que podem ser carregadas no Qiskit. As operações clássicas, por exemplo, não são compatíveis, pois não podem ser carregadas no Qiskit. Consulte Importar um programa doOpenQASM3 para o Qiskit para obter mais informações. -
As
forinstruçõesswitch,while, e não são suportadas.
Use circuitos dinâmicos com o Estimator
Como o Estimator não suporta circuitos dinâmicos, você pode usar o Sampler e criar seus próprios circuitos de medição.
Para reproduzir o comportamento do Estimator, siga este procedimento:
- Agrupe os termos de todas as variáveis observáveis em uma partição. Isso pode ser feito usando o
PauliListAPI, por exemplo.NoteVocê pode usar o
BitArrayatributoprimitivepara calcular os valores esperados das observáveis fornecidas. - Execute um circuito de mudança de base por partição (conforme a mudança de base necessária para cada partição). Consulte o módulo
measurement_basesutilitário complementar “Bases de medição” para obter mais informações. Para obter mais informações, consulte a documentação do pacote de utilitários do complemento Qiskit. - Some os resultados de cada partição.
Restrições
Consulte qualquer tabela de compatibilidade de recursos para entender as restrições ao usar circuitos dinâmicos. Observe que a compatibilidade dos recursos não depende das primitivas.
Próximas etapas
- Saiba como implementar um desacoplamento dinâmico preciso usando o stretch.
- Consulte o guia clássico sobre alimentação direta e fluxo de controle.
- Use a visualização do cronograma do circuito para depurar e otimizar seus circuitos dinâmicos.
- Nem todas as funções são compatíveis com circuitos dinâmicos. Consulte a seção de compatibilidade de recursos do Sampler para obter mais detalhes.