Zum Hauptinhalt springen
Dieses Projekt befindet sich in der Beta-Phase. Einen Fehler gefunden? GitHub Issue erstellen. Frage oder Idee? In Discussions fragen — wir freuen uns über dein Feedback!
Wie ist die Übersetzungsqualität auf dieser Seite?

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:

Abbildung des folgenden Schaltkreises

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()
hinweis

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: a+8+a+8+a+8=100=3∗a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24

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'])
hinweis

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.

Die Ausgabe des Befehls draw_circuit_schedule_timing wird angezeigt.

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.

    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 q0
  • Der von zwei Barrieren eingeschlossene Bereich wird als Barrierenregion bezeichnet. Eine stretch-Variable darf nicht in mehreren Barrierenregionen verwendet werden.

    # 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))

    Illustration der vorherigen Codeausgabe

  • Stretch-Ausdrücke sind auf die Form X*stretch + Y beschränkt, wobei X und Y Gleitkomma- oder Ganzzahlkonstanten sind.

    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)
  • Stretch-Ausdrücke dürfen nur eine einzige stretch-Variable enthalten.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 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.

    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))