Aufgeschobene Festlegung des Timings mit stretch
Die OpenQASM 3-Sprachspezifikation enthält einen stretch-Typ, mit dem du relative anstelle absoluter Zeitangaben für Operationen festlegen kannst. Die Unterstützung für stretch als Dauer für Delay-Instruktionen wurde in Qiskit v2.0.0 hinzugefügt. Der konkrete Wert einer stretch-Dauer wird zur Kompilierzeit aufgelöst, nachdem die genaue Dauer der kalibrierten Gates bekannt ist. Der Compiler versucht, die stretch-Dauer zu minimieren, wobei er Zeitbeschränkungen auf einem oder mehreren Qubits berücksichtigt. So kannst du Gate-Designs ausdrücken, wie zum Beispiel das gleichmäßige Verteilen von Gates (etwa zur Implementierung einer Echo-Entkopplungssequenz höherer Ordnung), das linksbündige Ausrichten einer Gate-Sequenz oder das Anwenden eines Gates für die Dauer eines Teilschaltkreises – ohne das genaue Timing zu kennen.
Beispiele
Dynamische Entkopplung
Ein häufiger Anwendungsfall von stretch ist es, dynamische Entkopplung auf ein im Leerlauf befindliches Qubit anzuwenden, während auf ein anderes Qubit bedingte Operationen angewendet werden.
Zum Beispiel können wir stretch verwenden, um eine XX-Sequenz zur dynamischen Entkopplung auf Qubit 1 anzuwenden, für die Dauer des bedingten Blocks auf Qubit 0, wie im folgenden Diagramm dargestellt:
Der entsprechende Schaltkreis würde wie folgt aussehen. Beachte, dass ein Paar von Barrieren benötigt wird, um die Grenzen dieses relativen Timings festzulegen.
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()
Ausrichtung der Zeitplanung
Dieses Beispiel verwendet stretch, um sicherzustellen, dass eine Sequenz von Gates zwischen zwei Barrieren linksbündig ausgerichtet ist, unabhängig von ihrer tatsächlichen Dauer:
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()
Wenn du stretch mit IBM Quantum Compute Service verwendest, wird jeder Rest, der aus einer stretch-Auflösung entsteht, zur ersten Verzögerung hinzugefügt, die den stretch verwendet.
Beispiel:
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)
Für den obigen Code ergibt sich ein Wert von 25 mit einem Rest von 1. Zu der ersten Verzögerung [a] wird der Rest addiert.
Stretch-Auflösungsgleichung:
Stretch-Werte im IBM Quantum Compute Service anzeigen
Der tatsächliche Wert einer stretch-Dauer wird zur Kompilierzeit aufgelöst, nach der Zeitplanung (dem Scheduling) des Schaltkreises. Wenn du einen Sampler-Job im IBM Quantum Compute Service ausführst, kannst du die aufgelösten stretch-Werte in den Job-Ergebnis-Metadaten einsehen. Die Unterstützung für stretch in Quantum Compute ist derzeit experimentell, daher musst du zunächst eine experimentelle Option aktivieren, um den Abruf zu ermöglichen, und dann wie folgt direkt auf die Daten aus den Metadaten zugreifen:
# 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'])
Obwohl die gesamte Schaltkreiszeit in den „compilation"-Metadaten zurückgegeben wird, ist dies NICHT die für die Abrechnung verwendete Zeit (QPU-Zeit).
Metadaten-Ausgabe verstehen
Die stretch_values-Metadaten geben die folgenden Informationen zurück:
-
Name: Der Name des angewendeten stretch.
-
Value: Der angestrebte Zielwert.
-
Remainder: Der Rest aus der Auflösung des stretch, der zur ersten Verzögerung hinzugefügt wird, die den stretch verwendet.
-
Expanded values: Wertegruppen, die den Beginn des stretch und seine Dauer angeben.
Beispiel
# 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()
Metadaten-Ausgabe
[{'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]]}
]
Die zurückgegebenen Dauerwerte hängen vom Zielwert und dem berechneten Rest ab. Zum Beispiel sind dies die für foo zurückgegebenen Dauern:
-
foo value+remainder(8+2 = 10) -
foo value* 3 (8 x 3 = 24) -
foo value* 2 (8 x 2 = 16)
Du kannst eine Visualisierung verwenden, um das Timing besser zu verstehen und zu überprüfen.
draw_circuit_schedule_timing(job.result()[0].metadata
['compilation']['scheduler_timing']['timing'])
Im folgenden Bild, basierend auf der Beispielausgabe, entspricht foo den Stretches auf Qubit 2. Die erste stretch-Verzögerung, die foo verwendet, beginnt am Ende des init_play (1365). Die stretch-Dauer beträgt 10, sodass diese Verzögerung endet, wenn das x-Gate beginnt (1365+10=1375). Die zweite und dritte stretch-Verzögerung lassen sich entsprechend interpretieren.

Verwende die Schieberegler unten, die Steuerungen oben (fahre mit der Maus über dein Ausgabebild, um diese einzublenden) und die Legende seitlich der Ausgabe, um die Ansicht anzupassen. Fahre mit der Maus über das Bild, um genaue Daten einzusehen.
Weitere Details findest du im Thema Schaltkreis-Timing visualisieren.
Einschränkungen von Quantum Compute
Die Unterstützung für stretch in Quantum Compute ist derzeit experimentell und hat folgende Einschränkungen:
-
Höchstens eine stretch-Variable pro Qubit-Menge zwischen Barrieren (implizit und explizit). Eine Qubit-Menge besteht aus einem oder mehreren Qubits; diese Mengen müssen sich gegenseitig ausschließen.
- Ungültig
- Gültig
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) -
Der von zwei Barrieren eingeschlossene Bereich wird als Barrierenregion bezeichnet. Eine stretch-Variable darf nicht in mehreren Barrierenregionen verwendet werden.
- Ungültig
- Gültig
# Stretch a is used in two barrier regionsa = 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 q1a = circuit.add_stretch("a")circuit.barrier((q0, q1))circuit.delay(a, q0)circuit.barrier(q2)circuit.delay(a, q0)circuit.barrier((q0, q1)) -
Stretch-Ausdrücke sind auf die Form
X*stretch + Ybeschränkt, wobeiXundYGleitkomma- oder Ganzzahlkonstanten sind.- Ungültig
- Gültig
a = circuit.add_stretch("a")b = circuit.add_stretch("b")c = circuit.add_stretch("c")# (a / b) * c is not supportedcircuit.delay(expr.mul(expr.div(a, b), c), q1)from qiskit.circuit import Durationa = circuit.add_stretch("a")circuit.delay(expr.add(expr.mul(a, 2), Duration.dt(3)), 0) -
Stretch-Ausdrücke dürfen nur eine einzige stretch-Variable enthalten.
- Ungültig
- Gültig
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) -
Stretch-Ausdrücke dürfen nicht zu negativen Verzögerungswerten aufgelöst werden. Der aktuelle Löser leitet keine Nicht-Negativitätsbeschränkungen ab.
- Ungültig
- Gültig
from qiskit.circuit import Durationcircuit.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))