Feedforward clássico e fluxo de controle (circuitos dinâmicos)
Package versions
O código nesta página foi desenvolvido usando os seguintes requisitos. Recomendamos usar estas versões ou mais recentes.
qiskit[all]~=2.4.0
Os circuitos dinâmicos são ferramentas poderosas com as quais você pode medir qubits no meio da execução de um circuito quântico e, em seguida, realizar operações de lógica clássica dentro do circuito, com base nos resultados dessas medições intermediárias. Esse processo também é conhecido como feedforward clássico. Embora ainda seja cedo para 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 o 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 eficiente de longo alcance entre qubits no mesmo chip usando circuitos rasos
- Amostragem eficiente de circuitos similares a IQP
Instrução if
A instrução if é usada para realizar operações condicionalmente com base no valor de um bit ou registrador clássico.
No exemplo abaixo, aplicamos uma gate Hadamard a um qubit e o medimos. Se o resultado for 1, aplicamos uma gate X no qubit, que tem o efeito de revertê-lo para o estado 0. Em seguida, medimos o qubit novamente. O resultado da medição deve ser 0 com 100% de probabilidade.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")
# example output counts: {'0': 1024}
A instrução with pode receber um alvo de atribuição que é, por si só, um gerenciador de contexto que pode ser armazenado e posteriormente usado para criar um bloco else, que é executado sempre que o conteúdo do bloco if não é executado.
No exemplo abaixo, inicializamos registradores com dois qubits e dois bits clássicos. Aplicamos uma gate Hadamard ao primeiro qubit e o medimos. Se o resultado for 1, aplicamos uma gate Hadamard no segundo qubit; caso contrário, aplicamos uma gate X no segundo qubit. Por fim, medimos o segundo qubit também.
qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)
circuit.draw("mpl")
# example output counts: {'01': 260, '11': 272, '10': 492}
Além de condicionar em um único bit clássico, também é possível condicionar no valor de um registrador clássico composto por múltiplos bits.
No exemplo abaixo, aplicamos gates Hadamard a dois qubits e os medimos. Se o resultado for 01, ou seja, o primeiro qubit é 1 e o segundo qubit é 0, então aplicamos uma gate X a um terceiro qubit. Por fim, medimos o terceiro qubit. Observe que, para maior clareza, optamos por especificar o estado do terceiro bit clássico, que é 0, na condição if. No desenho do circuito, a condição é indicada pelos círculos nos bits clássicos sobre os quais a condição é aplicada. Um círculo sólido indica condicionamento em 1, enquanto um círculo sem preenchimento indica condicionamento em 0.
qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits
circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)
circuit.draw("mpl")
# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}
Expressões clássicas
O módulo de expressões clássicas do Qiskit qiskit.circuit.classical contém uma representação exploratória de operações em tempo de execução sobre valores clássicos durante a execução do circuito. Devido a limitações de hardware, apenas as condições QuantumCircuit.if_test() são atualmente suportadas.
O exemplo a seguir mostra que você pode usar o cálculo de paridade para criar um estado GHZ de n qubits usando circuitos dinâmicos. Primeiro, gere pares de Bell em qubits adjacentes. Em seguida, una esses pares usando uma camada de gates CNOT entre os pares. Você então mede o qubit alvo de todas as gates CNOT anteriores e reinicia cada qubit medido para o estado . Você aplica a cada site não medido para o qual a paridade de todos os bits precedentes é ímpar. Por fim, as gates CNOT são aplicadas aos qubits medidos para restabelecer o emaranhamento perdido na medição.
No cálculo de paridade, o primeiro elemento da expressão construída envolve elevar o objeto Python mr[0] a um nó Value (lift é usado para transformar objetos arbitrários em expressões clássicas). Isso não é necessário para mr[1] e o possível registrador clássico seguinte, pois eles são entradas para expr.bit_xor, e qualquer elevação necessária é feita automaticamente nesses casos. Tais expressões podem ser construídas em loops e outras estruturas.
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset
qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)
# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])
# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue
# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])
# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)
Próximas etapas
- Aprenda a implementar desacoplamento dinâmico preciso usando stretch.
- Use a visualização do cronograma do circuito para depurar e otimizar seus circuitos dinâmicos.
- Execute circuitos dinâmicos.