Skip to main content
IBM Quantum Platform

Crear un complemento transpilador

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o versiones más recientes.

    qiskit[all]~=2.5.0
    

Crear un plugin de transpilador es una buena forma de compartir tu código de transpilación con toda la comunidad Qiskit, permitiendo que otros usuarios se beneficien de la funcionalidad que has desarrollado. Gracias por tu interés en contribuir a la comunidad Qiskit

Antes de crear un plugin de transpilador, tienes que decidir qué tipo de plugin es el adecuado para tu situación. Existen tres tipos de plugins transpiladores:

  • Transpiler stage plugin. Seleccione esta opción si está definiendo un gestor de pases que pueda sustituir a una de las 6 etapas de un gestor de pases por etapas preestablecido.
  • Plugin de síntesis unitaria. Elija esta opción si su código de transpilación toma como entrada una matriz unitaria (representada como una matriz Numpy) y da como salida una descripción de un circuito cuántico que implemente esa matriz unitaria.
  • Plugin de síntesis de alto nivel. Elija esta opción si su código de transpilación toma como entrada un "objeto de alto nivel", como un operador Clifford o una función lineal, y da como salida una descripción de un circuito cuántico que implementa ese objeto de alto nivel. Los objetos de alto nivel están representados por subclases de la clase Operación.

Una vez que hayas determinado qué tipo de plugin crear, sigue estos pasos para crear el plugin:

  1. Crear una subclase de la clase plugin abstracta apropiada:
  2. Exponga la clase como un punto de entrada setuptools en los metadatos del paquete, normalmente editando el archivo pyproject.toml, setup.cfg, o setup.py para su paquete Python.

No hay límite en el número de plugins que puede definir un mismo paquete, pero cada plugin debe tener un nombre único. El propio SDK de Qiskit incluye una serie de plugins, cuyos nombres también están reservados. Los nombres reservados son:

  • Plugins de etapa del transpilador: Consulte 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
Cliffordclifforddefault, ag, bm, greedy, layers, lnn
LinearFunctionlinear_functiondefault, kms, pmh
PermutationGatepermutationdefault, kms, basic, acg, token_swapper

En las siguientes secciones, mostramos ejemplos de estos pasos para los distintos tipos de plugins. En estos ejemplos, suponemos que estamos creando un paquete Python llamado my_qiskit_plugin. Para obtener información sobre la creación de paquetes Python, puede consultar este tutorial del sitio web Python.


Ejemplo: Crear un complemento de etapa transpilador

En este ejemplo, creamos un plugin de etapa de transpilación para la etapa layout (ver Etapas de transpilación para una descripción de las 6 etapas del pipeline de transpilación incorporado en Qiskit). Nuestro plugin simplemente ejecuta VF2Layout durante un número de pruebas que depende del nivel de optimización solicitado.

En primer lugar, creamos una subclase de PassManagerStagePlugin. Hay un método que necesitamos implementar, llamado pass_manager. Este método toma como entrada un PassManagerConfig y devuelve el gestor de pases que estamos definiendo. El objeto PassManagerConfig almacena información sobre el backend de destino, como su mapa de acoplamiento y sus puertas 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_pm

Ahora, exponemos el plugin añadiendo un punto de entrada en los metadatos de nuestro paquete Python. Aquí, asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo al ser importada en el archivo __init__.py del módulo my_qiskit_plugin . Editamos el archivo pyproject.toml, setup.cfg, o setup.py de nuestro paquete (dependiendo del tipo de archivo que haya elegido para almacenar los metadatos de su proyecto Python ):

[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"

Consulte la tabla de etapas del plugin de transpilador para conocer los puntos de entrada y las expectativas de cada etapa del transpilador.

Para comprobar que tu plugin es detectado correctamente por Qiskit, instala tu paquete de plugins y sigue las instrucciones en Transpiler plugins para listar los plugins instalados, y asegúrate de que tu plugin aparece en la lista:

from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins

list_stage_plugins("layout")

Output:

['default', 'dense', 'sabre', 'trivial']

Si nuestro plugin de ejemplo estuviera instalado, entonces el nombre my_layout aparecería en esta lista.

Si desea utilizar una etapa de transpilador incorporada como punto de partida para su plugin de etapa de transpilador, puede obtener el gestor de pases para una etapa de transpilador incorporada utilizando PassManagerStagePluginManager. La siguiente celda de código muestra cómo hacerlo para obtener la etapa de optimización incorporada 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 complemento de síntesis unitaria

En este ejemplo, crearemos un plugin de síntesis unitaria que simplemente utiliza el pase 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 unitaria. El método principal es run, que toma como entrada una matriz Numpy que almacena 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. Consulte UnitarySynthesisPlugin para obtener documentación sobre todas las propiedades necesarias.

Vamos a crear nuestra subclase 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 considera que las entradas disponibles para el run son insuficientes para sus fines, abra una incidencia explicando sus necesidades. Los cambios en la interfaz de los plugins, como la adición de entradas opcionales adicionales, se realizarán de forma compatible con versiones anteriores para que no requieran cambios en los plugins existentes.

Nota

Todos los métodos prefijados con supports_ están reservados en una clase derivada de UnitarySynthesisPlugin como parte de la interfaz. No debe definir ningún método personalizado supports_* en una subclase que no esté definido en la clase abstracta.

Ahora, exponemos el plugin añadiendo un punto de entrada en los metadatos de nuestro paquete Python. Aquí, asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo al ser importada en el archivo __init__.py del módulo my_qiskit_plugin . Editamos el archivo pyproject.toml, setup.cfg, o setup.py de nuestro paquete:

[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"

Al igual que antes, si su proyecto utiliza setup.cfg o setup.py en lugar de pyproject.toml, consulte la documentación de setuptools para saber cómo adaptar estas líneas a su situación.

Para comprobar que tu plugin es detectado correctamente por Qiskit, instala tu paquete de plugins y sigue las instrucciones en Transpiler plugins para listar los plugins instalados, y asegúrate de que tu plugin aparece en la lista:

from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names

unitary_synthesis_plugin_names()

Output:

['aqc', 'clifford', 'default', 'gridsynth', 'sk']

Si nuestro plugin de ejemplo estuviera instalado, entonces el nombre my_unitary_synthesis aparecería en esta lista.

Para dar cabida a los plugins de síntesis unitaria que exponen múltiples opciones, la interfaz de plugins tiene una opción para que los usuarios proporcionen una forma libre diccionario de configuración. Esto se pasará al método run mediante el argumento de la palabra clave options . Si tu plugin tiene estas opciones de configuración, deberías documentarlas claramente.


Ejemplo: Crear un complemento de síntesis de alto nivel

En este ejemplo, crearemos un plugin de síntesis de alto nivel que simplemente utiliza la función incorporada 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 Operación que representa el objeto de "alto nivel" a sintetizar. Por ejemplo, podría ser un LinearFunction o un Clifford. Existen los siguientes argumentos de palabra clave:

  • target especifica el backend de destino, lo que permite al plugin acceder a toda la información específica del objetivo, como el mapa de acoplamiento, el conjunto de puertas soportadas, etc
  • coupling_map sólo especifica el mapa de acoplamiento, y sólo se utiliza cuando no se especifica target .
  • qubits especifica la lista de qubits sobre la que se define el objeto de alto nivel, en caso de que la síntesis se realice sobre el circuito físico. Un valor de None indica que aún no se ha elegido la disposición y que aún no se han determinado los qubits físicos del mapa de destino o de acoplamiento sobre los que opera esta operación.
  • optionsun diccionario de configuración libre para las opciones específicas del plugin. Si su plugin tiene estas opciones de configuración debe documentarlas claramente.

El método run devuelve un QuantumCircuit que representa el circuito sintetizado a partir de ese objeto de alto nivel. También se permite devolver None, indicando que el plugin no puede sintetizar el objeto de alto nivel dado. La síntesis real de los objetos de alto nivel es realizada por el módulo HighLevelSynthesis transpilador.

Además del método run , hay una serie de métodos de propiedad que deben definirse. Consulte HighLevelSynthesisPlugin para obtener documentación sobre todas las propiedades necesarias.

Definamos nuestra subclase 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, utilizando el método synth_clifford_bm .

Ahora, exponemos el plugin añadiendo un punto de entrada en los metadatos de nuestro paquete Python. Aquí, asumimos que la clase que definimos está expuesta en un módulo llamado my_qiskit_plugin, por ejemplo al ser importada en el archivo __init__.py del módulo my_qiskit_plugin . Editamos el archivo pyproject.toml, setup.cfg, o setup.py de nuestro paquete:

[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"

name consta de dos partes separadas por un punto (.):

  • El nombre del tipo de Operación que sintetiza el plugin (en este caso, clifford). Nótese que esta cadena corresponde al atributo name de la clase Operation, y no al nombre de la clase en sí.
  • El nombre del plugin (en este caso, special).

Al igual que antes, si su proyecto utiliza setup.cfg o setup.py en lugar de pyproject.toml, consulte la documentación de setuptools para saber cómo adaptar estas líneas a su situación.

Para comprobar que tu plugin es detectado correctamente por Qiskit, instala tu paquete de plugins y sigue las instrucciones en Transpiler plugins para listar los plugins instalados, y asegúrate 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")

Output:

['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']

Si nuestro plugin de ejemplo estuviera instalado, entonces el nombre my_clifford_synthesis aparecería en esta lista.

Recomendación
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.