Skip to main content
IBM Quantum Platform

Étapes du transpilateur

  • Le code de cette page a été développé en tenant compte des exigences suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.

    qiskit[all]~=2.5.1
    

Cette page décrit les étapes du pipeline de transpilation préconstruit dans le SDK Qiskit. Il y a six étapes :

  1. init
  2. layout
  3. routing
  4. translation
  5. optimization
  6. scheduling

La fonction generate_preset_pass_manager crée un gestionnaire de passage par étapes prédéfini composé de ces étapes. Les passes spécifiques qui composent chaque étape dépendent des arguments transmis à generate_preset_pass_manager. optimization_level est un argument de position qui doit être spécifié; c'est un nombre entier qui peut être 0, 1, 2 ou 3. Des valeurs plus élevées indiquent une optimisation plus importante mais plus coûteuse (voir les options de configuration et les valeurs par défaut de la transpilation ).

La méthode recommandée pour transpiler un circuit consiste à créer un gestionnaire de passes prédéfini et à le faire fonctionner sur le circuit, comme décrit dans Transpiler avec des gestionnaires de passes. Cependant, une alternative plus simple mais moins personnalisable consiste à utiliser la fonction transpile fonction. Cette fonction accepte le circuit directement comme argument. Comme pour generate_preset_pass_manager, les passes de transposition spécifiques utilisées dépendent des arguments, tels que optimization_level, transmis à transpile. En fait, en interne, la fonction transpile appelle generate_preset_pass_manager pour créer un gestionnaire de passage prédéfini et l'exécuter sur le circuit.


Phase initiale

Cette première étape ne fait pas grand-chose par défaut et est surtout utile si vous souhaitez inclure vos propres optimisations initiales. Comme la plupart des algorithmes de disposition et de routage ne sont conçus que pour fonctionner avec des portes à un ou deux qubits, cette étape est également utilisée pour convertir toutes les portes qui fonctionnent sur plus de deux qubits en portes qui ne fonctionnent que sur un ou deux qubits.

Pour plus d'informations sur la mise en œuvre de vos propres optimisations initiales pour cette étape, voir la section sur les plugins et la personnalisation des gestionnaires de passe.


Phase de mise en page

L'étape suivante concerne la configuration ou la connectivité du backend vers lequel le circuit sera acheminé. En général, les circuits quantiques sont des entités abstraites dont les qubits sont des représentations « virtuelles » ou « logiques » des qubits réels utilisés dans les calculs. Pour exécuter une séquence de portes, il est nécessaire d'établir une correspondance biunivoque entre les qubits « virtuels » et les qubits « physiques » d'un dispositif quantique réel. Ce mappage est stocké sous la forme d'un Layout objet et fait partie des contraintes définies dans l'architecture de jeu d'instructions (ISA) d'un backend.

Cette image montre la conversion des qubits de la représentation schématique à un diagramme illustrant la manière dont les qubits sont connectés sur le processeur quantique (QPU). Conversion
des qubits

Le choix du mappage est extrêmement important pour minimiser le nombre d'opérations SWAP nécessaires pour mapper le circuit d'entrée sur la topologie du dispositif et garantir l'utilisation des qubits les mieux calibrés. En raison de l'importance de cette étape, les gestionnaires de la présélection essaient plusieurs méthodes différentes pour trouver la meilleure disposition. En règle générale, cela implique deux étapes : d'abord, essayer de trouver une disposition "parfaite" (une disposition qui ne nécessite aucune opération SWAP), et ensuite, une passe heuristique qui tente de trouver la meilleure disposition à utiliser si une disposition parfaite ne peut être trouvée. Deux sites Passes sont généralement utilisés pour cette première étape :

  • TrivialLayout:Mappe naïvement chaque qubit virtuel au même qubit physique numéroté sur l'appareil (c'est-à-dire, [0,1,1,3] -> [0,1,1,3] ). Il s'agit d'un comportement historique utilisé uniquement dans optimzation_level=1 pour essayer de trouver une disposition parfaite. En cas d'échec, l'essai suivant est celui de VF2Layout .
  • VF2Layout: Il s'agit d'un site AnalysisPass qui sélectionne une disposition idéale en traitant cette étape comme un problème d'isomorphisme de sous-graphes, résolu par l'algorithme VF2++. Si plus d'une disposition est trouvée, une heuristique de notation est exécutée pour sélectionner la cartographie présentant l'erreur moyenne la plus faible.

Ensuite, pour l'étape heuristique, deux passages sont utilisés par défaut :

  • DenseLayout: Trouve le sous-graphe du dispositif avec la plus grande connectivité et qui a le même nombre de qubits que le circuit (utilisé pour le niveau d'optimisation 1 s'il y a des opérations de flux de contrôle (telles que IfElseOp ) présentes dans le circuit).
  • SabreLayout: Ce passage sélectionne une disposition en partant d'une disposition aléatoire initiale et en exécutant de manière répétée SabreSwap l'algorithme. Ce passage n'est utilisé que dans les niveaux d'optimisation 1, 2 et 3 si aucune disposition parfaite n'est trouvée via le VF2Layout passage. Pour plus de détails sur cet algorithme, consultez l'article « arXiv:1809.02573 » (Algorithmes de calcul de la valeur de la confiance).

Étape de routage

Pour mettre en œuvre une porte à deux qubits entre des qubits qui ne sont pas directement connectés sur un dispositif quantique, une ou plusieurs portes SWAP doivent être insérées dans le circuit pour déplacer les états des qubits jusqu'à ce qu'ils soient adjacents sur la carte des portes du dispositif. Chaque porte SWAP représente une opération coûteuse et bruyante. Ainsi, la recherche du nombre minimum de portes SWAP nécessaires pour transposer un circuit sur un dispositif donné est une étape importante du processus de transpilation. Par souci d'efficacité, cette étape est généralement calculée par défaut en même temps que l'étape de mise en page, mais elles sont logiquement distinctes l'une de l'autre. L'étape de mise en page sélectionne les qubits matériels à utiliser, tandis que l'étape de routage insère la quantité appropriée de portes SWAP afin d'exécuter les circuits à l'aide de la mise en page sélectionnée.

Cependant, il est difficile de trouver la cartographie SWAP optimale. En fait, il s'agit d'un problème NP-hard, dont le coût de calcul est prohibitif pour tous les dispositifs quantiques et circuits d'entrée, à l'exception des plus petits. Pour contourner ce problème, Qiskit utilise un algorithme heuristique stochastique appelé SabreSwap pour calculer une bonne correspondance SWAP, mais pas nécessairement optimale. L'utilisation d'une méthode stochastique signifie que les circuits générés ne sont pas garantis d'être les mêmes lors d'exécutions répétées. En effet, l'exécution répétée d'un même circuit se traduit par une distribution des profondeurs de circuit et des nombres de portes à la sortie. C'est pour cette raison que de nombreux utilisateurs choisissent d'exécuter plusieurs fois la fonction de routage (ou l'ensemble du site StagedPassManager) et de sélectionner les circuits les moins profonds dans la distribution des sorties.

Prenons par exemple un circuit GHZ de 15 qubits exécuté 100 fois, en utilisant un "mauvais" circuit (déconnecté) initial_layout.

import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2

backend = GenericBackendV2(15)


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

depths = []
for seed in range(100):
    pass_manager = generate_preset_pass_manager(
        optimization_level=1,
        backend=backend,
        layout_method="trivial",  # Fixed layout mapped in circuit order
        seed_transpiler=seed,  # For reproducible results
    )
    depths.append(pass_manager.run(ghz).depth())

plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)

Output:

Text(0, 0.5, 'Counts')
Output of the previous code cell

Cette large distribution démontre à quel point il est difficile pour le SWAP mapper de calculer la meilleure cartographie. Pour mieux comprendre, examinons à la fois le circuit exécuté et les qubits choisis sur le matériel.

ghz.draw("mpl", idle_wires=False)

Output:

Output of the previous code cell
from qiskit.visualization import plot_circuit_layout

# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)

Output:

Output of the previous code cell

Comme vous pouvez le voir, ce circuit doit exécuter une porte à deux qubits entre les qubits 0 et 14, qui sont très éloignés l'un de l'autre sur le graphe de connectivité. L'exécution de ce circuit nécessite donc l'insertion de portes SWAP pour exécuter toutes les portes à deux qubits à l'aide de la passe SabreSwap .

Notez également que l'algorithme SabreSwap est différent de la méthode SabreLayout plus large de l'étape précédente. Par défaut, SabreLayout exécute à la fois la mise en page et le routage, et renvoie le circuit transformé. Ceci est fait pour quelques raisons techniques particulières spécifiées dans la page de référence de l'API de la passe.


Étape de traduction

Lorsque vous écrivez un circuit quantique, vous êtes libre d'utiliser n'importe quelle porte quantique (opération unitaire) de votre choix, ainsi qu'un ensemble d'opérations autres que les portes, telles que la mesure de qubits ou les instructions de réinitialisation. Cependant, la plupart des dispositifs quantiques ne prennent en charge nativement qu'une poignée d'opérations quantiques, qu'il s'agisse de portes quantiques ou d'autres opérations. Ces portes natives font partie de la définition de l'ISA d'une cible, et cette étape du préréglage PassManagers convertit (ou «* déroule* ») les portes spécifiées dans un circuit en portes de base natives d'un backend donné. Il s'agit d'une étape importante, car elle permet au circuit d'être exécuté par le backend, mais elle entraîne généralement une augmentation de la profondeur et du nombre de portes logiques.

Deux cas particuliers sont particulièrement importants et permettent d'illustrer le rôle de cette étape.

  1. Si une porte SWAP n'est pas une porte native du backend cible, trois portes CNOT sont nécessaires :
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")

Output:

native gates:['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Output of the previous code cell

En tant que produit de trois portes CNOT, un SWAP est une opération coûteuse à réaliser sur des dispositifs quantiques bruyants. Toutefois, ces opérations sont généralement nécessaires pour intégrer un circuit dans les connectivités de porte limitées de nombreux dispositifs. La minimisation du nombre de portes SWAP dans un circuit est donc un objectif primordial dans le processus de transpilation.

  1. Une porte de Toffoli, ou porte contrôlée-contrôlée-non (ccx), est une porte à trois qubits. Étant donné que notre ensemble de portes de base ne comprend que des portes à un ou deux qubits, cette opération doit être décomposée. Cependant, il est assez coûteux :
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")

Output:

Output of the previous code cell

Pour chaque porte de Toffoli dans un circuit quantique, le matériel peut exécuter jusqu'à six portes CNOT et une poignée de portes à qubit unique. Cet exemple démontre que tout algorithme utilisant plusieurs portes de Toffoli aboutira à un circuit de grande profondeur et sera donc sensiblement affecté par le bruit.


Phase d'optimisation

Cette étape est centrée sur la décomposition des circuits quantiques dans l'ensemble de portes de base du dispositif cible, et doit lutter contre l'augmentation de la profondeur résultant des étapes de mise en page et de routage. Heureusement, il existe de nombreuses routines permettant d'optimiser les circuits en combinant ou en éliminant des portes. Dans certains cas, ces méthodes sont si efficaces que les circuits de sortie ont une profondeur inférieure à celle des circuits d'entrée, même après la mise en page et l'acheminement vers la topologie matérielle. Dans d'autres cas, il n'y a pas grand-chose à faire et le calcul peut être difficile à effectuer sur des appareils bruyants. C'est à ce stade que les différents niveaux d'optimisation commencent à se différencier.

En outre, cette étape exécute également quelques vérifications finales pour s'assurer que toutes les instructions du circuit sont composées des portes de base disponibles sur le backend cible.

L'exemple ci-dessous, qui utilise un état GHZ, montre les effets des différents niveaux d'optimisation sur la profondeur du circuit et le nombre de portes.

Note

Le résultat de la transpilation varie en raison du mappeur SWAP stochastique. Par conséquent, les chiffres ci-dessous changeront probablement à chaque fois que vous exécuterez le code.

état GHZ à 15 qubits
État GHZ à 15 qubits avant transpilation

Le code suivant construit un état GHZ à 15 qubits et compare la transpilation à l'adresse optimization_levels en termes de profondeur de circuit, de nombre de portes et de nombre de portes à plusieurs qubits.

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

depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
    pass_manager = generate_preset_pass_manager(
        optimization_level=level,
        backend=backend,
        seed_transpiler=1234,
    )
    circ = pass_manager.run(ghz)
    depths.append(circ.depth())
    gate_counts.append(sum(circ.count_ops().values()))
    multiqubit_gate_counts.append(circ.count_ops()["cx"])

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()

Output:

Output of the previous code cell

Planification

Cette dernière étape n'est exécutée que si elle est explicitement demandée (comme l'étape Init) et n'est pas exécutée par défaut (bien qu'une méthode puisse être spécifiée en définissant l'argument scheduling_method lors de l'appel à generate_preset_pass_manager). L'étape de programmation est généralement utilisée une fois que le circuit a été traduit dans la base cible, mappé sur le dispositif et optimisé. Ces passes visent à tenir compte de toutes les périodes d'inactivité dans un circuit. À un niveau élevé, la passe d'ordonnancement peut être considérée comme l'insertion explicite d'instructions de retard pour tenir compte du temps d'inactivité entre les exécutions de portes et pour contrôler la durée d'exécution du circuit sur le backend.

Par exemple :

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


# Use fake backend
backend = GenericBackendV2(5)

# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    scheduling_method="asap",
    seed_transpiler=1234,
)


circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)

Output:

Output of the previous code cell
Circuit avec instructions de retardement

Le transpileur a inséré des instructions Delay 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() :

Vue timeline.draw () du même circuit

L'ordonnancement d'un circuit comporte deux parties : l'analyse et la cartographie des contraintes, suivies d'une passe de remplissage. La première partie nécessite l'exécution d'une passe d'analyse d'ordonnancement (par défaut, il s'agit de ALAPSchedulingAnalysis), qui analyse le circuit et enregistre l'heure de début de chaque instruction du circuit dans un calendrier. Une fois que le circuit dispose d'une programmation initiale, des passes supplémentaires peuvent être exécutées pour tenir compte de toute contrainte de temps sur le backend cible. Enfin, une passe de rembourrage telle que PadDelay ou PadDynamicalDecoupling peut être exécutée.


Etapes suivantes

Recommandations
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.