Risoluzione differita del timing con stretch
La specifica del linguaggio OpenQASM 3 contiene un tipo stretch con cui puoi specificare il timing relativo delle operazioni anziché il timing assoluto. Il supporto per stretch come durate per le istruzioni Delay è stato aggiunto in Qiskit v2.0.0. Il valore concreto di una durata stretch viene risolto in fase di compilazione, dopo che sono note le durate esatte dei gate calibrati. Il compilatore cerca di minimizzare la durata stretch, tenendo conto dei vincoli di timing su uno o più qubit. Puoi quindi esprimere design di gate come la spaziatura uniforme dei gate (ad esempio, per implementare una sequenza di echo decoupling di ordine superiore), l'allineamento a sinistra di una sequenza di gate, oppure l'applicazione di un gate per la durata di un sotto-circuito, senza conoscere il timing esatto.
Esempi​
Dynamical decoupling​
Un caso d'uso comune di stretch è applicare il dynamical decoupling a un qubit inattivo mentre un altro qubit è sottoposto a operazioni condizionali.
Ad esempio, possiamo usare stretch per applicare una sequenza di dynamical decoupling XX al qubit 1, per la durata del blocco condizionale applicato al qubit 0, come illustrato nel diagramma seguente:
Il circuito corrispondente avrà il seguente aspetto. Nota che è necessaria una coppia di barrier per definire i confini di questo timing relativo.
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()
Allineamento della schedulazione​
Questo esempio usa stretch per garantire che una sequenza di gate tra due barrier sia allineata a sinistra, indipendentemente dalle loro durate effettive:
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()
Quando si usa stretch con Qiskit Runtime, qualsiasi resto risultante dalla risoluzione di uno stretch viene aggiunto al primo delay che usa lo stretch.
Esempio:
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)
Il codice precedente si risolve in un valore di 25 con un resto di 1. Al primo delay[a] verrà aggiunto il resto.
Equazione di risoluzione dello stretch:
Visualizzare i valori stretch in Qiskit Runtime​
Il valore effettivo di una durata stretch viene risolto in fase di compilazione, dopo la schedulazione del circuito. Quando si esegue un job Sampler in Qiskit Runtime, puoi visualizzare i valori stretch risolti nei metadati del risultato del job. Il supporto per stretch in Qiskit Runtime è attualmente sperimentale, quindi devi prima impostare un'opzione sperimentale per abilitarne il recupero, quindi accedere ai dati direttamente dai metadati come segue:
# 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'])
Sebbene il tempo totale del circuito venga restituito nei metadati "compilation", questo NON è il tempo utilizzato per la fatturazione (quantum time).
Comprendere l'output dei metadati​
I metadati stretch_values restituiscono le seguenti informazioni:
- Name: Il nome dello stretch applicato.
- Value: Il valore obiettivo richiesto.
- Remainder: Il resto dalla risoluzione dello stretch, che viene aggiunto al primo delay che usa lo stretch.
- Expanded values: Insiemi di valori che specificano l'inizio dello stretch e la sua durata.
Esempio​
# 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()
Output dei metadati​
[{'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]]}
]
I valori restituiti per la durata dipendono dal valore obiettivo e dal resto calcolato. Ad esempio, queste sono le durate restituite per foo:
foo value+remainder(8+2 = 10)foo value* 3 (8 x 3 = 24)foo value* 2 (8 x 2 = 16)
Puoi usare una visualizzazione per comprendere e verificare il timing.
draw_circuit_schedule_timing(job.result()[0].metadata['compilation']['scheduler_timing']['timing'])
Nell'immagine seguente, basata sull'output di esempio, foo corrisponde agli stretch sul qubit 2. Il primo stretch delay che usa foo inizia alla fine dell'init_play (1365). La durata dello stretch è 10, quindi quel delay termina quando inizia il gate x (1365+10=1375). Puoi interpretare il secondo e il terzo stretch in modo analogo.

Usa i cursori in basso, i controlli in alto (passa il cursore sull'immagine di output per visualizzarli) e la legenda sul lato dell'output per personalizzare la vista. Passa il cursore sull'immagine per visualizzare i dati esatti.
Per tutti i dettagli, consulta l'argomento Visualizzare il timing del circuito.
Limitazioni di Qiskit Runtime​
Il supporto per stretch in Qiskit Runtime è attualmente sperimentale e presenta i seguenti vincoli:
-
Al massimo una variabile stretch per insieme di qubit tra barrier (implicite ed esplicite). Un insieme di qubit è composto da uno o più qubit; questi insiemi devono essere mutuamente esclusivi.
- Non valido
- Valido
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) -
L'area delimitata da un insieme di barrier è chiamata regione di barrier. Una variabile stretch non può essere usata in più regioni di barrier.
- Non valido
- Valido
# 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))
-
Le espressioni stretch sono limitate a quelle della forma
X*stretch + YdoveXeYsono costanti in virgola mobile o intere.- Non valido
- Valido
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) -
Le espressioni stretch possono includere una sola variabile stretch.
- Non valido
- Valido
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) -
Le espressioni stretch non possono risolversi in valori di delay negativi. Il solver attuale non deduce i vincoli di non negatività .
- Non valido
- Valido
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))