Skip to main content
IBM Quantum Platform

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:

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.

Note

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.

Notas
  • 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.

Note

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: ═══════════════════
                        
Avisos Importantes
  • É 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.

Note

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 if instruçõ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 if condiçã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_test objetos em c0 são considerados uma única transmissão, pois o conteúdo de não c0 sofreu alterações e, portanto, não precisa ser retransmitido. A transmissão if_test em questão c1 é uma segunda transmissão. O primeiro transmite os três bits, c0 e 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_test instrução deve ter 32 bits ou menos. Portanto, se você estiver comparando um inteiro ClassicalRegister, o tamanho desse inteiro ClassicalRegister deve ser de 32 bits ou menos. No entanto, se você estiver comparando apenas um único bit de um ClassicalRegister, esse ClassicalRegister pode 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 cr tem 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)):
          ...
  • 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_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)):
         ...
  • Não é permitido incluir reset ou medidas dentro de expressões condicionais.

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

  • Consulte a tabela de recursos do OpenQASM 3 para determinar quais recursos do OpenQASM 3 são compatíveis com o Qiskit e o Qiskit Runtime.

  • Quando se utiliza OpenQASM 3 (em vez de QuantumCircuit) 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 do OpenQASM 3 para o Qiskit para obter mais informações.

  • As forinstruções switch , 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:

  1. Agrupe os termos de todas as variáveis observáveis em uma partição. Isso pode ser feito usando o PauliList API, por exemplo.
    Note

    Você pode usar o BitArray atributo primitive para calcular os valores esperados das observáveis fornecidas.

  2. 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_bases utilitá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.
  3. 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

Recomendações
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.