Aller au contenu principal

Visualiser le timing des circuits

Versions des packages

Le code sur cette page a été développé en utilisant les exigences suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

Bien que le dessinateur de chronologie intégré à Qiskit soit utile pour les circuits statiques, il pourrait ne pas refléter avec précision le timing des circuits dynamiques en raison d'opérations implicites telles que la diffusion et la détermination de branche. Dans le cadre de la prise en charge des circuits dynamiques, Qiskit Runtime renvoie les informations précises de timing des circuits dans les résultats du job lorsqu'elles sont demandées.

Notes
  • Il s'agit d'une fonction expérimentale. Elle est en statut de version préliminaire et est donc sujette à changement.
  • Cette fonction s'applique uniquement aux jobs Qiskit Runtime Sampler.
  • Bien que le temps total du circuit soit renvoyé dans les métadonnées de « compilation », ce n'est PAS le temps utilisé pour la facturation (temps QPU).

Activer la récupération des données de timing

Pour activer la récupération des données de timing, définis l'indicateur expérimental scheduler_timing à True lors de l'exécution du job de primitive.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

sampler = SamplerV2(backend)
sampler.options.experimental = {
"execution": {
"scheduler_timing": True,
},
}

sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

Accéder aux données de timing du circuit

Lorsqu'elles sont demandées, les données de timing du circuit pour chaque PUB sont renvoyées dans les métadonnées des résultats du job, sous ["compilation"]["scheduler_timing"]["timing"]. Ce champ contient les informations de timing brutes. Pour afficher les informations de timing, utilise l'outil de visualisation intégré, comme décrit dans la section Visualiser les timings.

Utilise le code suivant pour accéder aux données de timing du circuit pour le premier PUB :

job_result = sampler_job.result()
circuit_schedule = job_result[0].metadata["compilation"]["scheduler_timing"]
circuit_schedule_timing = circuit_schedule["timing"]

Comprendre les données de timing brutes

Bien que la visualisation des données de timing du circuit à l'aide de la méthode draw_circuit_schedule_timing soit le cas d'utilisation le plus courant, il peut être utile de comprendre la structure des données de timing brutes renvoyées. Cela pourrait t'aider, par exemple, à extraire des informations par programmation.

Les données de timing renvoyées dans ["compilation"]["scheduler_timing"]["timing"] sont une liste de chaînes. Chaque chaîne représente une seule instruction sur un canal et est séparée par des virgules dans les types de données suivants :

  • Branch - Détermine si l'instruction se trouve dans un flux de contrôle (then/else) ou une branche principale.
  • Instruction - Le gate et le qubit sur lequel opérer.
  • Channel - Le canal auquel l'instruction est affectée. Il peut être l'un des suivants :
    • Qubit x - Le canal de commande pour le qubit x.
    • AWGRx_y (générateur de forme d'onde arbitraire en lecture) - Utilisé par les canaux de lecture pour communiquer lors de la mesure des qubits. Les arguments x et y correspondent respectivement à l'ID de l'instrument de lecture et au numéro de qubit.
  • T0 - L'heure de début de l'instruction dans le programme complet
  • Duration - La durée de l'instruction, en unités de dt secondes, où 1 dt = 1 cycle de planification. Tu peux trouver la valeur dt d'un Backend en utilisant backend.dt.
  • Pulse - Le type d'opération d'impulsion utilisée.

Exemple :

main,barrier,Qubit 0,7,0,barrier # A barrier on the main branch on qubit 0 at time 7 with 0 duration
main,reset_0,Qubit 0,7,64,play # A reset instruction on the main branch on qubit 0 at time 7 with duration 64 and a play operation
...

Visualiser les timings

Avec qiskit-ibm-runtime v0.43.0 ou ultérieur, tu peux visualiser les timings des circuits. Pour visualiser les timings, tu dois d'abord convertir les métadonnées du résultat en fig en utilisant la méthode draw_circuit_schedule_timing. Cette méthode renvoie une figure plotly, que tu peux afficher directement, enregistrer dans un fichier, ou les deux. Pour plus d'informations sur les commandes plotly à utiliser, consulte fig.show() et fig.write_image("<path.format>").

from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing

# Create a figure from the metadata
fig = draw_circuit_schedule_timing(
circuit_schedule=circuit_schedule_timing,
included_channels=None,
filter_readout_channels=False,
filter_barriers=False,
width=1000,
)

# Uncomment the following line to display the figure
# fig.show(renderer="notebook")

# Save to a file
# fig.write_html("scheduler_timing.html")

Survoler la sortie affiche des informations telles que le début, la fin et la durée.

Comprendre la figure générée

L'image des données de timing du circuit générée par draw_circuit_schedule_timing transmet les informations suivantes :

  • L'axe X est le temps en unités de dt secondes, où 1 dt = 1 cycle de planification. Tu peux trouver la valeur dt d'un Backend en utilisant backend.dt.
  • L'axe Y est le canal (pense aux canaux comme des instruments qui émettent des impulsions).
    • Receive channel - Le seul canal qui n'est pas un instrument en soi. C'est une instruction jouée sur tous les canaux qui font partie d'une procédure de communication avec le hub à ce moment-là.
    • Qubit x - Le canal de commande pour le qubit x.
    • AWGRx_y (générateur de forme d'onde arbitraire en lecture) - Utilisé par les canaux de lecture pour communiquer lors de la mesure des qubits. Les arguments x et y correspondent respectivement à l'ID de l'instrument de lecture et au numéro de qubit.
    • Hub - Contrôle la diffusion.

De plus, chaque instruction a le format X_Y, où X est le nom de l'instruction et Y est le type d'impulsion. Un type play applique des impulsions de contrôle, et un type capture enregistre l'état du qubit. Tu peux également survoler chaque instruction pour obtenir plus de détails. Par exemple, la figure précédente montre une impulsion de contrôle pour le gate X appliqué au qubit 10 à 1161 dt.

Exemple de bout en bout

Cet exemple te montre comment activer l'option, obtenir les informations de timing du circuit depuis les métadonnées et les afficher sous forme d'image.

D'abord, configure l'environnement, définis les circuits et convertis-les en circuits ISA, et définis et exécute les jobs.

from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Create a dynamic circuit

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)

# Convert to an ISA circuit for the given backend

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

# Generate samplers for backend targets
sampler = SamplerV2(backend)
sampler.options.experimental = {"execution": {"scheduler_timing": True}}

# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: d8287kugbeec73alfbug (DONE)

Ensuite, obtiens le timing du programme de circuit :

# Get the circuit schedule timing
result[0].metadata["compilation"]["scheduler_timing"]["timing"]
'main,rz_0,Qubit 0,1365,0,shift_phase\nmain,sx_0,Qubit 0,1365,9,play\nmain,sx_0,Qubit 0,1369,0,shift_phase\nmain,rz_0,Qubit 0,1374,0,shift_phase\nmain,barrier,Qubit 0,1374,0,barrier\nmain,measure_0,Qubit 0,1374,64,play\nmain,measure_0,Qubit 0,1438,108,play\nmain,measure_0,AWGR0_0,1485,360,capture\nmain,measure_0,Qubit 0,1546,64,play\nmain,measure_0,Qubit 0,1610,64,play\nmain,barrier,Qubit 0,2046,0,barrier\nmain,broadcast,Hub,1485,561,broadcast\nmain,receive,Receive,2046,7,receive\nthen,x_0,Qubit 0,2061,9,play\nmain,barrier,Qubit 0,2079,0,barrier\nmain,measure_0,Qubit 0,2079,64,play\nmain,measure_0,Qubit 0,2143,108,play\nmain,measure_0,AWGR0_0,2190,360,capture\nmain,measure_0,Qubit 0,2251,64,play\nmain,measure_0,Qubit 0,2315,64,play\nmain,barrier,Qubit 0,2725,0,barrier\nmain,barrier,Qubit 0,2725,0,barrier\n'

Enfin, tu peux visualiser et enregistrer le timing :

from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing

circuit_schedule = result[0].metadata["compilation"]["scheduler_timing"][
"timing"
]
fig = draw_circuit_schedule_timing(
circuit_schedule=circuit_schedule,
included_channels=None,
filter_readout_channels=False,
filter_barriers=False,
width=1000,
)

# Uncomment the following line to display the figure
# fig.show(renderer="notebook")

# Save to a file
# fig.write_html("scheduler_timing.html")

Étapes suivantes