Exécuter des circuits dynamiques
Les circuits dynamiques sont des outils puissants qui permettent de mesurer des qubits au cours de l'exécution d'un circuit quantique, puis d'effectuer des opérations logiques classiques au sein du circuit, en fonction des résultats de ces mesures intermédiaires. Ce processus est également connu sous le nom de «* feedforward classique* ». Même si l'on n'en est qu'aux prémices de la compréhension des meilleures façons de tirer parti des circuits dynamiques, la communauté des chercheurs en physique quantique a déjà identifié un certain nombre de cas d'application, tels que les suivants :
- La préparation efficace d'états quantiques, tels que l'état GHZ, l'état W (pour plus d'informations sur l'état W, voir également « Préparation d'états par des circuits peu profonds utilisant la transmission directe » ) et une large classe d'états de produits matriciels
- Intrication efficace à longue portée entre des qubits situés sur la même puce grâce à l'utilisation de circuits peu profonds
- Échantillonnage efficace des circuits de type IQP
Ces améliorations apportées par les circuits dynamiques s'accompagnent toutefois de compromis. Les mesures en cours de circuit et les opérations classiques ont généralement un temps d'exécution plus long que les portes à deux qubits, et cette augmentation du temps pourrait annuler les avantages liés à la réduction de la profondeur du circuit. C'est pourquoi la réduction de la durée des mesures en cours de circuit constitue un axe prioritaire d'amélioration alors qu' IBM Quantum® s'apprête à lancer la nouvelle version de ses circuits dynamiques. Pour connaître les autres restrictions liées à l'utilisation des circuits dynamiques, consultez le tableau de compatibilité des fonctionnalités « Estimator » et « Sampler ».
La spécification « OpenQASM e 3 » définit plusieurs structures de contrôle de flux, mais le service de calcul d’ IBM Quantum ne prend actuellement en charge que l’instruction if conditionnelle. Dans « Qiskit SDK », cela correspond à la méthode if_test disponible sur QuantumCircuit. Cette méthode renvoie un gestionnaire de contexte et est généralement utilisée dans une instruction with . Ce guide explique comment utiliser cette instruction conditionnelle.
Les exemples de code présentés dans ce guide utilisent l'instruction de mesure standard pour les mesures en cours de circuit. Il est toutefois recommandé d'utiliser plutôt MidCircuitMeasure l'instruction, si le backend la prend en charge. Pour plus de détails, consultez la section « Mesures en cours de circuit ».
Le code présenté sur cette page a été développé en tenant compte des exigences suivantes. Nous vous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.5.1 qiskit-ibm-runtime~=0.47.0
Trouver des backends prenant en charge les circuits dynamiques
Pour répertorier tous les backends auxquels votre compte a accès et qui prennent en charge les circuits dynamiques, exécutez un code similaire à celui-ci. Cet exemple part du principe que vous avez enregistré vos identifiants de connexion. Vous pouvez également indiquer explicitement vos identifiants lors de l'initialisation de votre compte du service Compute d' IBM Quantum. Cela vous permettrait, par exemple, de consulter les backends disponibles sur une instance spécifique ou pour un type de forfait donné.
- Les backends disponibles pour le compte dépendent de l'instance indiquée dans les informations d'identification.
- La nouvelle version des circuits dynamiques est désormais disponible pour tous les utilisateurs sur tous les backends. Consultez l'annonce pour plus de détails.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)Output:
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_kingston')>]
Mesures en cours de circuit
Avant l'introduction de qiskit-ibm-runtime v0.43.0, measure c'était la seule instruction de mesure disponible dans Qiskit. Les mesures en cours de circuit ont toutefois des exigences de réglage différentes de celles des mesures aux bornes (mesures effectuées à l'extrémité d'un circuit). Par exemple, il faut tenir compte de la durée des instructions lors du réglage d'une mesure en cours de circuit, car des instructions plus longues entraînent davantage de bruit dans les circuits. Il n'est pas nécessaire de tenir compte de la durée des instructions pour les mesures de terminaison, car aucune instruction ne suit ces mesures.
L'instruction MidCircuitMeasure correspond à measure_2 l'instruction indiquée dans le fichier. du backend supported_instructions. Cependant, cette measure_2 fonctionnalité n'est pas prise en charge par tous les backends. Utilisez service.backends(filters=lambda b: "measure_2" in b.supported_instructions) pour trouver les backends qui le prennent en charge. D'autres mesures pourraient être ajoutées à l'avenir, mais cela n'est pas garanti.
MidCircuitMeasureméthode
Dans la version qiskit-ibm-runtime v0.43.0, cette MidCircuitMeasure instruction a été introduite. Comme son nom l'indique, il s'agit d'une nouvelle instruction de calcul optimisée pour l'exécution en milieu de circuit sur les QPU d' IBM®. Bien que vous puissiez utiliser QuantumCircuit.measure pour une mesure à mi-circuit, en raison de sa conception, MidCircuitMeasure constitue généralement un meilleur choix. Par exemple, cela alourdit moins votre circuit que lorsque vous utilisez QuantumCircuit.measure.
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)
circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))Output:
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
- Il faut qu'il y ait au moins un registre classique pour pouvoir utiliser les mesures.
- La primitive Sampler nécessite des mesures de circuit. Vous pouvez ajouter des mesures de circuit à l'aide de la primitive Estimator, mais celles-ci sont ignorées.
Store
À partir de qiskit-ibm-runtime la version 0.47.0, vous pouvez utiliser l'instruction store pour enregistrer le résultat d'une expression classique, si celle-ci doit être réutilisée à plusieurs reprises. Les opérations sont automatiquement parallélisées, ce qui rend votre code nettement plus efficace lors de l'exécution.
Pour plus d'informations, consultez le guide sur les circuits de commande classiques à action directe et le flux de contrôle.
Lorsque vous utilisez store pour enregistrer une valeur dans un registre classique sur un backend réel, cette valeur n'est conservée en mémoire que pendant l'exécution; elle n'est ni copiée ni renvoyée dans le résultat de la tâche.
if_test Par exemple, dans le code suivant, temp a la même valeur que creg lors de l'exécution, et fonctionne comme prévu. Mais une fois la tâche terminée, l' tempBitArray e renvoyée dans le résultat de la tâche ne contient pas la valeur de creg. Autrement dit, job.result()[0].data.temp est 0.
creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...Exemple complet
Le code suivant permet de créer et d'exécuter un circuit dynamique sur le matériel d' IBM®.
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, dynamic_circuits=True
)
# 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)
# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()
print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)Output:
>>> Job ID: d9mq9qnurbec73e67acg (DONE)
Limites de l'informatique quantique
Tenez compte des contraintes suivantes lors de l'exécution de circuits dynamiques dans Quantum Compute.
-
En raison de la mémoire physique limitée des composants électroniques de commande, le nombre
ifd'instructions et la taille de leurs opérandes sont également limités. Cette limite dépend du nombre de diffusions et du nombre de bits diffusés dans une tâche (et non dans un circuit).Lors du traitement d'une
ifcondition, les données de mesure doivent être transmises à la logique de commande afin de permettre cette évaluation. Une diffusion correspond au transfert de données classiques uniques, et le nombre de bits diffusés correspond au nombre de bits classiques transférés. Tenez compte des éléments suivants :c0 = ClassicalRegister(3) c1 = ClassicalRegister(5) ... with circuit.if_test((c0, 1)) ... with circuit.if_test((c0, 3)) ... with circuit.if_test((c1[2], 1)) ...Dans l'exemple de code précédent, les deux premiers
if_testobjets dec0sont considérés comme une seule diffusion, car le contenu dec0n'a pas changé et n'a donc pas besoin d'être rediffusé. Ilc1``if_tests'agit d'une rediffusion. Le premier diffuse les trois bits,c0tandis que le second n'en diffuse qu'un seul, ce qui fait un total de quatre bits diffusés.Actuellement, si vous diffusez 60 bits à chaque fois, la tâche peut comporter environ 300 diffusions. Si, en revanche, vous ne transmettez qu'un seul bit à chaque fois, la tâche peut alors comporter 2 400 transmissions.
-
L'opérande utilisé dans une
if_testinstruction doit comporter 32 bits ou moins. Ainsi, si vous comparez un entierClassicalRegister, la taille de celui-ciClassicalRegisterdoit être de 32 bits ou moins. Si vous ne comparez qu'un seul bit d'unClassicalRegister, celui-ciClassicalRegisterpeut toutefois être de n'importe quelle taille (puisque l'opérande ne comporte qu'un seul bit).Par exemple, le bloc de code « Non valide » ne fonctionne pas car
crdépasse 32 bits. Vous pouvez toutefois utiliser un registre classique de plus de 32 bits si vous ne testez qu'un seul bit, comme le montre le bloc de code « Valid ».cr = ClassicalRegister(50) qr = QuantumRegister(50) circuit = QuantumCircuit(qr, cr) ... circ.measure(qr, cr) with circ.if_test((cr, 15)): ...cr = ClassicalRegister(50) qr = QuantumRegister(50) circuit = QuantumCircuit(qr, cr) ... circ.measure(qr, cr) with circ.if_test((cr[5], 1)): ... -
Les conditions imbriquées ne sont pas autorisées. Par exemple, le bloc de code suivant ne fonctionnera pas car il contient un
if_testà l'intérieur d'un autreif_test:c1 = ClassicalRegister(1, "c1") c2 = ClassicalRegister(2, "c2") ... with circ.if_test((c1, 1)): with circ.if_test(c2, 1)): ...cr = ClassicalRegister(2) ... with circuit.if_test((cr, 0b11)): ... -
L'utilisation de
resetou de mesures à l'intérieur de conditions n'est pas prise en charge. -
Les opérations arithmétiques ne sont pas prises en charge.
-
Consultez le tableau des fonctionnalités d' OpenQASM e 3 pour déterminer quelles fonctionnalités d' OpenQASM e 3 sont prises en charge par Qiskit et Quantum Compute.
-
Lorsque le format d'entrée « OpenQASM 3 » (au lieu de
QuantumCircuit) est utilisé pour transmettre des circuits aux primitives de type « IBM Quantum », seules les instructions pouvant être chargées dans Qiskit sont prises en charge. Les opérations classiques, par exemple, ne sont pas prises en charge car elles ne peuvent pas être chargées dans Qiskit. Pour plus d'informations, consultez la section « Importer un programme d' OpenQASM ie 3 dans Qiskit ». -
Les commandes
for,while``switch, et ne sont pas prises en charge.
Utiliser des circuits dynamiques avec Estimator
Comme Estimator ne prend pas en charge les circuits dynamiques, vous pouvez utiliser Sampler et créer vos propres circuits de mesure à la place.
Pour reproduire le comportement d'Estimator, procédez comme suit :
- Regroupez les termes de toutes les observables dans une partition. Pour ce faire, on peut par exemple utiliser le
PauliListAPI.NoteVous pouvez utiliser l'attribut «
BitArrayprimitive » pour calculer les valeurs attendues des observables fournies. - Effectuer un cycle de changement de base par partition (quel que soit le changement de base à effectuer pour chaque partition). Pour plus d'informations, consultez le module
measurement_basesutilitaire « Measurement bases ». Pour plus d'informations, consultez la documentation relative au package d'utilitaires du module complémentaire Qiskit. - Additionnez les résultats obtenus pour chaque partition.
Restrictions
Consultez le tableau de compatibilité des fonctionnalités pour connaître les restrictions liées à l'utilisation des circuits dynamiques. Notez que la compatibilité des fonctionnalités ne dépend pas des primitives.
Etapes suivantes
- Découvrez comment mettre en œuvre un découplage dynamique précis à l'aide de stretch.
- Consultez le guide sur les circuits de commande classiques à action directe et le flux de contrôle.
- Utilisez la visualisation du schéma de circuit pour déboguer et optimiser vos circuits dynamiques.
- Toutes les fonctions ne sont pas compatibles avec les circuits dynamiques. Pour plus d'informations, consultez la section « Compatibilité des fonctionnalités » de Sampler.