Skip to main content
IBM Quantum Platform

Résolution différée de la chronologie grâce à l'étirement

La spécification du langage 3 de l' OpenQASM contient un stretch type qui permet de spécifier la synchronisation relative des opérations plutôt que la synchronisation absolue. La prise en charge stretch des durées pour Delay les instructions a été ajoutée dans Qiskit v2.0.0. La valeur concrète d'une durée d'étirement est déterminée au moment de la compilation, une fois que la durée exacte des portes calibrées est connue. Le compilateur tente de minimiser la durée d'étirement, sous réserve des contraintes de synchronisation sur un ou plusieurs qubits. Vous pouvez alors exprimer des conceptions de portes telles que l'espacement régulier des portes (par exemple, pour mettre en œuvre une séquence de découplage d'écho d'ordre supérieur), l'alignement à gauche d'une séquence de portes ou l'application d'une porte pendant la durée d'un sous-circuit, sans connaître le timing exact.


Exemples

Découplage dynamique

Un cas d'utilisation courant de stretch consiste à appliquer un découplage dynamique à un qubit au repos pendant qu'un autre qubit subit des opérations conditionnelles.

Par exemple, nous pouvons utiliser stretch pour appliquer une séquence de découplage dynamique XX au qubit 1, pendant la durée du bloc conditionnel appliqué au qubit 0, comme illustré par le diagramme suivant :

Image illustrant le circuit suivant

Le circuit correspondant ressemblerait à ce qui suit. Il convient de noter qu'une paire de barrières est nécessaire pour définir les limites de cette synchronisation relative.

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

Alignement des calendriers

Cet exemple utilise stretch pour s'assurer qu'une séquence de portes entre deux barrières est alignée à gauche, quelle que soit leur durée réelle :

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

Lors de l'utilisation de cette fonctionnalité avec stretch le service Compute d' IBM Quantum, tout reste résultant d'une résolution d'étirement est ajouté au premier délai utilisant cet étirement.

Exemple :

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)

Le code ci-dessus donne une valeur de 25 avec un reste de 1. Le premier délai[a] se verra ajouter le reste.

Équation de la résolution d'étirement : a+8+a+8+a+8=100=3a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24


Afficher les valeurs d'étirement dans le service de calcul d' IBM Quantum

La valeur réelle de la durée d'un intervalle est déterminée au moment de la compilation, une fois que le circuit a été planifié. Lorsque vous exécutez une tâche Sampler dans le service de calcul d' IBM Quantum, vous pouvez consulter les valeurs d'étirement déterminées dans les métadonnées des résultats de la tâche. La prise en charge de dans stretch Quantum Compute est actuellement à un stade expérimental; vous devez donc d'abord activer une option expérimentale pour permettre sa récupération, puis accéder aux données directement à partir des métadonnées comme suit :

# 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'])
Note

Bien que la durée totale d'exécution soit indiquée dans les métadonnées de « compilation », il ne s'agit PAS de la durée prise en compte pour la facturation (temps de QPU).

Comprendre la sortie des métadonnées

Les stretch_values métadonnées renvoient les informations suivantes :

  • Nom : Nom de l'étirement appliqué.
  • Valeur : la valeur cible demandée.
  • Reste : le reste résultant de la résolution de l'étirement, qui est ajouté au premier délai utilisant l'étirement.
  • Valeurs étendues : ensembles de valeurs qui spécifient le début de l'étirement et sa durée.

Exemple

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

Sortie des métadonnées

 [{'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]]}
  ]

Les valeurs renvoyées pour la durée dépendent de la valeur cible et du reste calculé. Par exemple, voici les durées renvoyées pour foo :

  • foo value + remainder (8+2 = 10)
  • foo value * 3 (8 x 3 = 24)
  • foo value * 2 (8 x 2 = 16)

Vous pouvez utiliser une visualisation pour aider à comprendre et vérifier le timing.

draw_circuit_schedule_timing(job.result()[0].metadata
    ['compilation']['scheduler_timing']['timing'])

Dans l'image suivante, basée sur l'exemple de sortie, foo correspond aux étirements sur Qubit 2. Le premier délai d'étirement qui utilise foo commence à la fin du init_play (1365). La durée d'extension est de 10, de sorte que le retard prend fin lorsque la x porte commence (1365+10=1375). Vous pouvez interpréter les deuxième et troisième étirements de la même manière.

La sortie de la commande draw_circuit_schedule_timing est affichée.

Utilisez les curseurs en bas, les commandes en haut (survolez votre image de sortie pour les afficher) et la légende sur le côté de la sortie pour personnaliser l'affichage. Passez la souris sur l'image pour afficher les données exactes.

Pour plus de détails, consultez la rubrique « Visualisation du timing des circuits ».


Limites de l'informatique quantique

La prise en charge de stretch dans Quantum Compute est actuellement à l'état expérimental et est soumise aux contraintes suivantes :

  • Au plus une variable d'étirement par ensemble de qubits entre les barrières (implicites et explicites). Un ensemble de qubits est un ou plusieurs qubits; ces ensembles doivent s'exclure mutuellement.

    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
  • La zone entourée d'un ensemble de barrières est appelée région à barrières. Une variable d'étirement ne peut pas être utilisée dans plusieurs régions de barrière.

    # 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 du résultat du code
    précédent : utilisation incorrecte de la fonction stretch dans les zones de barrière
  • Les expressions d'étirement sont limitées à celles de la forme X*stretch + YX et Y sont des constantes à virgule flottante ou des constantes entières.

    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)
  • Les expressions d'étirement ne peuvent inclure qu'une seule variable d'étirement.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 0)
  • Les expressions d'étirement ne peuvent pas se résoudre à des valeurs de retard négatives. Le solveur actuel ne déduit pas les contraintes de non-négativité.

    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))
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.