Resolução de temporização diferida usando stretch
A especificação da linguagem OpenQASM 3 contém um tipo stretch com o qual você pode especificar a temporização relativa de operações em vez de uma temporização absoluta. O suporte a stretch como durações para instruções Delay foi adicionado no Qiskit v2.0.0. O valor concreto de uma duração stretch é resolvido em tempo de compilação, após as durações exatas dos Gates calibrados serem conhecidas. O compilador tenta minimizar a duração do stretch, sujeita a restrições de temporização em um ou mais Qubits. Você pode então expressar designs de Gate como espaçamento uniforme de Gates (por exemplo, para implementar uma sequência de desacoplamento de eco de ordem superior), alinhamento à esquerda de uma sequência de Gates, ou aplicação de um Gate pela duração de algum sub-Circuit, sem precisar conhecer a temporização exata.
Exemplos
Desacoplamento dinâmico
Um caso de uso comum de stretch é aplicar desacoplamento dinâmico a um Qubit ocioso enquanto outro Qubit está passando por operações condicionais.
Por exemplo, podemos usar stretch para aplicar uma sequência de desacoplamento dinâmico XX ao Qubit 1, pela duração do bloco condicional aplicado ao Qubit 0, como ilustrado pelo seguinte diagrama:
O Circuit correspondente seria como o seguinte. Observe que um par de barreiras é necessário para definir os limites desta temporização relativa.
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits
# Add barriers to define the boundaries
circuit.barrier()
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q0)
with else_:
circuit.x(q0)
# Apply an XX DD sequence with stretch on qubit 1
s = circuit.add_stretch("s")
circuit.delay(s, q1)
circuit.x(q1)
circuit.delay(expr.mul(s, 2), q1)
circuit.x(q1)
circuit.delay(s, q1)
circuit.barrier()
Alinhamento de escalonamento
Este exemplo usa stretch para garantir que uma sequência de Gates entre duas barreiras seja alinhada à esquerda, quaisquer que sejam suas durações reais:
from qiskit import QuantumCircuit
from numpy import pi
qc = QuantumCircuit(5)
qc.barrier()
qc.cx(0, 1)
qc.u(pi/4, 0, pi/2, 2)
qc.cx(3, 4)
a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")
# Use the stretches as Delay duration.
qc.delay(a, [0, 1])
qc.delay(b, 2)
qc.delay(c, [3, 4])
qc.barrier()
Ao usar stretch com o Qiskit Runtime, qualquer resto resultante de uma resolução de stretch é adicionado ao primeiro delay que usa o stretch.
Exemplo:
a = circuit.add_stretch("a")
circuit.barrier(q0, q1)
circuit.delay(100, q0)
circuit.delay(a, q1) # resolve to 26
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.barrier(q0, q1)
O código acima resolve para um valor de 25 com um resto de 1. O primeiro delay[a] terá o resto adicionado.
Equação de resolução do stretch:
Ver valores de stretch no Qiskit Runtime
O valor real de uma duração stretch é resolvido em tempo de compilação, após o Circuit ser escalonado. Ao executar um job do Sampler no Qiskit Runtime, você pode visualizar os valores de stretch resolvidos nos metadados do resultado do job. O suporte a stretch no Qiskit Runtime é atualmente experimental, portanto você precisa primeiro definir uma opção experimental para habilitar sua recuperação e, em seguida, acessar os dados diretamente dos metadados da seguinte forma:
# Enable stretch value retrieval.
sampler.options.experimental = {
"execution": {
"stretch_values": True,
"scheduler_timing": True,
},
}
# Access the stretch values from the metadata.
job_result = job.result()
circuit_stretch_values = job_result[0].metadata["compilation"]["stretch_values"]
# Visualize the timing.
# Use the sliders at the bottom, the controls at the top, and the legend on the side
# of the output to customize the view.
draw_circuit_schedule_timing(ob.result()[0].metadata['compilation']['scheduler_timing']['timing'])
Embora o tempo total do Circuit seja retornado nos metadados de "compilation", este NÃO é o tempo usado para faturamento (tempo quântico).
Entender a saída dos metadados
Os metadados stretch_values retornam as seguintes informações:
- Name: O nome do stretch aplicado.
- Value: O valor de meta solicitado.
- Remainder: O resto da resolução do stretch, que é adicionado ao primeiro delay que usa o stretch.
- Expanded values: Conjuntos de valores que especificam o início do stretch e sua duração.
Exemplo
# Define the circuit
circuit = QuantumCircuit(4)
foo = circuit.add_stretch("foo")
bar = circuit.add_stretch("bar")
circuit.barrier()
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.delay(foo, 2)
circuit.x(2)
# 3*foo
circuit.delay(expr.mul(3, foo), 2)
circuit.x(2)
# 2*foo
circuit.delay(expr.mul(2, foo), 2)
circuit.delay(bar, 3)
circuit.x(3)
circuit.delay(bar, 3)
circuit.measure_all()
Saída dos metadados
[{'name': 'bar',
'value': 29,
'remainder': 1,
'expanded_values': [[1365, 30], [1404, 29]]},
{'name': 'foo',
'value': 8,
'remainder': 2,
'expanded_values': [[1365, 10], [1384, 24], [1417, 16]]}
]
Os valores retornados para a duração dependem do valor de meta e do resto calculado. Por exemplo, estas são as durações retornadas para foo:
foo value+remainder(8+2 = 10)foo value* 3 (8 x 3 = 24)foo value* 2 (8 x 2 = 16)
Você pode usar uma visualização para ajudar a entender e verificar a temporização.
draw_circuit_schedule_timing(job.result()[0].metadata['compilation']['scheduler_timing']['timing'])
Na imagem a seguir, com base na saída do exemplo, foo corresponde aos stretches no Qubit 2. O primeiro stretch delay que usa foo começa ao final do init_play (1365). A duração do stretch é 10, portanto esse delay termina quando o Gate x começa (1365+10=1375). Você pode interpretar o segundo e o terceiro stretches de forma similar.

Use os controles deslizantes na parte inferior, os controles na parte superior (passe o cursor sobre a imagem de saída para revelá-los) e a legenda ao lado da saída para personalizar a visualização. Passe o cursor sobre a imagem para ver os dados exatos.
Para detalhes completos, consulte o tópico Visualizar temporização do Circuit.
Limitações do Qiskit Runtime
O suporte a stretch no Qiskit Runtime é atualmente experimental e possui as seguintes restrições:
-
No máximo uma variável stretch por conjunto de Qubits entre barreiras (implícitas e explícitas). Um conjunto de Qubits é composto por um ou mais Qubits; esses conjuntos devem ser mutuamente exclusivos.
- Inválido
- Válido
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(a, (q0, q1))
circuit.delay(b, q0) # Invalid because 2 stretches are applied on q0a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(a, (q0, q1))
circuit.delay(b, q2) -
A área delimitada por um conjunto de barreiras é chamada de região de barreira. Uma variável stretch não pode ser usada em múltiplas regiões de barreira.
- Inválido
- Válido
# Stretch a is used in two barrier regions
a = circuit.add_stretch("a")
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier((q0, q1))
# Stretch a is used inside a barrier region that is on q0 and q1
a = circuit.add_stretch("a")
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier(q2)
circuit.delay(a, q0)
circuit.barrier((q0, q1))
-
Expressões stretch são limitadas àquelas da forma
X*stretch + Y, ondeXeYsão constantes de ponto flutuante ou inteiras.- Inválido
- Válido
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
c = circuit.add_stretch("c")
# (a / b) * c is not supported
circuit.delay(expr.mul(expr.div(a, b), c), q1)from qiskit.circuit import Duration
a = circuit.add_stretch("a")
circuit.delay(expr.add(expr.mul(a, 2), Duration.dt(3)), 0) -
Expressões stretch só podem incluir uma única variável stretch.
- Inválido
- Válido
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(expr.add(a, b), 0)a = circuit.add_stretch("a")
circuit.delay(expr.add(a, a), 0) -
Expressões stretch não podem resolver para valores de delay negativos. O solucionador atual não infere restrições de não-negatividade.
- Inválido
- Válido
from qiskit.circuit import Duration
circuit.barrier((q0, q1))
circuit.delay(20, q1)
# The length of this barrier region is 20dt, meaning the
# equation for solving stretch 'a' is a + 40dt = 20dt, giving a = -20dt.
circuit.delay(expr.add(a, Duration.dt(40)), q0)
circuit.barrier((q0, q1))circuit.barrier((q0, q1))
circuit.delay(20, q1)
circuit.delay(a, q0)
circuit.barrier((q0, q1))