Crear un plugin para el transpilador
Versiones de los paquetes
El código de esta página fue desarrollado con los siguientes requisitos. Recomendamos usar estas versiones o superiores.
qiskit[all]~=2.3.0
Crear un plugin para el transpilador es una excelente manera de compartir tu código de transpilación con la comunidad de Qiskit, permitiendo que otros usuarios se beneficien de la funcionalidad que has desarrollado. ¡Gracias por tu interés en contribuir a la comunidad de Qiskit!
Antes de crear un plugin para el transpilador, debes decidir qué tipo de plugin es el adecuado para tu situación. Hay tres tipos de plugins:
- Plugin de etapa del transpilador. Elige este si estás definiendo un pass manager que puede sustituir a una de las 6 etapas de un pass manager de etapas preestablecido.
- Plugin de síntesis unitaria. Elige este si tu código de transpilación recibe como entrada una matriz unitaria (representada como un array de Numpy) y devuelve la descripción de un circuito cuántico que implementa dicha unitaria.
- Plugin de síntesis de alto nivel. Elige este si tu código de transpilación recibe como entrada un "objeto de alto nivel", como un operador Clifford o una función lineal, y devuelve la descripción de un circuito cuántico que implementa dicho objeto. Los objetos de alto nivel están representados por subclases de la clase Operation.
Una vez que hayas determinado qué tipo de plugin crear, sigue estos pasos:
- Crea una subclase de la clase abstracta de plugin correspondiente:
- PassManagerStagePlugin para un plugin de etapa del transpilador,
- UnitarySynthesisPlugin para un plugin de síntesis unitaria, y
- HighLevelSynthesisPlugin para un plugin de síntesis de alto nivel.
- Expón la clase como un entry point de setuptools en los metadatos del paquete, generalmente editando el archivo
pyproject.toml,setup.cfgosetup.pyde tu paquete de Python.
No hay límite en el número de plugins que puede definir un solo paquete, pero cada plugin debe tener un nombre único. El propio SDK de Qiskit incluye varios plugins cuyos nombres están reservados:
- Plugins de etapa del transpilador: consulta esta tabla.
- Plugins de síntesis unitaria:
default,aqc,sk - Plugins de síntesis de alto nivel:
| Clase de operación | Nombre de operación | Nombres reservados |
|---|---|---|
| Clifford | clifford | default, ag, bm, greedy, layers, lnn |
| LinearFunction | linear_function | default, kms, pmh |
| PermutationGate | permutation | default, kms, basic, acg, token_swapper |
En las secciones siguientes se muestran ejemplos de estos pasos para los distintos tipos de plugins. En estos ejemplos, asumimos que estamos creando un paquete de Python llamado my_qiskit_plugin. Para información sobre cómo crear paquetes de Python, puedes consultar este tutorial en el sitio web de Python.
Ejemplo: Crear un plugin de etapa del transpilador
En este ejemplo, creamos un plugin de etapa del transpilador para la etapa layout (consulta Etapas del transpilador para una descripción de las 6 etapas del pipeline de transpilación integrado en Qiskit).
Nuestro plugin simplemente ejecuta VF2Layout con un número de intentos que depende del nivel de optimización solicitado.
Primero, creamos una subclase de PassManagerStagePlugin. Hay un método que debemos implementar, llamado pass_manager. Este método recibe como entrada un PassManagerConfig y devuelve el pass manager que estamos definiendo. El objeto PassManagerConfig almacena información sobre el backend de destino, como su mapa de acoplamiento y sus puertas base.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
# 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_pm
Ahora, exponemos el plugin añadiendo un entry point en los metadatos de nuestro paquete de Python.
Aquí asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo importándola en el archivo __init__.py del módulo my_qiskit_plugin.
Editamos el archivo pyproject.toml, setup.cfg o setup.py de nuestro paquete (según el tipo de archivo que hayas elegido para almacenar los metadatos de tu proyecto Python):
- pyproject.toml
- setup.cfg
- setup.py
[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"
[options.entry_points]
qiskit.transpiler.layout =
my_layout = my_qiskit_plugin:MyLayoutPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.transpiler.layout': [
'my_layout = my_qiskit_plugin:MyLayoutPlugin',
]
}
)
Consulta la tabla de etapas de plugins del transpilador para ver los entry points y los requisitos de cada etapa.
Para verificar que Qiskit detecta correctamente tu plugin, instala el paquete del plugin y sigue las instrucciones en Plugins del transpilador para listar los plugins instalados, asegurándote de que tu plugin aparece en la lista:
from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins
list_stage_plugins("layout")
['default', 'dense', 'sabre', 'trivial']
Si nuestro plugin de ejemplo estuviera instalado, el nombre my_layout aparecería en esta lista.
Si quieres usar una etapa del transpilador integrada como punto de partida para tu plugin, puedes obtener el pass manager de esa etapa usando PassManagerStagePluginManager. El siguiente bloque de código muestra cómo obtener la etapa de optimización integrada para el nivel de optimización 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
)
Ejemplo: Crear un plugin de síntesis unitaria
En este ejemplo, crearemos un plugin de síntesis unitaria que simplemente usa el paso de transpilación integrado UnitarySynthesis para sintetizar una puerta. Por supuesto, tu propio plugin hará algo más interesante que eso.
La clase UnitarySynthesisPlugin define la interfaz y el contrato para los plugins de síntesis unitaria. El método principal es
run,
que recibe como entrada un array de Numpy con una matriz unitaria
y devuelve un DAGCircuit que representa el circuito sintetizado a partir de esa matriz unitaria.
Además del método run, hay una serie de métodos de propiedad que deben definirse.
Consulta UnitarySynthesisPlugin para ver la documentación de todas las propiedades requeridas.
Vamos a crear nuestra subclase de 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_circuit
Si los datos de entrada disponibles para el método run
son insuficientes para tus necesidades, por favor abre un issue explicando tus requisitos. Los cambios en la interfaz del plugin, como la adición de entradas opcionales, se realizarán de forma retrocompatible para no requerir cambios en los plugins existentes.
Todos los métodos con el prefijo supports_ están reservados en una clase derivada de UnitarySynthesisPlugin como parte de la interfaz. No debes definir ningún método supports_* personalizado en una subclase que no esté definido en la clase abstracta.
Ahora, exponemos el plugin añadiendo un entry point en los metadatos de nuestro paquete de Python.
Aquí asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo importándola en el archivo __init__.py del módulo my_qiskit_plugin.
Editamos el archivo pyproject.toml, setup.cfg o setup.py de nuestro paquete:
- pyproject.toml
- setup.cfg
- setup.py
[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:MyUnitarySynthesisPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.unitary_synthesis': [
'my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin',
]
}
)
Como antes, si tu proyecto usa setup.cfg o setup.py en lugar de pyproject.toml, consulta la documentación de setuptools para saber cómo adaptar estas líneas a tu situación.
Para verificar que Qiskit detecta correctamente tu plugin, instala el paquete del plugin y sigue las instrucciones en Plugins del transpilador para listar los plugins instalados, asegurándote de que tu plugin aparece en la lista:
from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names
unitary_synthesis_plugin_names()
['aqc', 'clifford', 'default', 'gridsynth', 'sk']
Si nuestro plugin de ejemplo estuviera instalado, el nombre my_unitary_synthesis aparecería en esta lista.
Para dar cabida a plugins de síntesis unitaria que exponen múltiples opciones,
la interfaz del plugin permite a los usuarios proporcionar un diccionario de configuración libre.
Este se pasará al método run mediante el argumento de palabra clave options. Si tu plugin tiene estas opciones de configuración, debes documentarlas claramente.
Ejemplo: Crear un plugin de síntesis de alto nivel
En este ejemplo, crearemos un plugin de síntesis de alto nivel que simplemente usa la función integrada synth_clifford_bm para sintetizar un operador Clifford.
La clase HighLevelSynthesisPlugin define la interfaz y el contrato para los plugins de síntesis de alto nivel. El método principal es run.
El argumento posicional high_level_object es una Operation que representa el objeto de "alto nivel" a sintetizar. Por ejemplo, puede ser una
LinearFunction o un
Clifford.
Los siguientes argumentos de palabra clave están disponibles:
targetespecifica el backend de destino, lo que permite al plugin acceder a toda la información específica del destino, como el mapa de acoplamiento, el conjunto de puertas admitidas, etc.coupling_mapsolo especifica el mapa de acoplamiento y se usa únicamente cuando no se especificatarget.qubitsespecifica la lista de qubits sobre los que está definido el objeto de alto nivel, en caso de que la síntesis se realice sobre el circuito físico. Un valor deNoneindica que el layout aún no ha sido elegido y que los qubits físicos del destino o del mapa de acoplamiento sobre los que opera esta operación todavía no han sido determinados.options, un diccionario de configuración libre para opciones específicas del plugin. Si tu plugin tiene estas opciones de configuración, debes documentarlas claramente.
El método run devuelve un QuantumCircuit
que representa el circuito sintetizado a partir del objeto de alto nivel.
También está permitido devolver None, lo que indica que el plugin no puede sintetizar el objeto de alto nivel dado.
La síntesis real de los objetos de alto nivel la realiza el paso del transpilador
HighLevelSynthesis.
Además del método run, hay una serie de métodos de propiedad que deben definirse.
Consulta HighLevelSynthesisPlugin para ver la documentación de todas las propiedades requeridas.
Vamos a definir nuestra subclase de 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 None
Este plugin sintetiza objetos de tipo Clifford que tienen
como máximo 3 qubits, usando el método synth_clifford_bm.
Ahora, exponemos el plugin añadiendo un entry point en los metadatos de nuestro paquete de Python.
Aquí asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo importándola en el archivo __init__.py del módulo my_qiskit_plugin.
Editamos el archivo pyproject.toml, setup.cfg o setup.py de nuestro paquete:
- pyproject.toml
- setup.cfg
- setup.py
[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:MyCliffordSynthesisPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.synthesis': [
'clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin',
]
}
)
El name consta de dos partes separadas por un punto (.):
- El nombre del tipo de Operation que sintetiza el plugin (en este caso,
clifford). Ten en cuenta que esta cadena corresponde al atributonamede la clase Operation, y no al nombre de la clase en sí. - El nombre del plugin (en este caso,
special).
Como antes, si tu proyecto usa setup.cfg o setup.py en lugar de pyproject.toml, consulta la documentación de setuptools para saber cómo adaptar estas líneas a tu situación.
Para verificar que Qiskit detecta correctamente tu plugin, instala el paquete del plugin y sigue las instrucciones en Plugins del transpilador para listar los plugins instalados, asegurándote de que tu plugin aparece en la lista:
from qiskit.transpiler.passes.synthesis import (
high_level_synthesis_plugin_names,
)
high_level_synthesis_plugin_names("clifford")
['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']
Si nuestro plugin de ejemplo estuviera instalado, el nombre my_clifford_synthesis aparecería en esta lista.
- ¡Envía tu plugin al Ecosistema de Qiskit!.
- Consulta los tutoriales para ver ejemplos de transpilación y ejecución de circuitos cuánticos.