Créer un plugin transcompilateur
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
La création d'un plugin de transpilation est un excellent moyen de partager votre code de transpilation avec l'ensemble de la communauté Qiskit, permettant ainsi aux autres utilisateurs de bénéficier des fonctionnalités que vous avez développées. Nous vous remercions de l'intérêt que vous portez à la communauté Qiskit!
Avant de créer un plugin de transposition, vous devez décider quel type de plugin est approprié à votre situation. Il existe trois types de plugins de transposition :
- Transpiler stage plugin. Choisissez cette option si vous définissez un gestionnaire de passage qui peut être substitué à l'une des six étapes d'un gestionnaire de passage prédéfini.
- Plugin de synthèse unitaire. Choisissez cette option si votre code de transpilation prend en entrée une matrice unitaire (représentée sous la forme d'un tableau Numpy) et produit une description d'un circuit quantique mettant en œuvre cette matrice unitaire.
- Plugin de synthèse de haut niveau. Choisissez cette option si votre code de transpilation prend en entrée un "objet de haut niveau" tel qu'un opérateur de Clifford ou une fonction linéaire et produit en sortie une description d'un circuit quantique mettant en œuvre cet objet de haut niveau. Les objets de haut niveau sont représentés par des sous-classes de la classe Opération.
Une fois que vous avez déterminé le type de plugin à créer, suivez les étapes suivantes pour créer le plugin :
- Créer une sous-classe de la classe de plugin abstraite appropriée :
- PassManagerStagePlugin pour un plugin d'étape de transpilation,
- UnitarySynthesisPlugin pour un plugin de synthèse unitaire, et
- HighLevelSynthesisPlugin pour un plugin de synthèse de haut niveau.
- Exposer la classe en tant que point d'entrée setuptools dans les métadonnées du paquetage, typiquement en éditant le fichier
pyproject.toml,setup.cfg, ousetup.pypour votre paquetage Python.
Il n'y a pas de limite au nombre de plugins qu'un paquet peut définir, mais chaque plugin doit avoir un nom unique. Le SDK Qiskit lui-même comprend un certain nombre de plugins, dont les noms sont également réservés. Les noms réservés sont les suivants
- Plugins d'étape Transpiler : Voir ce tableau.
- Plugins de synthèse unitaire :
default,aqc,sk - Plugins de synthèse de haut niveau :
Classe d'opération | Nom de l'opération | Noms réservés |
|---|---|---|
| Clifford | clifford | default, ag, bm, greedy, layers, lnn |
| LinearFunction | linear_function | default, kms, pmh |
| PermutationGate | permutation | default, kms, basic, acg, token_swapper |
Dans les sections suivantes, nous présentons des exemples de ces étapes pour les différents types de plugins. Dans ces exemples, nous supposons que nous créons un paquet Python appelé my_qiskit_plugin. Pour plus d'informations sur la création de paquets Python, vous pouvez consulter ce tutoriel sur le site Python.
Exemple : créer un plugin de transpilateur
Dans cet exemple, nous créons un plugin d'étape de transpilation pour l'étape layout (voir Étapes de transpilation pour une description des 6 étapes du pipeline de transpilation intégré de Qiskit).
Notre plugin exécute simplement VF2Layout pendant un nombre d'essais qui dépend du niveau d'optimisation demandé.
Tout d'abord, nous créons une sous-classe de PassManagerStagePlugin. Il existe une méthode que nous devons mettre en œuvre, appelée pass_manager. Cette méthode prend en entrée un PassManagerConfig et renvoie le gestionnaire de passe que nous sommes en train de définir. L'objet PassManagerConfig stocke des informations sur le backend cible, telles que sa carte de couplage et ses portes de base.
# This import is needed for python versions prior to 3.10
from __future__ import annotations
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import common
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePlugin,
)
class MyLayoutPlugin(PassManagerStagePlugin):
def pass_manager(
self,
pass_manager_config: PassManagerConfig,
optimization_level: int | None = None,
) -> PassManager:
layout_pm = PassManager(
[
VF2Layout(
coupling_map=pass_manager_config.coupling_map,
properties=pass_manager_config.backend_properties,
max_trials=optimization_level * 10 + 1,
target=pass_manager_config.target,
)
]
)
layout_pm += common.generate_embed_passmanager(
pass_manager_config.coupling_map
)
return layout_pmMaintenant, nous exposons le plugin en ajoutant un point d'entrée dans les métadonnées de notre paquet Python.
Nous supposons ici que la classe que nous avons définie est exposée dans un module appelé my_qiskit_plugin, par exemple en étant importée dans le fichier __init__.py du module my_qiskit_plugin .
Nous éditons le fichier pyproject.toml, setup.cfg, ou setup.py de notre paquet (selon le type de fichier que vous avez choisi pour stocker les métadonnées de votre projet Python ) :
[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"[options.entry_points]
qiskit.transpiler.layout =
my_layout = my_qiskit_plugin:MyLayoutPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.transpiler.layout': [
'my_layout = my_qiskit_plugin:MyLayoutPlugin',
]
}
)Voir le tableau des étapes du plugin de transposition pour les points d'entrée et les attentes pour chaque étape de transposition.
Pour vérifier que votre plugin est bien détecté par Qiskit, installez le paquet de votre plugin et suivez les instructions à Transpiler plugins pour lister les plugins installés, et assurez-vous que votre plugin apparaît dans la liste :
from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins
list_stage_plugins("layout")Output:
['default', 'dense', 'sabre', 'trivial']
Si notre exemple de plugin était installé, le nom my_layout apparaîtrait dans cette liste.
Si vous souhaitez utiliser une étape de transposition intégrée comme point de départ de votre plugin d'étape de transposition, vous pouvez obtenir le gestionnaire de passes d'une étape de transposition intégrée en utilisant la commande PassManagerStagePluginManager. La cellule de code suivante montre comment procéder pour obtenir l'étape d'optimisation intégrée pour le niveau d'optimisation 3.
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePluginManager,
)
# Initialize the plugin manager
plugin_manager = PassManagerStagePluginManager()
# Here we create a pass manager config to use as an example.
# Instead, you should use the pass manager config that you already received as input
# to the pass_manager method of your PassManagerStagePlugin.
pass_manager_config = PassManagerConfig()
# Obtain the desired built-in transpiler stage
optimization = plugin_manager.get_passmanager_stage(
"optimization", "default", pass_manager_config, optimization_level=3
)Exemple : Créer un plugin de synthèse unitaire
Dans cet exemple, nous allons créer un plugin de synthèse unitaire qui utilise simplement la passe de transpilation intégrée pour synthétiser une porte UnitarySynthesis pour synthétiser une porte. Bien sûr, votre propre plugin fera quelque chose de plus intéressant que cela.
La classe UnitarySynthesisPlugin définit l'interface et le contrat pour les plugins de synthèse unitaire de la synthèse unitaire. La méthode principale est run, qui prend en entrée un tableau Numpy stockant une matrice unitaire et renvoie un DAGCircuit représentant le circuit synthétisé à partir de cette matrice unitaire.
Outre la méthode run , un certain nombre de méthodes relatives aux propriétés doivent être définies.
Voir UnitarySynthesisPlugin pour la documentation de toutes les propriétés requises.
Créons notre sous-classe UnitarySynthesisPlugin :
import numpy as np
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.converters import circuit_to_dag
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.quantum_info import Operator
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes.synthesis.plugin import UnitarySynthesisPlugin
class MyUnitarySynthesisPlugin(UnitarySynthesisPlugin):
@property
def supports_basis_gates(self):
# Returns True if the plugin can target a list of basis gates
return True
@property
def supports_coupling_map(self):
# Returns True if the plugin can synthesize for a given coupling map
return False
@property
def supports_natural_direction(self):
# Returns True if the plugin supports a toggle for considering
# directionality of 2-qubit gates
return False
@property
def supports_pulse_optimize(self):
# Returns True if the plugin can optimize pulses during synthesis
return False
@property
def supports_gate_lengths(self):
# Returns True if the plugin can accept information about gate lengths
return False
@property
def supports_gate_errors(self):
# Returns True if the plugin can accept information about gate errors
return False
@property
def supports_gate_lengths_by_qubit(self):
# Returns True if the plugin can accept information about gate lengths
# (The format of the input differs from supports_gate_lengths)
return False
@property
def supports_gate_errors_by_qubit(self):
# Returns True if the plugin can accept information about gate errors
# (The format of the input differs from supports_gate_errors)
return False
@property
def min_qubits(self):
# Returns the minimum number of qubits the plugin supports
return None
@property
def max_qubits(self):
# Returns the maximum number of qubits the plugin supports
return None
@property
def supported_bases(self):
# Returns a dictionary of supported bases for synthesis
return None
def run(self, unitary: np.ndarray, **options) -> DAGCircuit:
basis_gates = options["basis_gates"]
synth_pass = UnitarySynthesis(basis_gates, min_qubits=3)
qubits = QuantumRegister(3)
circuit = QuantumCircuit(qubits)
circuit.append(Operator(unitary).to_instruction(), qubits)
dag_circuit = synth_pass.run(circuit_to_dag(circuit))
return dag_circuitSi vous constatez que les entrées disponibles pour le run sont insuffisantes pour vos besoins, veuillez ouvrir un dossier expliquant vos exigences. Les modifications apportées à l'interface du plugin, telles que l'ajout d'entrées optionnelles supplémentaires, seront effectuées de manière à ce qu'elles ne nécessitent pas de modifications des plugins existants.
Toutes les méthodes précédées du préfixe supports_ sont réservées à une classe dérivée UnitarySynthesisPlugin en tant que partie de l'interface. Vous ne devez pas définir de méthodes supports_* personnalisées sur une sous-classe qui ne sont pas définies dans la classe abstraite.
Maintenant, nous exposons le plugin en ajoutant un point d'entrée dans les métadonnées de notre paquet Python.
Nous supposons ici que la classe que nous avons définie est exposée dans un module appelé my_qiskit_plugin, par exemple en étant importée dans le fichier __init__.py du module my_qiskit_plugin .
Nous modifions le fichier pyproject.toml, setup.cfg, ou setup.py de notre paquet :
[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"[options.entry_points]
qiskit.unitary_synthesis =
my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.unitary_synthesis': [
'my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin',
]
}
)Comme précédemment, si votre projet utilise setup.cfg ou setup.py au lieu de pyproject.toml, consultez la documentation de setuptools pour adapter ces lignes à votre situation.
Pour vérifier que votre plugin est bien détecté par Qiskit, installez le paquet de votre plugin et suivez les instructions à Transpiler plugins pour lister les plugins installés, et assurez-vous que votre plugin apparaît dans la liste :
from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names
unitary_synthesis_plugin_names()Output:
['aqc', 'clifford', 'default', 'gridsynth', 'sk']
Si notre exemple de plugin était installé, le nom my_unitary_synthesis apparaîtrait dans cette liste.
Pour accommoder les plugins de synthèse unitaire qui exposent des options multiples, l'interface du plugin comporte une option permettant aux utilisateurs de fournir un de configuration de forme libre. Elle sera transmise à la méthode run par l'intermédiaire du mot-clé options . Si votre plugin dispose de ces options de configuration, vous devez les documenter clairement.
Exemple : Créer un plugin de synthèse de haut niveau
Dans cet exemple, nous allons créer un plugin de synthèse de haut niveau qui utilise simplement la fonction intégrée synth_clifford_bm pour synthétiser un opérateur de Clifford.
La classe HighLevelSynthesisPlugin définit l'interface et le contrat pour les plugins de synthèse de haut niveau. La méthode principale est run.
L'argument positionnel high_level_object est une opération représentant l'objet de "haut niveau" à synthétiser. Par exemple, il peut s'agir d'un LinearFunction ou un Clifford.
Les arguments suivants sont présents dans les mots-clés :
targetspécifie le backend cible, ce qui permet au plugin d'accéder à toutes les informations spécifiques à la cible, telles que la carte de couplage, le jeu de portes supporté, etccoupling_mapne spécifie que la carte de couplage et n'est utilisé que sitargetn'est pas spécifié.qubitsspécifie la liste des qubits sur lesquels l'objet de haut niveau est défini, dans le cas où la synthèse est effectuée sur le circuit physique est défini, dans le cas où la synthèse est effectuée sur le circuit physique. Une valeur deNoneindique que la disposition n'a pas encore été choisie et que les qubits physiques de la cible ou de la carte de couplage sur laquelle cette opération est effectuée n'ont pas encore été déterminés.optionsun dictionnaire de configuration de forme libre pour les options spécifiques au plugin. Si votre plugin dispose de ces options de configuration, vous vous devez les documenter clairement.
La méthode run renvoie un QuantumCircuit représentant le circuit synthétisé à partir de cet objet de haut niveau.
Il est également autorisé à renvoyer None, indiquant que le plugin n'est pas en mesure de synthétiser l'objet de haut niveau donné.
La synthèse proprement dite des objets de haut niveau est réalisée par le programme HighLevelSynthesis transpileur.
Outre la méthode run , un certain nombre de méthodes relatives aux propriétés doivent être définies.
Voir HighLevelSynthesisPlugin pour la documentation de toutes les propriétés requises.
Définissons notre sous-classe HighLevelSynthesisPlugin :
from qiskit.synthesis import synth_clifford_bm
from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin
class MyCliffordSynthesisPlugin(HighLevelSynthesisPlugin):
def run(
self,
high_level_object,
coupling_map=None,
target=None,
qubits=None,
**options,
) -> QuantumCircuit:
if high_level_object.num_qubits <= 3:
return synth_clifford_bm(high_level_object)
else:
return NoneCe plugin synthétise des objets de type Clifford qui possèdent au plus 3 qubits, en utilisant la méthode synth_clifford_bm .
Maintenant, nous exposons le plugin en ajoutant un point d'entrée dans les métadonnées de notre paquet Python.
Nous supposons ici que la classe que nous avons définie est exposée dans un module appelé my_qiskit_plugin, par exemple en étant importée dans le fichier __init__.py du module my_qiskit_plugin .
Nous modifions le fichier pyproject.toml, setup.cfg, ou setup.py de notre paquet :
[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"[options.entry_points]
qiskit.synthesis =
clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.synthesis': [
'clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin',
]
}
)Le site name se compose de deux parties séparées par un point (.) :
- Le nom du type d' opération que le plugin synthétise (dans ce cas,
clifford). Notez que cette chaîne correspond à l'attributnamede la classe Operation, et non au nom de la classe elle-même. - Le nom du plugin (dans ce cas,
special).
Comme précédemment, si votre projet utilise setup.cfg ou setup.py au lieu de pyproject.toml, consultez la documentation de setuptools pour adapter ces lignes à votre situation.
Pour vérifier que votre plugin est bien détecté par Qiskit, installez le paquet de votre plugin et suivez les instructions à Transpiler plugins pour lister les plugins installés, et assurez-vous que votre plugin apparaît dans la liste :
from qiskit.transpiler.passes.synthesis import (
high_level_synthesis_plugin_names,
)
high_level_synthesis_plugin_names("clifford")Output:
['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']
Si notre exemple de plugin était installé, le nom my_clifford_synthesis apparaîtrait dans cette liste.
- Soumettez votre plugin à l'écosystème Qiskit!
- Consultez les didacticiels pour des exemples de transposition et d'exécution de circuits quantiques.