Skip to main content
IBM Quantum Platform

Transpiler

qiskit.transpiler


Aperçu

Remarque

Si vous êtes déjà familiarisé avec les concepts de transpilation/compilation de circuits, vous pouvez passer directement à l'étape suivante :

La transpilation est le processus de réécriture d'un circuit d'entrée donné pour qu'il corresponde à la topologie d'un dispositif quantique spécifique et/ou pour optimiser le circuit en vue de son exécution sur des systèmes quantiques.

La plupart des circuits doivent subir une série de transformations qui les rendent compatibles avec un dispositif cible donné et les optimisent pour réduire les effets du bruit sur les résultats obtenus. La réécriture des circuits quantiques en fonction des contraintes matérielles et l'optimisation des performances sont loin d'être triviales. Le flux logique dans la chaîne d'outils de réécriture ne doit pas nécessairement être linéaire et peut souvent comporter des sous-boucles itératives, des branches conditionnelles et d'autres comportements complexes. Ceci étant dit, le flux de compilation standard suit la structure indiquée ci-dessous :

Le processus de transpilation prend le circuit d'entrée, applique les passes de transpilation, puis produit le circuit de sortie.

QuantumCircuitQiskit utilise la représentation intermédiaire (IR) d'un circuit sous DAGCircuit forme de graphe tout au long de la chaîne de transcompilation, plutôt que celle sous forme d'arbre. Un pipeline de transcompilation est un PassManager objet dont PassManager.run() la méthode prend en paramètre un QuantumCircuit et le convertit en un DAGCircuit, puis soumet ce code intermédiaire à une séquence de passes, avant de renvoyer finalement un QuantumCircuit . Un passage est soit un AnalysisPass, qui calcule et stocke des propriétés relatives au circuit dans le PropertySet, soit un TransformationPass, qui modifie l'IR afin d'atteindre un objectif singulier particulier. On peut considérer qu'un pipeline est divisé en « étapes », chacune d'entre elles étant chargée d'effectuer une transformation de haut niveau.

Qiskit met à disposition un générateur de pipeline de transpilation par défaut via la fonction generate_preset_pass_manager(). Cette fonction renvoie un pipeline correctement configuré pour une transpilation complète, à un niveau choisi optimization_level (compris entre 0 et 3, inclus). À moins que vous ne recherchiez quelque chose de très spécialisé, c'est très certainement par là qu'il faut commencer. Voici un exemple de transpilation :

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Any abstract circuit you want:
abstract = QuantumCircuit(2)
abstract.h(0)
abstract.cx(0, 1)

# Any method you like to retrieve the backend you want to run on:
backend = QiskitRuntimeService().backend("some-backend")

# Create the pass manager for the transpilation ...
pm = generate_preset_pass_manager(backend=backend)
# ... and use it (as many times as you like).
physical = pm.run(abstract)

Dans le cadre des premières expériences menées en matière de tolérance aux pannes, les fonctions generate_preset_pass_manager() et transpile() font appel à un pipeline de transpilation spécialisé lorsque la base cible est constituée de portes Clifford+T; voir generate_preset_clifford_t_pass_manager() pour plus d'informations. Il est recommandé d'utiliser cette dernière pour une configuration détaillée des pipelines Clifford+T. Par exemple, la précision de synthèse de l' RZR_Z ne peut pas être définie via "unitary_synthesis_method" dans generate_preset_pass_manager() mais ne peut être définie que de manière globale via le "approximation_degree". Cependant, generate_preset_clifford_t_pass_manager() cela met "rz_synthesis_config" en évidence ce problème.

Par exemple :

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import get_clifford_gate_names

# Any abstract circuit you want:
abstract = QuantumCircuit(4)
abstract.append(QFTGate(4), [0, 1, 2, 3])

# Use all Clifford+T basis gates
basis_gates = get_clifford_gate_names() + ["t", "tdg"]

# Create and run the pass manager
pm = generate_preset_pass_manager(basis_gates=basis_gates)
transpiled = pm.run(abstract)

Dans la plupart des cas, c'est tout ce dont vous avez besoin. Cependant, toute l'infrastructure de transposition de Qiskit est hautement extensible et configurable. Le reste de cette page explique comment exploiter les capacités de bas niveau de la pile du transpileur.


Gestionnaires de passes prédéfinis

Cette fonction generate_preset_pass_manager() crée les « gestionnaires de passes prédéfinis ». Ce sont tous des exemples de PassManager, qui sont donc utilisés en transmettant un QuantumCircuit à la PassManager.run() méthode. Plus précisément, les gestionnaires de passes prédéfinis sont des instances de StagedPassManager, ce qui permet une configuration plus poussée des différentes étapes d'une transpilation, y compris les hooks pré- et post-étape.

Un gestionnaire de laissez-passer préétabli peut comporter jusqu'à six étapes nommées. Elles sont résumées ci-dessous, dans l'ordre d'exécution, et des informations plus détaillées sont fournies dans les sous-sections suivantes.

init

Résumé : optimisation des circuits et réduction des opérations multi-qubits à des opérations à un ou deux qubits. Voir l' étape d'initialisation pour plus de détails.

layout

Choisir une cartographie initiale des qubits virtuels aux qubits physiques, y compris l'expansion du circuit pour contenir des ancillas explicites. Cette étape peut parfois se substituer à routing. Voir l' étape de la mise en page pour plus de détails.

routing

Insérez des portes logiques dans le circuit afin de vous assurer qu'il respecte les contraintes de connectivité du Target. Les portes insérées ne doivent pas nécessairement correspondre à l'ISA cible pour l'instant; il s'agit donc souvent simplement swap d'instructions. Cette étape est parfois omise, lorsque layout l'étape en question remplit correctement sa fonction. Pour plus de détails, consultez la section « Étape de routage ».

translation

Convertissez toutes les portes du circuit en portes compatibles avec l'ISA du Target. Pour plus de détails, consultez la section « Étape de traduction ».

optimization

Optimisations de bas niveau tenant compte du matériel. Contrairement aux optimisations abstraites de l'étape init , cette étape agit sur un circuit physique. Voir l' étape d'optimisation pour plus de détails.

scheduling

Insérer Delay des instructions pour rendre explicite la synchronisation d'un circuit avec l'horloge murale. Il peut également s'agir de techniques de réduction des erreurs en ligne tenant compte du matériel, telles que le découplage dynamique, qui dépendent de la connaissance des durées de l'horloge murale. Pour plus de détails, voir l' étape de la programmation.

Les pipelines de transpilation prédéfinis peuvent également être configurés à un niveau élevé en définissant une adresse optimization_level. Il s'agit d'un nombre entier compris entre 0 et 3 inclus, qui indique l'effort relatif à fournir pour tenter d'optimiser le circuit pour le matériel. Le niveau 0 désactive toutes les optimisations inutiles; seules les transformations nécessaires pour rendre le circuit exécutable sont utilisées. À l'autre extrémité, le niveau 3 permet une gamme complète de techniques d'optimisation, dont certaines peuvent être très coûteuses en temps de compilation. Comme pour les compilateurs classiques, le niveau d'optimisation 3 ne garantit pas toujours les meilleurs résultats. Qiskit utilise par défaut le niveau d'optimisation 2, comme compromis entre le temps de compilation et la quantité d'optimisation attendue.

Le niveau d'optimisation détermine les implémentations utilisées par défaut pour une étape donnée, mais il est possible de le contourner en passant des arguments explicites <stage>_method="<choice>" à generate_preset_pass_manager().

Reproductibilité des pipelines prédéfinis

La compilation quantique implique souvent de résoudre des problèmes dont on sait que leur complexité n'est pas polynomiale, et qui sont donc impossibles à optimiser globalement. Dans ces cas-là, les algorithmes stochastiques et heuristiques sont souvent plus adaptés. Cela pose toutefois des problèmes de reproductibilité.

Les gestionnaires de passes prédéfinis comprennent presque toujours des passes stochastiques, basées sur l'heuristique. Si vous devez assurer la reproductibilité d'une compilation, passez un entier connu à l'argument seed_transpiler des fonctions du générateur.

Tous les plugins intégrés à Qiskit doivent produire leurs analyses et modifier le code DAGCircuit de manière déterministe si leur randomisation (le cas échéant) est initialisée, afin qu'une compilation puisse être répétée ultérieurement. Il y a toutefois des limites à cela :

  • Toutes les passes intégrées comportant des éléments stochastiques doivent fournir un moyen d'amorcer la randomisation et, si elles sont amorcées, elles doivent respecter les règles de la sortie déterministe.
  • Toutes les fonctions intégrées ne comportant pas de composantes stochastiques doivent respecter les règles de sortie déterministe pour une entrée identique. Il est permis d’utiliser un cache à des fins d’efficacité, mais, pour un même ensemble d’entrées, les résultats du passage doivent être identiques si celui-ci est appelé plusieurs fois, à moins qu’un élément échappant au contrôle du passage ne modifie l’une de ses entrées in situ (par exemple, le BasisTranslator utilise le SessionEquivalenceLibrary par défaut dans les gestionnaires de passages prédéfinis, et le fait d’obtenir des résultats différents lorsque de nouvelles entrées sont ajoutées à la bibliothèque d’équivalences ne constitue pas un bug). Une « sortie » désigne tout ce que la passe génère en vue d'une utilisation ultérieure; il peut s'agir de la valeur explicite return renvoyée par la passe, mais cela inclut également les propriétés destinées à être utilisées ultérieurement dans le PropertySet.
  • Le résultat d'une passe doit être déterministe sur une machine donnée pour une version donnée de Qiskit et un environnement figé, quel que soit le nombre de threads disponibles pour la passe. De nombreuses passes Qiskit intégrées utilisent la concurrence threadée, et elles ne sont pas autorisées à avoir un comportement différent en fonction du nombre de threads.
  • Le résultat d'une passe pour une graine fixe n'est pas tenu d'être égal si une partie de l'environnement Python sous-jacent change (par exemple, si un paquet dépendant est mis à jour) ou si les bibliothèques mathématiques du système changent (par exemple, si une implémentation différente de BLAS est disponible).
  • Le résultat d'une passe pour une graine fixe n'est pas tenu d'être identique d'un système d'exploitation à l'autre (bien que ce soit généralement la mise en œuvre de la bibliothèque mathématique du système qui soit à l'origine des différences liées au système d'exploitation).
  • Il n'est pas nécessaire que le résultat d'une passe pour une graine fixe soit le même entre deux machines disposant d'instructions différentes de l'unité centrale; on s'attend à ce que différentes mises en œuvre de noyaux mathématiques de base produisent un comportement différent si différentes instructions de l'unité centrale sont disponibles, telles que des instructions de multiplication-addition fusionnées ayant des caractéristiques d'arrondi différentes de celles de deux instructions distinctes de multiplication et d'addition en virgule flottante.
  • Le résultat d'une passe pour une graine fixe doit être le même, quel que soit le nombre de threads qu'elle est autorisée à utiliser, à moins que l'utilisateur ne choisisse spécifiquement de ne pas le faire. Par exemple, dans les gestionnaires de passifs prédéfinis, les méthodes de mise en page et de routage Sabre doivent exécuter le même nombre d'essais par défaut, qu'il y ait un seul fil autorisé ou même plus de fils que d'essais, bien que ce comportement puisse être explicitement ignoré en définissant la variable d'environnement QISKIT_SABRE_ALL_THREADS afin d'accepter d'être sensible au nombre de fils.
  • Toutes les règles ci-dessus s'appliquent même entre des sessions distinctes de l'interprète Python, même si PYTHONHASHSEED n'a pas été explicitement défini.

En règle générale, un utilisateur de devrait pouvoir partir du principe que DAGCircuit , une fois qu’une combinaison quelconque de passes Qiskit intégrées (et initialisées le cas échéant) a été exécutée avec des entrées fixes, le résultat exact de toutes DAGCircuit() les méthodes est déterministe. Cela inclut l'ordre de sortie, même pour les méthodes qui ne donnent aucune garantie quant à cet ordre; bien qu'on ne puisse pas se fier à la sémantique ni à l'ordre précis, on peut compter sur son déterminisme pour des entrées fixes.

Les auteurs de passes de transpileur devraient consulter Randomness and determinism pour une discussion sur la façon de rendre une passe de transpileur déterministe.

Choix des implémentations de scène prédéfinies

Qiskit comprend plusieurs implémentations des étapes ci-dessus, et d'autres peuvent être installées en tant que "plugins" séparés. Pour contrôler l'implémentation d'une étape, passez son nom à l'argument <stage>_method des deux fonctions, par exemple translation_method="translator". Pour en savoir plus sur la mise en œuvre de tels plugins externes pour une scène, voir qiskit.transpiler.preset_passmanagers.plugin.

Par exemple, pour générer un gestionnaire de passage prédéfini au niveau d'optimisation 1 qui utilise explicitement la méthode trivial pour la mise en page et la méthode sabre pour le routage, il faut procéder comme suit :

from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2

# Whatever backend you like:
backend = GenericBackendV2(num_qubits=5)

pass_manager = generate_preset_pass_manager(
    optimization_level=1,
    backend=backend,
    layout_method="trivial",
    routing_method="sabre",
)
Remarque

L'ensemble intégré de plugins disponibles pour chaque étape fait partie de l'API publique de Qiskit et bénéficie de toutes les garanties de stabilité. Cela inclut les effets logiques de haut niveau de cette méthode (par exemple, routing_method="sabre" elle utilisera toujours un algorithme dérivé de Sabre). La structure interne exacte de la PassManager fonction représentant l'étape n'est toutefois pas fixe; l'ordre des passes peut varier d'une version mineure à l'autre, ou de nouvelles passes peuvent être introduites.

Pour toutes les étapes qui en ont une, la méthode nommée "default" est la plus susceptible d'être modifiée. Qiskit n'effectue généralement que des changements algorithmiques complets dans la méthode par défaut entre deux versions majeures, mais il peut rééquilibrer les heuristiques et ajouter de nouvelles passes aux méthodes par défaut entre deux versions mineures.

Étant donné que la sortie de generate_preset_pass_manager() est un StagedPassManager, vous pouvez également modifier le gestionnaire de passes après sa création afin de proposer une implémentation entièrement personnalisée de l'étape. Par exemple, si vous souhaitez exécuter une étape de planification personnalisée en utilisant le découplage dynamique (via le paramètre PadDynamicalDecoupling pass) et ajouter également une optimisation logique initiale avant le routage, vous pourriez procéder comme suit (en vous appuyant sur l'exemple précédent) :

import numpy as np
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.circuit import library as lib
from qiskit.transpiler import PassManager, generate_preset_pass_manager
from qiskit.transpiler.passes import (
    ALAPScheduleAnalysis,
    InverseCancellation,
    PadDynamicalDecoupling,
)

backend = GenericBackendV2(num_qubits=5)
dd_sequence = [lib.XGate(), lib.XGate()]
scheduling_pm = PassManager(
    [
        ALAPScheduleAnalysis(target=backend.target),
        PadDynamicalDecoupling(target=backend.target, dd_sequence=dd_sequence),
    ]
)
inverse_gate_list = [
    lib.CXGate(),
    lib.HGate(),
    (lib.RXGate(np.pi / 4), lib.RXGate(-np.pi / 4)),
    (lib.PhaseGate(np.pi / 4), lib.PhaseGate(-np.pi / 4)),
    (lib.TGate(), lib.TdgGate()),
]
logical_opt = PassManager([InverseCancellation(inverse_gate_list)])

pass_manager = generate_preset_pass_manager(optimization_level=0)
# Add pre-layout stage to run extra logical optimization
pass_manager.pre_layout = logical_opt
# Set scheduling stage to custom pass manager
pass_manager.scheduling = scheduling_pm

Désormais, lorsque le gestionnaire de passes par étape est exécuté via la run() méthode, ce logical_opt gestionnaire sera appelé avant l'étape layout , et c'est scheduling_pm lui qui sera utilisé pour cette scheduling étape à la place du gestionnaire par défaut.

Si vous construisez des scènes personnalisées pour les gestionnaires de passe prédéfinis, vous trouverez peut-être certaines des fonctions d'aide de bas niveau dans qiskit.transpiler.preset_passmanagers vous seront utiles.

Phase d'initialisation

Voir aussi

Explication de l'étape d'initialisation

Explication à un niveau plus élevé de l'étape init dans le guide IBM Quantum.

Cette init étape est chargée d'effectuer des optimisations logiques de haut niveau sur des circuits abstraits, ainsi que de décomposer les opérations multi-qubits (3+) en une série d'opérations à un et deux qubits. Comme il s'agit de la première exécution de l'étape, son entrée est un circuit entièrement abstrait. La init plateforme doit pouvoir prendre en charge les portes personnalisées définies par l'utilisateur, ainsi que tous les objets de description de circuits abstraits de haut niveau, tels que AnnotatedOperation.

Le résultat de l'étape init est un circuit abstrait qui ne contient que des opérations à un ou deux qubits.

Lors de l'écriture des plugins de scène, le point d'entrée pour init est qiskit.transpiler.init. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautDéroulement intégré des opérations multi-qubits et optimisations abstraites.

Plugin default intégré

Au niveau d'optimisation 0, aucune optimisation abstraite n'est effectuée. Le plugin par défaut se contente de « dérouler » les opérations comportant plus de trois qubits en accédant à leurs champs hiérarchiques definition .

Aux niveaux d'optimisation 1 et supérieurs, le plugin par défaut effectue également une annulation simple des portes inverses adjacentes, telles que deux portes cx dos à dos.

Aux niveaux d'optimisation 2 et 3, le plugin par défaut permet une gamme beaucoup plus large d'optimisations abstraites. Comprend :

  • « Élision de permutation virtuelle » (voir ElidePermutations), où les opérations induisant explicitement une permutation sont supprimées et remplacées par un remappage des qubits virtuels.
  • Analyse de la structure de commutation de l'IR pour trouver des paires de portes qui peuvent être annulées.
  • Fractionnement numérique des opérations à deux qubits qui peuvent être exprimées comme une série d'opérations séparables à un qubit.
  • Suppression des opérations imperceptibles, telles que les rotations de Pauli à angle réduit et les opérations diagonales précédant immédiatement les mesures.

Phase de mise en page

Voir aussi

Explication de l'étape de la mise en page

Explication de l'étape de la mise en page dans le guide IBM Quantum à l'intention des utilisateurs.

L'étape de la mise en page est chargée d'établir une première correspondance entre les qubits virtuels du circuit d'entrée et les qubits matériels de la cible. Il s'agit notamment d'étendre le circuit d'entrée avec des ancillas explicites de manière à ce qu'il ait autant de qubits que la cible, et de réécrire toutes les opérations en termes de qubits matériels. Vous pouvez également voir ce problème appelé problème de "placement" dans d'autres boîtes à outils ou dans d'autres documents.

PropertySetL'étape de mise en page doit définir les propriétés layout et original_qubit_indices dans le fichier. du pipeline.

Remarque

Tous les plugins intégrés destinés à la phase de mise en page donneront la priorité à une mise en page explicite sélectionnée à l'aide de initial_layout l'argument de generate_preset_pass_manager() ou transpile().

À tout moment dans un circuit, nous pouvons identifier une correspondance entre les qubits "virtuels" actuellement actifs du circuit d'entrée et les qubits matériels du backend. Un qubit matériel ne peut jamais représenter qu'un seul qubit virtuel à un moment donné, mais la correspondance peut varier au cours du circuit. En principe, certains qubits virtuels peuvent ne pas être mappés à tous les moments de l'exécution du circuit, si la durée de vie de l'état d'un qubit virtuel peut être raccourcie, bien que les pipelines intégrés de Qiskit ne l'utilisent pas actuellement.

Illustration de la manière dont les qubits virtuels d'un circuit d'entrée peuvent être mis en correspondance avec des qubits matériels sur la carte de connectivité d'un dispositif dorsal.

La phase de mise en page n'est pas chargée de veiller à ce que la connectivité de la cible soit respectée tout au long du circuit, ni à ce que toutes les opérations soient valables pour une exécution directe sur la cible; ces responsabilités incombent respectivement aux phases de routage et de traduction.

Le choix de la disposition initiale est l'un des facteurs les plus importants qui affectent la qualité du circuit de sortie. L'étape de la mise en page est souvent la plus coûteuse en termes de calculs dans les pipelines par défaut; le plugin par défaut pour la mise en page essaie même plusieurs algorithmes différents (décrit plus en détail dans le plugin par défaut intégré ).

Dans l'idéal, lors de la phase de conception, il faudrait trouver une configuration « parfaite », dans laquelle toutes les opérations respecteraient les contraintes de connectivité du circuit Target , de sorte que la phase de routage ne soit pas nécessaire. En règle générale, cela n'est pas possible pour des circuits d'entrée arbitraires, mais lorsque c'est le cas, cette VF2Layout étape peut être utilisée pour trouver une configuration initiale valide. Si plusieurs dispositions optimales sont identifiées, une heuristique de notation fondée sur les taux d'erreur estimés est utilisée pour déterminer laquelle retenir.

Dans tous les plugins intégrés, le fait de passer l'argument initial_layoutgenerate_preset_pass_manager() entraîne l'utilisation de la mise en page indiquée telle quelle, en contournant la logique de « sélection » individuelle. Tous les plugins intégrés gèrent également l'intégration du circuit sur toute la largeur du dispositif, y compris l'affectation des composants auxiliaires.

Si vous écrivez votre propre plugin de mise en page, vous trouverez peut-être generate_embed_passmanager() utile pour automatiser l'étape d'intégration de l'application de mise en page.

Lors de l'écriture des plugins de scène, le point d'entrée pour layout est qiskit.transpiler.layout. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautAux niveaux d'optimisation les plus élevés, il tente de trouver une disposition parfaite, puis essaie une passe combinée de disposition et de routage basée sur Sabre.
denseTrouve le sous-graphe le plus dense (en termes de degrés de liaison des qubits) du backend à utiliser comme qubits initiaux.
TrivialeAssocie le qubit virtuel 0 au qubit physique 0, et ainsi de suite.
sabreUtilise l' algorithme de mise en page Sabre amélioré de Qiskit.

À tous les niveaux d'optimisation, la méthode de mise en page par défaut est default, bien que la structure de cette étape change considérablement en fonction du niveau.

Plugin default intégré

Un amalgame de plusieurs techniques de mise en page.

Au niveau d'optimisation 0, la disposition triviale est choisie.

Pour les niveaux d'optimisation supérieurs à 0, il y a un processus en deux étapes :

  1. Commencez par utiliser VF2Layout pour essayer de trouver une mise en page « parfaite ». Le nombre maximal d'appels à l'évaluateur d'isomorphisme augmente avec le niveau d'optimisation. Pour les cibles très volumineuses et complexes, il n'est pas certain que nous trouvions des dispositions parfaites, même si celles-ci existent, mais les chances d'y parvenir augmentent avec le niveau d'optimisation.
  2. Si aucune disposition parfaite ne peut être trouvée, utilisez SabreLayout pour choisir une disposition initiale, le nombre d'essais de disposition initiale, d'essais de permutation et d'itérations avant-arrière augmentant en fonction du niveau d'optimisation.

En outre, le niveau d'optimisation 1 essaie également la disposition triviale avant la version VF2-based, à des fins de rétrocompatibilité historique.

Plugin dense intégré

Utilise le DenseLayout « pass » pour choisir la mise en page. Cette étape identifie le sous-graphe connexe le plus dense du graphe de connectivité complet cible, le terme « le plus dense » signifiant que la préférence est donnée aux qubits matériels disposant du plus grand nombre de connexions disponibles. La mise en correspondance entre les qubits virtuels et les qubits matériels s'effectue en attribuant les qubits virtuels de plus haut degré aux qubits matériels de plus haut degré.

Il s'agit d'une méthode heuristique relativement peu coûteuse pour choisir une disposition initiale, mais elle offre généralement une qualité de résultat bien inférieure à celle des méthodes basées sur Sabre. Le plugin de mise en page par défaut utilise le mappage initial sélectionné par DenseLayout comme l'une de ses mises en page initiales pour initialiser l'algorithme Sabre.

Plugin trivial intégré

Utilise le TrivialLayout « pass » pour choisir la mise en page. Il s'agit de l'affectation la plus simple, dans laquelle chaque qubit virtuel est associé au qubit matériel portant le même indice : ainsi, le qubit virtuel 0 est associé au qubit matériel 0, et ainsi de suite.

Cette méthode est particulièrement utile pour les expériences de caractérisation du matériel, où le circuit "abstrait" entrant est déjà en pleine largeur sur le dispositif, où ses opérations correspondent à des opérations physiques et où le transpileur est simplement invoqué pour formaliser la création d'un circuit physique QuantumCircuit.

Plugin sabre intégré

Utilise le SabreLayout pour choisir une configuration initiale, en recourant à l’algorithme de routage Sabre modifié de Qiskit comme sous-programme permettant d’effectuer un swap-map du circuit candidat aussi bien en sens direct qu’en sens inverse.

En résumé, le composant de disposition de l' algorithme Sabre original choisit arbitrairement une disposition initiale, puis tente de l'"améliorer" en exécutant le routage sur le circuit, en inversant le circuit et en exécutant le routage sur le circuit inversé avec l'affectation virtuelle-matérielle "finale" précédente en tant qu'état initial. Le niveau d'optimisation configuré décide du nombre d'itérations de ce va-et-vient et du nombre de dispositions initiales aléatoires différentes à essayer.

La principale différence par rapport à l' étape par défaut aux niveaux d'optimisation autres que 0 est que ce plugin exécute uniquement l'algorithme basé sur Sabre. Il ne cherche pas à trouver une disposition parfaite, ni à obtenir une disposition triviale.

Étape de routage

Voir aussi

Explication de l'étape de routage

Explication du niveau supérieur de l'étape de routage dans le guide IBM Quantum.

L'étape de routage garantit que le graphe de connectivité virtuelle du circuit est compatible avec le graphe de connectivité matérielle de la cible. En termes plus simples, l'étape de routage garantit que toutes les portes à deux qubits du circuit sont mappées sur des qubits matériels qui ont une opération à deux qubits définie dans l'ISA cible. Ce problème peut également être appelé "mapping" ou "swap-mapping" dans d'autres outils ou dans la littérature.

Les algorithmes de routage y parviennent généralement en insérant des portes swap dans le circuit et en modifiant la correspondance virtuelle et matérielle des qubits au cours de l'exécution du circuit.

L'étape de routage n'a pas à garantir que toutes les portes du circuit soient compatibles avec l'ISA cible. SwapGatePar exemple, un plugin de routage peut laisser des portes littérales swap dans le circuit, même si celui-ci Target n'en contient pas. Cependant, il doit y avoir au moins une porte à deux qubits définie dans le circuit Target pour toute paire de qubits matériels à laquelle une porte est appliquée dans le circuit.

L'étape de routage doit définir les final_layout propriétés et virtual_permutation_layout dans le PropertySet si un routage a eu lieu.

Toutes les étapes de routage intégrées à Qiskit exécuteront en outre la VF2PostLayout passe après le routage. Cela pourrait entraîner une réorganisation de la configuration initiale, si l'on parvient à trouver des qubits présentant moins d'erreurs. Cette passe est très similaire à la VF2Layout classe utilisée par le plugin de mise en page par défaut, à ceci près que VF2PostLayout nous pouvons garantir qu’il existe au moins un sous-graphe induit isomorphe de la topologie cible qui correspond à la topologie du circuit.

Remarque

Les plugins de routage intégrés à Qiskit supposent généralement que toutes les paires de qubits avec un lien défini entre deux qubits ont un ensemble universel de portes défini pour ces deux qubits. Le matériel ne doit pas nécessairement respecter cette règle (par exemple, si la seule porte à deux qubits définie est swap, des opérations d'enchevêtrement telles que cx ne peuvent pas être réalisées), mais Qiskit n'envisage pas encore cette possibilité.

Remarque

On sait que la recherche du nombre minimal d'échanges à insérer est un problème non polynomial. Cela signifie que le coût d'une telle tentative est prohibitif; c'est pourquoi de nombreux algorithmes intégrés à Qiskit sont stochastiques, et vous pouvez constater d'importantes variations d'une compilation à l'autre. Si vous avez besoin de reproductibilité, veillez à définir l'argument seed_transpiler de generate_preset_pass_manager() sur ou transpile().

Lors de l'écriture des plugins de scène, le point d'entrée pour routing est qiskit.transpiler.routing. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautUtiliser une méthode de routage par défaut choisie par Qiskit.
sabreValeur par défaut. Utilise l' algorithme de routage Sabre modifié de Qiskit pour échanger des cartes.
AucunDésactiver le routage. Lance une erreur si le routage est nécessaire.
de baseInsertion gourmande de swaps pour acheminer une seule opération à la fois.
assertion avantRecherche en première intention avec élagage heuristique pour trouver des permutations qui rendent les portes exécutables.

Plugin default intégré

Utilisez la méthode de routage par défaut choisie par Qiskit. Selon la page 2.0 de Qiskit, l’algorithme choisi est le même que celui du plugin Sabre intégré; toutefois, dans la pratique, c’est généralement le plugin « layout-stage » intégré par défaut qui exécute l’algorithme de routage basé sur Sabre, tandis que l’étape de routage ne sert qu’à exécuter VF2PostLayout.

Plugin none intégré

Un plugin factice utilisé pour désactiver complètement le routage. Cela peut parfois être utile pour des expériences de configuration matérielle ou dans certains cas particuliers de compilation partielle.

Plugin basic intégré

Utilise l'algorithme d'insertion par permutation BasisSwap . Le concept est très simple : pour chaque opération dans l'ordre topologique, insérer les échanges de chemins les plus courts nécessaires pour rendre la connexion exécutable sur l'appareil.

Le niveau d'optimisation n'influe que sur l'ampleur du travail effectué par cette VF2PostLayout étape pour tenter d'améliorer la disposition initiale après le routage.

Cette méthode présente généralement une qualité de sortie médiocre.

Plugin lookahead intégré

Utilise LookaheadSwap l'algorithme pour le routage. Il s'agit essentiellement d'une recherche en largeur visant à générer un réseau d'échanges, dans laquelle l'arbre exploré est élagué à chaque niveau de profondeur pour ne retenir qu'un petit nombre d'échanges potentiels.

Cet algorithme est similaire à l'heuristique basic du plugin "sabre", sauf qu'il prend également en compte les effets suivants de chaque échange dans une faible mesure.

Le niveau d'optimisation influe sur la profondeur de recherche, l'ampleur de l'élagage par niveau et la charge de travail nécessaire VF2PostLayout pour optimiser a posteriori la disposition initiale.

Dans la pratique, le plugin "sabre" fonctionne plusieurs ordres de grandeur plus rapidement et produit de meilleurs résultats.

Plugin sabre intégré

Utilise SabreSwap l'algorithme pour le routage. Cette méthode utilise la version améliorée par Qiskit de l'algorithme de routage Sabre d'origine.

Cet algorithme de routage s'exécute avec un parallélisme threadé pour envisager plusieurs possibilités de routage, en choisissant celle qui minimise le nombre de permutations insérées.

Le niveau d'optimisation détermine le nombre de graines stochastiques différentes utilisées pour le routage complet, ainsi que la charge de travail nécessaire à VF2PostLayout la post-optimisation de la disposition initiale.

C'est presque toujours le plugin intégré le plus performant, et celui que Qiskit utilise par défaut dans tous les cas où le routage est nécessaire.

Étape de traduction

Voir aussi

Explication de l'étape de traduction

Explication de l'étape de traduction dans le guide IBM Quantum à l'intention de l'utilisateur.

L'étape de traduction est responsable de la réécriture de toutes les portes du circuit en portes compatibles avec l'ISA cible. Par exemple, si une opération cx est demandée sur les qubits matériels 0 et 1, mais que l'ISA ne contient qu'une opération cz sur ces qubits, l'étape de traduction doit trouver un moyen de représenter la porte cx à l'aide de la porte cz et des portes à un qubit disponibles.

L'étape de traduction est appelée avant d'entrer dans l'étape d'optimisation. Les plugins d'optimisation (y compris les plugins intégrés de Qiskit) peuvent également utiliser l'étape de traduction comme une étape de "correction" après la boucle d'optimisation, si la boucle d'optimisation retourne un circuit qui inclut des portes non-ISA. Cette dernière situation est assez courante; la boucle d'optimisation peut ne s'intéresser qu'à la minimisation de propriétés telles que le "nombre de portes à deux qubits", et laissera ses résultats en termes de portes localement équivalentes, que l'étape de traduction peut facilement réécrire sans affecter les propriétés d'optimisation ciblées. Cela permet de séparer plus facilement les préoccupations entre les deux étapes. Certains plugins d'optimisation peuvent être plus stricts dans leurs résultats, de sorte que ce suivi de l'étape de traduction peut ne plus être nécessaire.

Lors de l'écriture des plugins de scène, le point d'entrée pour translation est qiskit.transpiler.translation. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautUtiliser une méthode de traduction par défaut choisie par Qiskit.
traducteurTraduction symbolique des portes vers la base cible à l'aide d'équivalences connues.
synthèseRassembler chaque série de portes à un ou deux qubits dans une représentation matricielle et resynthétiser à partir de là.

Plugin default intégré

Utiliser une méthode par défaut choisie par Qiskit pour la traduction. Depuis Qiskit 2.0, c'est la même chose que le plugin Built-in translator, mais l'algorithme choisi peut changer au cours de la série 2.x, soit pour toutes les cibles, soit seulement pour certaines classes de cibles.

Plugin synthesis intégré

Regroupez les séquences de portes portant sur les mêmes qubits sous forme matricielle, puis effectuez une resynthèse à l'aide du UnitarySynthesis passage (avec la configuration unitary_synthesis_method). Cela s'apparente, en grande partie, à la boucle d'optimisation elle-même à des niveaux d'optimisation élevés.

La collecte en matrices est généralement plus coûteuse que les traductions sans matrice, mais en principe la qualité des traductions peut être meilleure. Dans la pratique, cela nécessite un algorithme de synthèse adapté à l'ISA cible, ce qui rend cette méthode moins générale que d'autres. Il peut produire des résultats de meilleure qualité en ciblant des ISA simples qui correspondent aux routines de synthèse déjà présentes dans Qiskit.

Si cette méthode est utilisée, il se peut que la boucle d'optimisation ne soit pas nécessaire.

Le niveau d'optimisation n'a pas d'effet sur ce plugin.

Plugin translator intégré

Utilise BasisTranslator l'algorithme pour convertir symboliquement les portes dans la base cible. D'une manière générale, ce processus part de l'ensemble des portes requises par le circuit et utilise les règles d'un modèle donné EquivalenceLibrary (généralement le SessionEquivalenceLibrary) pour aboutir à l'ISA.

Il s'agit de la méthode de traduction par défaut.

Le niveau d'optimisation n'a pas d'effet sur ce plugin.

Phase d'optimisation

Voir aussi

Explication de la phase d'optimisation

Explication de l'étape d'optimisation dans le guide IBM Quantum.

L'étape d'optimisation concerne les optimisations de bas niveau tenant compte du matériel. Contrairement à l' étape init, l'entrée de cette étape est un circuit déjà compatible avec l'ISA, de sorte qu'un plugin d'optimisation de bas niveau peut être adapté à une ISA particulière.

Les exigences relatives à un plugin d'optimisation sont très peu nombreuses : il doit simplement accepter des circuits pris en charge par l'ISA et renvoyer des circuits pris en charge par l'ISA. Un plugin d'optimisation contient souvent une boucle, telle que la boucle DoWhileController, et peut intégrer l'étape de traduction configurée en tant que pipeline de correction.

Les plugins d'optimisation intégrés de Qiskit sont généraux et s'appliquent bien à la plupart des ISA du monde réel pour les dispositifs non corrigés des erreurs. Les plugins intégrés sont moins bien adaptés aux ISA qui n'ont pas de porte à un qubit paramétrée en continu.

Lors de l'écriture des plugins de scène, le point d'entrée pour optimization est qiskit.transpiler.optimization. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautUn ensemble de passes d'optimisation par défaut. Cela varie considérablement d'un niveau d'optimisation à l'autre.

Plugin default intégré

Cela varie considérablement en fonction du niveau d'optimisation.

Les détails de ce pipeline sont susceptibles de changer d'une version de Qiskit à l'autre. Les grands principes sont décrits ci-dessous.

Au niveau d'optimisation 0, la scène est vide.

Au niveau d'optimisation 1, l'étage procède à la resynthèse matricielle de séries de portes à un qubit et à l'annulation symbolique inverse très simple de portes à deux qubits, si elles apparaissent consécutivement. Ce processus tourne en boucle jusqu'à ce que la taille et la profondeur du circuit soient fixées.

Au niveau d'optimisation 2, en plus des optimisations du niveau 1, la boucle contient une analyse de commutation d'ensembles de portes afin d'élargir la gamme des portes pouvant être prises en compte pour l'annulation. Avant la boucle, les exécutions de portes à un ou deux qubits subissent une resynthèse unique basée sur une matrice.

Au niveau d'optimisation 3, la resynthèse basée sur la matrice à deux qubits s'exécute à l'intérieur de la boucle d'optimisation. La condition de la boucle d'optimisation tente également plusieurs exécutions et choisit le point minimum en cas de fluctuation de la sortie; cela est nécessaire parce que la resynthèse basée sur la matrice est relativement instable en termes de portes concrètes.

Le niveau d'optimisation 3 est généralement très coûteux pour les grands circuits.

Phase de planification

Voir aussi

Programmation des circuits

Un guide expliquant les concepts d'ordonnancement.

L'étape d'ordonnancement, si elle est demandée, est responsable de l'insertion d'instructions explicites pour rendre explicites les périodes d'inactivité des qubits Delay afin de rendre explicites les périodes d'inactivité des qubits. Les plugins peuvent optionnellement choisir d'effectuer des transformations sensibles au temps d'attente, telles que l'insertion de séquences de découplage dynamique.

L'entrée de l'étage d'ordonnancement est un circuit compatible ISA. La sortie de l'étape d'ordonnancement doit également être un circuit compatible ISA, avec des instructions explicites qui satisfont les informations temporelles du matériel, le cas échéant Delay instructions explicites qui satisfont les informations temporelles du matériel, le cas échéant.

PropertySetL'étape de planification doit définir la node_start_time propriété dans le pipeline.

Lors de l'écriture des plugins de scène, le point d'entrée pour scheduling est qiskit.transpiler.scheduling. Les plugins intégrés sont les suivants :

Méthode
Récapitulatif
Par défautTenter de satisfaire les contraintes d'alignement temporel sans autre forme d'ordonnancement.
alapProgrammer le circuit, en préférant que les opérations se déroulent le plus tard possible.
dès que possibleProgrammer le circuit, en préférant que les opérations aient lieu le plus tôt possible.

Plugin default intégré

Ne rien faire, sauf si le circuit contient déjà des instructions avec des timings explicites. Si le circuit comporte des opérations explicitement chronométrées, il convient d'insérer un rembourrage supplémentaire pour s'assurer que ces chronométrages satisfont à l'alignement et aux autres contraintes matérielles.

Plugin alap intégré

Planifiez explicitement toutes les opérations en suivant une stratégie consistant à les effectuer « le plus tard possible ». Cette méthode utilise ALAPScheduleAnalysis l'algorithme pour déterminer l'emplacement des portes.

Plugin asap intégré

Planifiez explicitement toutes les opérations en adoptant une stratégie « dès que possible ». Cette méthode utilise ASAPScheduleAnalysis l'algorithme pour déterminer l'emplacement des portes.


Gestionnaires de passes personnalisés

Outre la modification des gestionnaires de passes prédéfinis, il est également possible de créer un gestionnaire de passes afin de mettre en place un pipeline entièrement personnalisé pour la transformation des circuits d'entrée. Vous pouvez utiliser directement la StagedPassManager classe pour cela. Vous pouvez définir des noms d'étapes au choix et y associer une PassManager instance. Par exemple, le code suivant crée un nouvel StagedPassManager objet comportant deux étapes, init et translation.

from qiskit.transpiler.passes import (
    UnitarySynthesis,
    Collect2qBlocks,
    ConsolidateBlocks,
    UnitarySynthesis,
    Unroll3qOrMore,
)
from qiskit.transpiler import PassManager, StagedPassManager

basis_gates = ["rx", "ry", "rxx"]
init = PassManager([UnitarySynthesis(basis_gates, min_qubits=3), Unroll3qOrMore()])
translate = PassManager(
    [
        Collect2qBlocks(),
        ConsolidateBlocks(basis_gates=basis_gates),
        UnitarySynthesis(basis_gates),
    ]
)

staged_pm = StagedPassManager(
    stages=["init", "translation"], init=init, translation=translate
)

Il n'y a pas de limite au nombre d'étapes que vous pouvez inclure dans un StagedPassManager. Il n'est pas nécessaire que ces étapes correspondent à celles utilisées par les pipelines prédéfinis de Qiskit.

Les fonctions du générateur Stage peuvent s'avérer utiles pour la création d'instances personnalisées StagedPassManager . Ils génèrent des gestionnaires de passes qui fournissent des fonctionnalités courantes utilisées à de nombreuses étapes. Par exemple, generate_embed_passmanager() cette commande génère un fichier PassManager permettant d’« intégrer » une configuration initiale Layout sélectionnée, issue d’une étape de mise en page, dans le périphérique cible spécifié.


Écriture de passes de transpileur personnalisées

Qiskit est conçu pour être étendu avec des passes de transpilation personnalisées et spécialisées.

Il existe deux types de passes de transpileur : les passes « d’analyse » (AnalysisPass), qui lisent un circuit et écrivent des propriétés d’analyse globales dans le PropertySet; et les passes « de transformation » (TransformationPass), qui soit modifient un DAGCircuit sur place, soit renvoient un nouveau DAGCircuit. Historiquement, Qiskit a cherché à établir une distinction très nette entre ces deux types. TransformationPassDans la version moderne de Qiskit, il est toutefois assez courant d'intégrer à la fois l'analyse et les modifications dans un seul fichier autonome, plutôt que d'essayer de tout séparer. Même si votre approche repose exclusivement sur l'analyse, il est tout de même approprié d'utiliser AnalysisPass.

Principes généraux relatifs à la paternité des passes

Si vous souhaitez modifier ou créer un nouveau DAGCircuit, vous devez rédiger un TransformationPass. Si vous souhaitez uniquement écrire dans le PropertySet sans modifier le DAGCircuit, vous devez créer un AnalysisPass. Si vous souhaitez faire les deux, écrivez un TransformationPass. Dans les deux cas, la seule méthode requise est BasePass.run(), qui constitue l'essentiel de votre passage. Cette fonction devrait accepter un seul argument (autre que self), dag: DAGCircuit. AnalysisPassTranspilerPassSi a, elle doit renvoyer un DAGCircuit (qui peut être la donnée d'entrée, si celle-ci a été modifiée sur place), tandis que si a, elle doit renvoyer None.

Si votre passe a un initialisateur, vous devez appeler super().__init__().

En règle générale, votre pass doit accepter un Target dans son initialiseur, qui décrit le matériel quantique pour lequel vous effectuez la compilation. Il est déconseillé d'accepter des contraintes « vagues », telles qu'une carte de couplage distincte et une liste de portes de base; l'approche Target décrit de manière plus précise le matériel hétérogène en général.

Lors de l'exécution d'un PassManager pipeline, lorsque la run() méthode de votre passe est appelée, vous pouvez accéder à l'attribut self.property_set pour obtenir l'état actuel PropertySet de la transpilation. Vous devez lire et écrire dans ce fichier en place. Votre passe doit indiquer clairement, le cas échéant, quels attributs de l'ensemble de propriétés il lit et dans lesquels il écrit.

Aléatoire et déterminisme

La compilation quantique implique souvent la résolution de problèmes qui sont difficiles à optimiser globalement. Dans ces cas, les algorithmes stochastiques et heuristiques sont souvent plus appropriés. Cela pose toutefois des problèmes de reproductibilité.

Il n'y a aucune exigence formelle pour qu'une passe personnalisée soit déterministe selon le même ensemble de règles que les passes Qiskit intégrées doivent suivre. Cependant, nous vous encourageons vivement à suivre ces règles dans vos propres passes; la science se nourrit de reproductibilité, et le débogage est un cauchemar lorsque vous ne pouvez pas reproduire un comportement observé précédemment.

Lors de l'écriture d'une passe de transpilateur, vous pouvez vous fier aux exemples suivants (représentatifs et non exhaustifs) qui sont déterministes, même si la sémantique exacte de l'ordre peut ne pas être entièrement spécifiée, et les passes ne doivent pas s'appuyer sur un ordre particulier :

  • Les nœuds d'ordre apparaissent dans DAGCircuit.op_nodes(). En revanche, topological_op_nodes() inclut par défaut une clé d'ordonnancement qui fait que son ordre n'est absolument pas influencé par l'ordre de suppression ou d'insertion des nœuds; il est donc entièrement déterministe à condition que le même ensemble de nœuds avec le même flux de données soit spécifié, même s'il a été construit dans un ordre différent.
  • Les arêtes d'ordre apparaissent dans DAGCircuit.edges().
  • DAGCircuit.collect_2q_runs()L'ordre dans lequel les exécutions sont renvoyées, ainsi que l'ordre exact dans lequel les nœuds d'une exécution sont rencontrés.
  • L'ordre dans lequel les nœuds sont rencontrés dans les méthodes à ordre dégénéré telles que predecessors(), bfs_successors(), etc.

En général, il faut que toutes les modifications du circuit soient effectuées dans le même ordre. Par exemple, si des nœuds doivent être ajoutés, contractés ou supprimés, l'ordre de ces modifications doit être déterminé et les remplacements doivent être spécifiés de manière déterministe.

Voici quelques conseils pour y parvenir :

  • Soyez très prudent lorsque vous itérez sur des conteneurs à base de hachage. L'itération sur Python 's set est non déterministe en raison de la randomisation par hash-seed. En Rust, l'itération sur les conteneurs à base de hachage de la bibliothèque standard, y compris les équivalents hashbrown avec leurs hachoirs par défaut, est non déterministe.

    Remarque

    L'itération sur Python 's dict est déterministe et garantie dans l'ordre d'insertion s'il n'y a pas eu de suppressions, et dans un ordre arbitraire mais toujours déterministe s'il y a eu des suppressions déterministes.

    Dans Python, si vous devez créer un set puis d'itérer dessus, envisagez plutôt d'utiliser un dict avec toutes les valeurs à None comme substitut. L'utilisation d'un set uniquement pour tester l'adhésion ne pose aucun problème.

    En Rust, utilisez indexmap et ses structures IndexMap et IndexSet pour remplacer HashMap et HashSet, respectivement; ils ont des propriétés d'itération déterministe similaires à celles de Pythondict.

  • Si votre fonction contient des composantes stochastiques, assurez-vous d'accepter une seed entrée de type et de rendre votre sortie pure si celle-ci est fournie sous forme d'entier. En général, cela implique de stocker la graine et de créer une nouvelle instance de pRNG à partir de cette graine au début de chaque appel à BasePass.run().

  • Si vous utilisez le parallélisme threadé, veillez à ce que votre résultat ne dépende pas de l'ordre dans lequel les threads effectuent leur travail ou renvoient leurs résultats partiels. Par exemple, si l'on répartit le travail dans un pool de threads et que l'on recueille les résultats à la fin, il faut s'assurer que la sortie est organisée dans un ordre correspondant à l'entrée. Dans Python, des fonctions telles que concurrent.futures.ThreadPoolExecutor.map() garantissent cela. De même, en Rust, les itérateurs parallèles de rayoncollecteront leurs résultats dans le même ordre que l'entrée.

    Attention, les \Nréductions parallèles, telles que "appliquer une fonction à chaque élément de cet itérateur et choisir celui qui minimise une certaine métrique", sont typiquement très sensibles au non-déterminisme threadé, dans le cas de dégénérescences dans la métrique. Par exemple, si deux éléments de l'itérateur produisent des résultats non égaux qui ont néanmoins la même clé de comparaison, celle qui est choisie dans un environnement threadé n'est pas déterministe. Pour éviter cela, il convient d'appliquer un système déterministe de départage pour lever la dégénérescence, par exemple en énumérant les entrées et en utilisant le numéro de séquence comme clé de départage, de sorte que si deux éléments ont le même score, celui qui correspond à une entrée antérieure est choisi de manière fiable.


Représentation des ordinateurs quantiques

Pour pouvoir compiler un programme QuantumCircuit destiné à un backend spécifique, le transcompilateur a besoin d'une représentation spécialisée de ce backend, incluant notamment ses contraintes, son jeu d'instructions, les propriétés de ses qubits, etc., afin de pouvoir compiler et optimiser efficacement. Bien que cette BackendV2 classe définisse une interface permettant d'interroger les backends et d'interagir avec eux, son champ d'application va au-delà des simples besoins du transcompilateur : elle inclut notamment la gestion de la soumission des tâches et, éventuellement, l'interfaçage avec des services distants. Les informations spécifiques requises par le transpileur sont décrites par la Target classe

Par exemple, pour créer un objet simple Target , on peut ajouter de manière itérative les descriptions des instructions qu'il prend en charge :

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)
print(target)
Target
Number of qubits: 3
Instructions:
    cx
        (0, 1):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
    u
        (0,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (1,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    rz
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    ry
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    rx
        (1,):
            Duration: 5e-08 sec.
            Error Rate: 1e-05
        (2,):
            Duration: 6e-08 sec.
            Error Rate: 2e-05
    cz
        (1, 2):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
        (2, 0):
            Duration: 5e-07 sec.
            Error Rate: 0.0001
    measure
        (0,):
            Duration: 5e-05 sec.
            Error Rate: 0.001
        (1,):
            Duration: 6e-05 sec.
            Error Rate: 0.002
        (2,):
            Duration: 5e-07 sec.
            Error Rate: 0.2

Il Target s'agit d'un backend à 3 qubits qui prend en charge CXGate entre les qubits 0 et 1, UGate sur les qubits 0 et 1, RZGate, RXGate, et RYGate sur les qubits 1 et 2, CZGate entre les qubits 1 et 2, ainsi qu'entre les qubits 2 et 0, et Measure sur tous les qubits.

Il existe également des structures de données spécifiques permettant de représenter un sous-ensemble particulier d'informations issues du Target. Par exemple, la CouplingMap classe sert uniquement à représenter les contraintes de connectivité d'un backend sous la forme d'un graphe orienté. Une carte de couplage peut être générée à partir d'un Target à l'aide de la Target.build_coupling_map() méthode. Ces structures de données sont généralement antérieures à la Target classe, mais sont toujours utilisées par certaines étapes de transcompilation qui ne fonctionnent pas encore de manière native avec une Target instance de, ou lorsqu'il s'agit de backends qui n'utilisent pas la dernière BackendV2 interface.

Par exemple, si nous voulions visualiser le CouplingMap pour l'exemple à 3 qubits Target ci-dessus :

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map().draw()

Cela illustre la connectivité globale de, qui Target correspond à la combinaison des qubits pris en charge pour CXGate et CZGate. Pour consulter les informations de connectivité de chaque élément, vous pouvez transmettre le nom de l'opération à CouplingMap.build_coupling_map():

from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map('cx').draw()
from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate

target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
    UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
    {
        (0,): InstructionProperties(error=.00001, duration=5e-8),
        (1,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RZGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RYGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    RXGate(Parameter('theta')),
    {
        (1,): InstructionProperties(error=.00001, duration=5e-8),
        (2,): InstructionProperties(error=.00002, duration=6e-8)
    }
)
target.add_instruction(
    CZGate(),
    {
        (1, 2): InstructionProperties(error=.0001, duration=5e-7),
        (2, 0): InstructionProperties(error=.0001, duration=5e-7)
    }
)
target.add_instruction(
    Measure(),
    {
        (0,): InstructionProperties(error=.001, duration=5e-5),
        (1,): InstructionProperties(error=.002, duration=6e-5),
        (2,): InstructionProperties(error=.2, duration=5e-7)
    }
)

target.build_coupling_map('cz').draw()

Planification des circuits

Voir aussi

Phase de programmation

Comment configurer les étapes de programmation des gestionnaires de passage prédéfinis.

Une fois que le circuit a été traduit dans la base cible, mappé sur le dispositif et optimisé, une phase d'ordonnancement peut être appliquée pour tenir compte éventuellement de tous les temps morts dans le circuit. À un niveau élevé, l'ordonnancement peut être considéré comme l'insertion de délais dans le circuit pour tenir compte du temps d'inactivité des qubits entre l'exécution des instructions. Par exemple, si nous commençons par un circuit tel que :

Schéma illustrant le circuit décrit précédemment.

nous pouvons alors appeler transpile() avec scheduling_method set :

from qiskit import QuantumCircuit, transpile
from qiskit.providers.fake_provider import GenericBackendV2

backend = GenericBackendV2(5)

ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0,range(1,5))

circ = transpile(ghz, backend, scheduling_method="asap")
circ.draw(output='mpl')
Schéma de circuit produit par le code précédent.

Vous pouvez voir ici que le transpileur a inséré des instructions pour tenir compte du temps d'inactivité sur chaque qubit Delay des instructions pour tenir compte du temps d'inactivité de chaque qubit. Pour avoir une meilleure idée de la synchronisation du circuit, nous pouvons également l'examiner à l'aide de la fonction timeline.draw() :

Sortie du tiroir de la chronologie des circuits.

La planification d'un circuit comporte deux étapes : l'analyse et la cartographie des contraintes, suivies d'une passe de remplissage. La première étape consiste à exécuter une analyse de planification, telle que ALAPSchedulingAnalysis ou ASAPSchedulingAnalysis , qui analyse le circuit et enregistre l'heure de début de chaque instruction du circuit à l'aide d'un algorithme de planification (« aussi tard que possible » pour ALAPSchedulingAnalysis et « aussi tôt que possible » pour ASAPSchedulingAnalysis) défini dans l'ensemble de propriétés. Une fois que le circuit dispose d'une planification initiale, il est possible d'effectuer des passes supplémentaires afin de tenir compte des contraintes de synchronisation du backend cible, telles que les contraintes d'alignement. Cette opération s'effectue généralement à l'aide de la commande ConstrainedReschedule « pass », qui adapte la planification définie dans l'ensemble de propriétés aux contraintes du backend cible. Une fois que la planification et les ajustements/replanifications sont terminés, on exécute une passe de remplissage, telle que PadDelay ou PadDynamicalDecoupling , afin d'insérer les instructions dans le circuit, ce qui achève la planification.


API Transpiler

Description du matériel

Chronique « 1 »
Chronique « 2 »
Target( [description, num_qubits, dt,...] )L'objet Target a pour but d'informer le compilateur de Qiskit des contraintes d'un backend particulier afin que le compilateur puisse compiler un circuit d'entrée en quelque chose qui fonctionne et qui est optimisé pour un appareil.
InstructionProperties( [durée, erreur] )Une représentation des propriétés de l'implémentation d'une porte.
WrapAngleRegistry()Registre de la fonction d'enroulement angulaire

Définition du gestionnaire de mots de passe

Chronique « 1 »
Chronique « 2 »
StagedPassManager( [étapes] )Un pipeline de gestionnaires de passage construit à partir d'étapes individuelles.
PassManager( [passes, max_iteration] )Gestionnaire d'un ensemble de passes et de leur ordonnancement pendant la transpilation.
PassManagerConfig( [initial_layout,...] )Configuration du gestionnaire de passe.
PassManagerCliffordTConfig( [initial_layout,...] )Configuration de Pass Manager pour la transpilation Clifford+T.
generate_preset_pass_manager([...])Créer un préréglage PassManager
generate_preset_clifford_t_pass_manager([...])StagedPassManagerGénérer un préréglage Clifford+T.
generate_preset_pbc_pass_manager([...])StagedPassManagerGénérer un préréglage PBC.

Disposition et topologie

Chronique « 1 »
Chronique « 2 »
Layout( [input_dict] )Dictée à deux voies pour représenter une disposition.
CouplingMap( [liste de couplages, description] )Graphique orienté spécifiant le couplage fixe.
TranspileLayout(initial_layout,...[,...] )Attributs de mise en page pour le circuit de sortie du transpondeur.

Planification

Chronique « 1 »
Chronique « 2 »
InstructionDurations( [durées_d'enseignement, dt] )Classe d'aide permettant de fournir des durées d'instructions pour l'ordonnancement.

Passes abstraits

Chronique « 1 »
Chronique « 2 »
TransformationPass(*args, **kwargs)Une passe de transformation : modifier le DAG, pas l'ensemble des propriétés.
AnalysisPass(*args, **kwargs)Une passe d'analyse : modifier l'ensemble des propriétés, pas le DAG.

Exceptions

TranspilerError

exception qiskit.transpiler.TranspilerError(*message)

GitHub

Bases : TranspilerAccessError

Exceptions soulevées lors de la transpilation.

Définir le message d'erreur.

TranspilerAccessError

exception qiskit.transpiler.TranspilerAccessError(*message)

GitHub

Bases : PassManagerError

SUPPRIMÉ : Exception d'erreur d'accès dans les passes du transpondeur.

Définir le message d'erreur.

CouplingError

exception qiskit.transpiler.CouplingError(*msg)

GitHub

Bases : QiskitError

Classe de base pour les erreurs soulevées par l'objet "graphe de couplage".

Définir le message d'erreur.

LayoutError

exception qiskit.transpiler.LayoutError(*msg)

GitHub

Bases : QiskitError

Erreurs soulevées par l'objet de mise en page.

Définir le message d'erreur.

CircuitTooWideForTarget

exception qiskit.transpiler.CircuitTooWideForTarget(*message)

GitHub

Bases : TranspilerError

Erreur levée si le circuit est trop large pour la cible.

Définir le message d'erreur.

InvalidLayoutError

exception qiskit.transpiler.InvalidLayoutError(*message)

GitHub

Bases : TranspilerError

Erreur soulevée lorsque la mise en page fournie par l'utilisateur n'est pas valide.

Définir le message d'erreur.

Mesure d'optimisation

Chronique « 1 »
Chronique « 2 »
OptimizationMetric(*valeurs)Métrique d'optimisation prise en compte lors de la transpilation.
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.