diff --git a/docs/transpile/_toc.json b/docs/transpile/_toc.json index d2f354d5736..e969bd29cf4 100644 --- a/docs/transpile/_toc.json +++ b/docs/transpile/_toc.json @@ -22,6 +22,19 @@ "title": "Write a custom transpiler pass", "url": "/transpile/custom-transpiler-pass" }, + { + "title": "Transpiler plugins", + "children": [ + { + "title": "Install and use transpiler plugins", + "url": "/transpile/transpiler-plugins" + }, + { + "title": "Create a transpiler plugin", + "url": "/transpile/create-a-transpiler-plugin" + } + ] + }, { "title": "Use the transpile function", "children": [ diff --git a/docs/transpile/create-a-transpiler-plugin.ipynb b/docs/transpile/create-a-transpiler-plugin.ipynb new file mode 100644 index 00000000000..f1d3a0388a3 --- /dev/null +++ b/docs/transpile/create-a-transpiler-plugin.ipynb @@ -0,0 +1,536 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "58b1f0a7-d62e-4f71-ba01-b8f0beaeeb83", + "metadata": {}, + "source": [ + "# Create a transpiler plugin\n", + "\n", + "Creating a [transpiler plugin](transpiler-plugins) is a great way to share your transpilation code with the wider Qiskit community, allowing other users to benefit from the functionality you've developed. Thank you for your interest in contributing to the Qiskit community!\n", + "\n", + "Before you create a transpiler plugin, you need to decide what kind of plugin is appropriate for your situation. There are three kinds of transpiler plugins:\n", + "- [**Transpiler stage plugin**](/api/qiskit/transpiler_plugins). Choose this if you are defining a pass manager that can be substituted for one of the [6 stages](transpiler-stages) of a preset staged pass manager.\n", + "- [**Unitary synthesis plugin**](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin). Choose this if your transpilation code takes as input a unitary matrix (represented as a Numpy array) and outputs a description of a quantum circuit implementing that unitary.\n", + "- [**High-level synthesis plugin**](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin). Choose this if your transpilation code takes as input a \"high-level object\" such as a Clifford operator or a linear function and outputs a description of a quantum circuit implementing that high-level object. High-level objects are represented by subclasses of the [Operation](/api/qiskit/qiskit.circuit.Operation) class.\n", + "\n", + "Once you've determined which kind of plugin to create, follow these steps to create the plugin:\n", + "\n", + "1. Create a subclass of the appropriate abstract plugin class:\n", + " - [PassManagerStagePlugin](/api/qiskit/qiskit.transpiler.preset_passmanagers.plugin.PassManagerStagePlugin) for a transpiler stage plugin,\n", + " - [UnitarySynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin) for a unitary synthesis plugin, and\n", + " - [HighLevelSynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin) for a high-level synthesis plugin.\n", + "2. Expose the class as a [setuptools entry point](https://setuptools.pypa.io/en/latest/userguide/entry_point.html) in the package metadata, typically by editing the `pyproject.toml`, `setup.cfg`, or `setup.py` file for your Python package.\n", + "\n", + "There is no limit to the number of plugins a single package can define, but each plugin must have a unique name. Qiskit itself includes a number of plugins, whose names are also reserved. The reserved names are:\n", + "\n", + "- Transpiler stage plugins: See [this table](/api/qiskit/transpiler_plugins#plugin-stages).\n", + "- Unitary synthesis plugins: `default`, `aqc`, `sk`\n", + "- High-level synthesis plugins:\n", + "\n", + "| Operation class | Operation name | Reserved names |\n", + "| --- | --- | --- |\n", + "| [Clifford](/api/qiskit/qiskit.quantum_info.Clifford#clifford) | `clifford` | `default`, `ag`, `bm`, `greedy`, `layers`, `lnn` |\n", + "| [LinearFunction](/api/qiskit/qiskit.circuit.library.LinearFunction#linearfunction) | `linear_function` | `default`, `kms`, `pmh` |\n", + "| [PermutationGate](/api/qiskit/qiskit.circuit.library.PermutationGate#permutationgate) | `permutation` | `default`, `kms`, `basic`, `acg`, `token_swapper` |\n", + "\n", + "\n", + "In the next sections, we show examples of these steps for the different types of plugins. In these examples, we assume that we are creating a Python package called `my_qiskit_plugin`. For information on creating Python packages, you can check out [this tutorial](https://packaging.python.org/en/latest/tutorials/packaging-projects/) from the Python website." + ] + }, + { + "cell_type": "markdown", + "id": "4519c0dc-2bcd-48f4-8522-435b72efdb40", + "metadata": {}, + "source": [ + "## Example: Create a transpiler stage plugin\n", + "\n", + "In this example, we create a transpiler stage plugin for the `layout` stage (see [Transpiler stages](transpiler-stages) for a description of the 6 stages of Qiskit's built-in transpilation pipeline).\n", + "Our plugin simply runs [VF2Layout](/api/qiskit/qiskit.transpiler.passes.VF2Layout) for a number of trials that depends on the requested optimization level.\n", + "\n", + "First, we create a subclass of [PassManagerStagePlugin](/api/qiskit/qiskit.transpiler.preset_passmanagers.plugin.PassManagerStagePlugin). There is one method we need to implement, called [`pass_manager`](/api/qiskit/qiskit.transpiler.preset_passmanagers.plugin.PassManagerStagePlugin#pass_manager). This method takes as input a [PassManagerConfig](/api/qiskit/qiskit.transpiler.PassManagerConfig) and returns the pass manager that we are defining. The PassManagerConfig object stores information about the target backend, such as its coupling map and basis gates." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f90299d9-5026-424c-b528-6d0defdddb54", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.transpiler import PassManager\n", + "from qiskit.transpiler.passes import VF2Layout\n", + "from qiskit.transpiler.passmanager_config import PassManagerConfig\n", + "from qiskit.transpiler.preset_passmanagers import common\n", + "from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin\n", + "\n", + "\n", + "class MyLayoutPlugin(PassManagerStagePlugin):\n", + " def pass_manager(\n", + " self,\n", + " pass_manager_config: PassManagerConfig,\n", + " optimization_level: int | None = None,\n", + " ) -> PassManager:\n", + " layout_pm = PassManager(\n", + " [\n", + " VF2Layout(\n", + " coupling_map=pass_manager_config.coupling_map,\n", + " properties=pass_manager_config.backend_properties,\n", + " max_trials=optimization_level * 10 + 1,\n", + " target=pass_manager_config.target,\n", + " )\n", + " ]\n", + " )\n", + " layout_pm += common.generate_embed_passmanager(pass_manager_config.coupling_map)\n", + " return layout_pm" + ] + }, + { + "cell_type": "markdown", + "id": "d7666879-14bc-479c-a91b-56a800897073", + "metadata": {}, + "source": [ + "Now, we expose the plugin by adding an entry point in our Python package metadata.\n", + "Here, we assume that the class we defined is exposed in a module called `my_qiskit_plugin`, for example by being imported in the `__init__.py` file of the `my_qiskit_plugin` module.\n", + "We edit the `pyproject.toml`, `setup.cfg`, or `setup.py` file of our package (depending on which kind of file you chose to store your Python project metadata):\n", + "\n", + "\n", + " \n", + " ```toml\n", + " [project.entry-points.\"qiskit.transpiler.layout\"]\n", + " \"my_layout\" = \"my_qiskit_plugin:MyLayoutPlugin\"\n", + " ```\n", + " \n", + " \n", + " ```ini\n", + " [options.entry_points]\n", + " qiskit.transpiler.layout =\n", + " my_layout = my_qiskit_plugin:MyLayoutPlugin\n", + " ```\n", + " \n", + " \n", + " ```python\n", + " from setuptools import setup\n", + "\n", + " setup(\n", + " # ...,\n", + " entry_points={\n", + " 'qiskit.transpiler.layout': [\n", + " 'my_layout = my_qiskit_plugin:MyLayoutPlugin',\n", + " ]\n", + " }\n", + " )\n", + " ```\n", + " \n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "5c5edd30", + "metadata": {}, + "source": [ + "See the [table of transpiler plugin stages](/api/qiskit/transpiler_plugins#stage-table) for the entry points and expectations for each transpiler stage.\n", + "\n", + "To check that your plugin is successfully detected by Qiskit, install your plugin package and follow the instructions at [Transpiler plugins](transpiler-plugins#list-available-transpiler-stage-plugins) for listing installed plugins, and ensure that your plugin appears in the list:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "04b07ec3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['default', 'dense', 'sabre', 'trivial']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins\n", + "\n", + "list_stage_plugins(\"layout\")" + ] + }, + { + "cell_type": "markdown", + "id": "d62c4edb", + "metadata": {}, + "source": [ + "If our example plugin were installed, then the name `my_layout` would appear in this list." + ] + }, + { + "cell_type": "markdown", + "id": "fe3a1a6c-5aa2-4f00-9bdd-45598717be1d", + "metadata": {}, + "source": [ + "## Example: Create a unitary synthesis plugin\n", + "\n", + "In this example, we'll create a unitary synthesis plugin that simply uses the built-in [UnitarySynthesis](/api/qiskit/qiskit.transpiler.passes.UnitarySynthesis#unitarysynthesis) transpilation pass to synthesize a gate. Of course, your own plugin will do something more interesting than that.\n", + "\n", + "The [UnitarySynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin) class defines the interface and contract for unitary synthesis\n", + "plugins. The primary method is\n", + "[`run`](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin#run),\n", + "which takes as input a Numpy array storing a unitary matrix\n", + "and returns a [DAGCircuit](/api/qiskit/qiskit.dagcircuit.DAGCircuit) representing the circuit synthesized from that unitary matrix.\n", + "In addition to the `run` method, there are a number of property methods that need to be defined.\n", + "See [UnitarySynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin) for documentation of all required properties.\n", + "\n", + "Let's create our UnitarySynthesisPlugin subclass:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6bc1011c-b15d-4210-973b-d9d530ece880", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.circuit import QuantumCircuit, QuantumRegister\n", + "from qiskit.converters import circuit_to_dag\n", + "from qiskit.dagcircuit.dagcircuit import DAGCircuit\n", + "from qiskit.quantum_info import Operator\n", + "from qiskit.transpiler.passes import UnitarySynthesis\n", + "from qiskit.transpiler.passes.synthesis.plugin import UnitarySynthesisPlugin\n", + "\n", + "\n", + "class MyUnitarySynthesisPlugin(UnitarySynthesisPlugin):\n", + " @property\n", + " def supports_basis_gates(self):\n", + " # Returns True if the plugin can target a list of basis gates\n", + " return True\n", + "\n", + " @property\n", + " def supports_coupling_map(self):\n", + " # Returns True if the plugin can synthesize for a given coupling map\n", + " return False\n", + "\n", + " @property\n", + " def supports_natural_direction(self):\n", + " # Returns True if the plugin supports a toggle for considering\n", + " # directionality of 2-qubit gates\n", + " return False\n", + "\n", + " @property\n", + " def supports_pulse_optimize(self):\n", + " # Returns True if the plugin can optimize pulses during synthesis\n", + " return False\n", + "\n", + " @property\n", + " def supports_gate_lengths(self):\n", + " # Returns True if the plugin can accept information about gate lengths\n", + " return False\n", + "\n", + " @property\n", + " def supports_gate_errors(self):\n", + " # Returns True if the plugin can accept information about gate errors\n", + " return False\n", + "\n", + " @property\n", + " def supports_gate_lengths_by_qubit(self):\n", + " # Returns True if the plugin can accept information about gate lengths\n", + " # (The format of the input differs from supports_gate_lengths)\n", + " return False\n", + "\n", + " @property\n", + " def supports_gate_errors_by_qubit(self):\n", + " # Returns True if the plugin can accept information about gate errors\n", + " # (The format of the input differs from supports_gate_errors)\n", + " return False\n", + "\n", + " @property\n", + " def min_qubits(self):\n", + " # Returns the minimum number of qubits the plugin supports\n", + " return None\n", + "\n", + " @property\n", + " def max_qubits(self):\n", + " # Returns the maximum number of qubits the plugin supports\n", + " return None\n", + "\n", + " @property\n", + " def supported_bases(self):\n", + " # Returns a dictionary of supported bases for synthesis\n", + " return None\n", + "\n", + " def run(self, unitary: np.ndarray, **options) -> DAGCircuit:\n", + " basis_gates = options[\"basis_gates\"]\n", + " synth_pass = UnitarySynthesis(basis_gates, min_qubits=3)\n", + " qubits = QuantumRegister(3)\n", + " circuit = QuantumCircuit(qubits)\n", + " circuit.append(Operator(unitary).to_instruction(), qubits)\n", + " dag_circuit = synth_pass.run(circuit_to_dag(circuit))\n", + " return dag_circuit" + ] + }, + { + "cell_type": "markdown", + "id": "6daebf1b-aa38-44a8-bf62-eea96838e95f", + "metadata": {}, + "source": [ + "If you find that the inputs available to the [`run`](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin#run)\n", + "method are insufficient for your purposes, please [open an issue](https://github.com/Qiskit/qiskit/issues/new/choose) explaining your requirements. Changes to the plugin interface, such as adding additional optional inputs, will be done in a backward compatible way so that they do not require changes from existing plugins.\n", + "\n", + "\n", + "All methods prefixed with `supports_` are reserved on a `UnitarySynthesisPlugin` derived class as part of the interface. You should not define any custom `supports_*` methods on a subclass that are not defined in the abstract class.\n", + "\n", + "\n", + "Now, we expose the plugin by adding an entry point in our Python package metadata.\n", + "Here, we assume that the class we defined is exposed in a module called `my_qiskit_plugin`, for example by being imported in the `__init__.py` file of the `my_qiskit_plugin` module.\n", + "We edit the `pyproject.toml`, `setup.cfg`, or `setup.py` file of our package:\n", + "\n", + "\n", + " \n", + " ```toml\n", + " [project.entry-points.\"qiskit.unitary_synthesis\"]\n", + " \"my_unitary_synthesis\" = \"my_qiskit_plugin:MyUnitarySynthesisPlugin\"\n", + " ```\n", + " \n", + " \n", + " ```ini\n", + " [options.entry_points]\n", + " qiskit.unitary_synthesis =\n", + " my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin\n", + " ```\n", + " \n", + " \n", + " ```python\n", + " from setuptools import setup\n", + "\n", + " setup(\n", + " # ...,\n", + " entry_points={\n", + " 'qiskit.unitary_synthesis': [\n", + " 'my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin',\n", + " ]\n", + " }\n", + " )\n", + " ```\n", + " \n", + "\n", + "\n", + "As before, if your project uses `setup.cfg` or `setup.py` instead of `pyproject.toml`, see the [setuptools documentation](https://setuptools.pypa.io/en/latest/userguide/entry_point.html) for how to adapt these lines for your situation.\n", + "\n", + "To check that your plugin is successfully detected by Qiskit, install your plugin package and follow the instructions at [Transpiler plugins](transpiler-plugins#list-available-unitary-synthesis-plugins) for listing installed plugins, and ensure that your plugin appears in the list:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "31bfaf30", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['aqc', 'default', 'sk']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names\n", + "\n", + "unitary_synthesis_plugin_names()" + ] + }, + { + "cell_type": "markdown", + "id": "4faf2d51", + "metadata": {}, + "source": [ + "If our example plugin were installed, then the name `my_unitary_synthesis` would appear in this list.\n", + "\n", + "To accommodate unitary synthesis plugins that expose multiple options,\n", + "the plugin interface has an option for users to provide a free-form\n", + "configuration dictionary. This will be passed to the `run` method\n", + "via the `options` keyword argument. If your plugin has these configuration options, you should clearly document them." + ] + }, + { + "cell_type": "markdown", + "id": "651c863d-41d9-41f4-a133-b392dae2f363", + "metadata": {}, + "source": [ + "## Example: Create a high-level synthesis plugin\n", + "\n", + "In this example, we'll create a high-level synthesis plugin that simply uses the built-in [synth_clifford_bm](/api/qiskit/synthesis#synth_clifford_bm) function to synthesize a Clifford operator.\n", + "\n", + "The [HighLevelSynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin) class defines the interface and contract for high-level synthesis plugins. The primary method is [`run`](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin#run).\n", + "The positional argument `high_level_object` is an [Operation](/api/qiskit/qiskit.circuit.Operation) representing the \"high-level\" object to be synthesized. For example, it could be a\n", + "[LinearFunction](/api/qiskit/qiskit.circuit.library.LinearFunction) or a\n", + "[Clifford](/api/qiskit/qiskit.quantum_info.Clifford).\n", + "The following keyword arguments are present:\n", + "- `target` specifies the target backend, allowing the plugin\n", + "to access all target-specific information,\n", + "such as the coupling map, the supported gate set, and so on\n", + "- `coupling_map` only specifies the coupling map, and is only used when `target` is not specified.\n", + "- `qubits` specifies the list of qubits over which the\n", + "high-level object is defined, in case the synthesis is done on the physical circuit.\n", + "A value of ``None`` indicates that the layout has not yet been chosen and the physical qubits in the target or coupling map that this operation is operating on has not yet been determined.\n", + "- `options`, a free-form configuration dictionary for plugin-specific options. If your plugin has these configuration options you\n", + "should clearly document them.\n", + "\n", + "The `run` method returns a [QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit)\n", + "representing the circuit synthesized from that high-level object.\n", + "It is also allowed to return `None`, indicating that the plugin is unable to synthesize the given high-level object.\n", + "The actual synthesis of high-level objects is performed by the\n", + "[HighLevelSynthesis](/api/qiskit/qiskit.transpiler.passes.HighLevelSynthesis)\n", + "transpiler pass.\n", + "\n", + "In addition to the `run` method, there are a number of property methods that need to be defined.\n", + "See [HighLevelSynthesisPlugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin) for documentation of all required properties.\n", + "\n", + "Let's define our HighLevelSynthesisPlugin subclass:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3c0e59d4-85b0-4157-824b-b1f6220e83ad", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.synthesis import synth_clifford_bm\n", + "from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin\n", + "\n", + "\n", + "class MyCliffordSynthesisPlugin(HighLevelSynthesisPlugin):\n", + " def run(\n", + " self, high_level_object, coupling_map=None, target=None, qubits=None, **options\n", + " ) -> QuantumCircuit:\n", + " if high_level_object.num_qubits <= 3:\n", + " return synth_clifford_bm(high_level_object)\n", + " else:\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "id": "1285dbfd-04ce-494b-8a01-a58040035dbd", + "metadata": {}, + "source": [ + "This plugin synthesizes objects of type [Clifford](/api/qiskit/qiskit.quantum_info.Clifford) that have\n", + "at most 3 qubits, using the `synth_clifford_bm` method.\n", + "\n", + "Now, we expose the plugin by adding an entry point in our Python package metadata.\n", + "Here, we assume that the class we defined is exposed in a module called `my_qiskit_plugin`, for example by being imported in the `__init__.py` file of the `my_qiskit_plugin` module.\n", + "We edit the `pyproject.toml`, `setup.cfg`, or `setup.py` file of our package:\n", + "\n", + "\n", + " \n", + " ```toml\n", + " [project.entry-points.\"qiskit.synthesis\"]\n", + " \"clifford.my_clifford_synthesis\" = \"my_qiskit_plugin:MyCliffordSynthesisPlugin\"\n", + " ```\n", + " \n", + " \n", + " ```ini\n", + " [options.entry_points]\n", + " qiskit.synthesis =\n", + " clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin\n", + " ```\n", + " \n", + " \n", + " ```python\n", + " from setuptools import setup\n", + "\n", + " setup(\n", + " # ...,\n", + " entry_points={\n", + " 'qiskit.synthesis': [\n", + " 'clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin',\n", + " ]\n", + " }\n", + " )\n", + " ```\n", + " \n", + "\n", + "\n", + "The `name` consists of two parts separated by a dot (`.`):\n", + "- The name of the type of [Operation](/api/qiskit/qiskit.circuit.Operation) that the plugin synthesizes (in this case, `clifford`). Note that this string corresponds to the [`name`](/api/qiskit/qiskit.circuit.Operation#name) attribute of the Operation class, and not the name of the class itself.\n", + "- The name of the plugin (in this case, `special`).\n", + "\n", + "As before, if your project uses `setup.cfg` or `setup.py` instead of `pyproject.toml`, see the [setuptools documentation](https://setuptools.pypa.io/en/latest/userguide/entry_point.html) for how to adapt these lines for your situation.\n", + "\n", + "To check that your plugin is successfully detected by Qiskit, install your plugin package and follow the instructions at [Transpiler plugins](transpiler-plugins#list-available-high-level-sythesis-plugins) for listing installed plugins, and ensure that your plugin appears in the list:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fbe1f265", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ag', 'bm', 'default', 'greedy', 'layers', 'lnn']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis import high_level_synthesis_plugin_names\n", + "\n", + "high_level_synthesis_plugin_names(\"clifford\")" + ] + }, + { + "cell_type": "markdown", + "id": "f1e86f49", + "metadata": {}, + "source": [ + "If our example plugin were installed, then the name `my_clifford_synthesis` would appear in this list." + ] + }, + { + "cell_type": "markdown", + "id": "a48c2d0b-c402-4b83-ae2e-42c33fe1720e", + "metadata": {}, + "source": [ + "\n", + " - [Submit your plugin to the Qiskit Ecosystem!](https://github.com/Qiskit/ecosystem?tab=readme-ov-file#how-to-join).\n", + " - Check out the tutorials on [IBM Quantum Learning](https://learning.quantum.ibm.com/catalog/tutorials) for examples of transpiling and running quantum circuits.\n", + "" + ] + } + ], + "metadata": { + "description": "How to create a Qiskit transpiler plugin to share your transpilation code with the Qiskit community.", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + }, + "title": "Create a transpiler plugin" + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/transpile/transpiler-plugins.ipynb b/docs/transpile/transpiler-plugins.ipynb new file mode 100644 index 00000000000..7398cc86cf9 --- /dev/null +++ b/docs/transpile/transpiler-plugins.ipynb @@ -0,0 +1,365 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "334fb4b3-9d8d-4ee2-ba20-b6008b13c6e5", + "metadata": {}, + "source": [ + "# Install and use transpiler plugins\n", + "\n", + "To facilitate the development and reuse of custom transpilation code by the wider community of Qiskit users, Qiskit supports a plugin interface that enables third-party Python packages to declare that they provide extended transpilation functionality accessible via Qiskit.\n", + "\n", + "Currently, third-party plugins can provide extended transpilation functionality in three ways:\n", + "\n", + "- A [transpiler stage plugin](/api/qiskit/transpiler_plugins) provides a pass manager that can be used in place of one of the [6 stages](transpiler-stages) of a preset staged pass manager: `init`, `layout`, `routing`, `translation`, `optimization`, and `scheduling`.\n", + "- A [unitary synthesis plugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin) provides extended functionality for unitary gate synthesis.\n", + "- A [high-level synthesis plugin](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin) provides extended functionality for synthesizing \"high-level objects\" such as linear functions or Clifford operators. High-level objects are represented by subclasses of the [Operation](/api/qiskit/qiskit.circuit.Operation) class.\n", + "\n", + "The rest of the page describes how to list available plugins, install new ones, and use them." + ] + }, + { + "cell_type": "markdown", + "id": "aa9e4775-d7fb-473f-8864-c6c2f42d309c", + "metadata": {}, + "source": [ + "## List available plugins and install new ones\n", + "\n", + "Qiskit already includes some built-in plugins for transpilation. To install more, you can use your Python package manager. For example, you might run `pip install qiskit-toqm` to install the [Qiskit TOQM](https://github.com/qiskit-toqm/qiskit-toqm) routing stage plugin. A number of third-party plugins are part of the [Qiskit ecosystem](https://qiskit.github.io/ecosystem/#transpiler_plugin).\n", + "\n", + "### List available transpiler stage plugins\n", + "\n", + "Use the [list_stage_plugins](/api/qiskit/transpiler_plugins#qiskit.transpiler.preset_passmanagers.plugin.list_stage_plugins) function, passing the name of the stage whose plugins you want to list." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "17cf6ef8-7edf-428c-a779-8c934e67720d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['default', 'dense', 'sabre', 'trivial']" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins\n", + "\n", + "list_stage_plugins(\"layout\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6077307e-3214-453d-af1b-c19ee2f917f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['basic', 'lookahead', 'none', 'sabre', 'stochastic']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list_stage_plugins(\"routing\")" + ] + }, + { + "cell_type": "markdown", + "id": "fde508b8-ac1d-42d8-84cc-60e37d78092e", + "metadata": {}, + "source": [ + " If `qiskit-toqm` were installed, then `toqm` would appear in the list of `routing` plugins." + ] + }, + { + "cell_type": "markdown", + "id": "c2214241-8bb7-47e4-91b2-d5225f9dd281", + "metadata": {}, + "source": [ + "### List available unitary synthesis plugins\n", + "\n", + "Use the [unitary_synthesis_plugin_names](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.unitary_synthesis_plugin_names) function." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5b8e4b56-79bd-4079-a22f-cf74f2a86f4a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['aqc', 'default', 'sk']" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names\n", + "\n", + "unitary_synthesis_plugin_names()" + ] + }, + { + "cell_type": "markdown", + "id": "5497ad2a-92aa-4461-8182-89472848e214", + "metadata": {}, + "source": [ + "### List available high-level sythesis plugins\n", + "\n", + "Use the [high_level_synthesis_plugin_names](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.high_level_synthesis_plugin_names) function, passing the name of the type of \"high-level object\" to be synthesized. The name corresponds to the [`name`](/api/qiskit/qiskit.circuit.Operation#name) attribute of the [Operation](/api/qiskit/qiskit.circuit.Operation) class representing the type of object being synthesized." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d88eee69-c021-41e1-9c69-6a461e77ab12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ag', 'bm', 'default', 'greedy', 'layers', 'lnn']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis import high_level_synthesis_plugin_names\n", + "\n", + "high_level_synthesis_plugin_names(\"clifford\")" + ] + }, + { + "cell_type": "markdown", + "id": "be7d3c04-3c0f-4a84-9311-108f04f75196", + "metadata": {}, + "source": [ + "You can use the [HighLevelSynthesisPluginManager](/api/qiskit/qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPluginManager) class to list the names of all high-level synthesis plugins:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7def6967-6125-43f4-b59f-617f0e2b298f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['clifford.ag',\n", + " 'clifford.bm',\n", + " 'clifford.default',\n", + " 'clifford.greedy',\n", + " 'clifford.layers',\n", + " 'clifford.lnn',\n", + " 'linear_function.default',\n", + " 'linear_function.kms',\n", + " 'linear_function.pmh',\n", + " 'permutation.acg',\n", + " 'permutation.basic',\n", + " 'permutation.default',\n", + " 'permutation.kms',\n", + " 'permutation.token_swapper']" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPluginManager\n", + "\n", + "HighLevelSynthesisPluginManager().plugins.names()" + ] + }, + { + "cell_type": "markdown", + "id": "bfdf815b-78f8-40b4-b24e-bab68506237a", + "metadata": {}, + "source": [ + "## Use a plugin\n", + "\n", + "In this section, we show how to use transpiler plugins. In the code examples, we use plugins that come with Qiskit, but plugins installed from third-party packages are used the same way.\n", + "\n", + "### Use a transpiler stage plugin\n", + "\n", + "To use a transpiler stage plugin, specify its name with the appropriate argument to [`generate_preset_pass_manager`](/api/qiskit/transpiler_preset#generate_preset_pass_manager) or [`transpile`](/api/qiskit/compiler#qiskit.compiler.transpile). The argument is formed by appending `_method` to the name of the transpilation stage. For example, to use the `stochastic` routing plugin, we would specify `stochastic` for the `routing_method` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "98b3b08c-0945-47dd-918a-ccd62e8efa54", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "from qiskit_ibm_runtime import QiskitRuntimeService\n", + "\n", + "service = QiskitRuntimeService()\n", + "backend = service.backend(\"ibm_brisbane\")\n", + "\n", + "pass_manager = generate_preset_pass_manager(\n", + " optimization_level=3, backend=backend, routing_method=\"stochastic\"\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "955b3de1-0363-4c05-bca4-47dec38837fb", + "metadata": {}, + "source": [ + "### Use a unitary synthesis plugin\n", + "\n", + "To use a unitary synthesis plugin, specify its name as the `unitary_synthesis_method` argument to [`generate_preset_pass_manager`](/api/qiskit/transpiler_preset#generate_preset_pass_manager) or [`transpile`](/api/qiskit/compiler#qiskit.compiler.transpile):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "134c7ce2-ae3b-496e-89a8-cfe554ed6e32", + "metadata": {}, + "outputs": [], + "source": [ + "pass_manager = generate_preset_pass_manager(\n", + " optimization_level=3,\n", + " backend=backend,\n", + " unitary_synthesis_method=\"sk\",\n", + " unitary_synthesis_plugin_config=dict(basis_gates=[\"cz\", \"id\", \"rz\", \"sx\", \"x\"]),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a2b1c570-50f6-45ae-928d-274e827501fe", + "metadata": {}, + "source": [ + "Unitary synthesis is used in the `init`, `translation`, and `optimization` stages of the staged pass manager returned by [`generate_preset_pass_manager`](/api/qiskit/transpiler_preset#generate_preset_pass_manager) or used in [`transpile`](/api/qiskit/compiler#qiskit.compiler.transpile). See [Transpiler stages](transpiler-stages) for a description of these stages.\n", + "\n", + "Use the `unitary_synthesis_plugin_config` argument, a free-form dictionary, to pass options for the unitary synthesis method. The documentation of the synthesis method should explain the options it supports. See [this list](/api/qiskit/transpiler_synthesis_plugins#unitary-synthesis-plugins-2) for links to the documentation of the built-in unitary synthesis plugins." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "241012ed-3f75-4ac9-a5bf-cf55b4db5b1f", + "metadata": {}, + "source": [ + "### Use a high-level synthesis plugin\n", + "\n", + "First, create an [HLSConfig](/api/qiskit/qiskit.transpiler.passes.HLSConfig) to\n", + "store the names of the plugins to use for various high-level objects. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "057d86eb-6d70-4772-8b9c-98fbe5566fac", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.transpiler.passes import HLSConfig\n", + "\n", + "hls_config = HLSConfig(clifford=[\"layers\"], linear_function=[\"pmh\"])" + ] + }, + { + "cell_type": "markdown", + "id": "7b5e1b28-a8e9-4767-b0ae-9cc940f89ede", + "metadata": {}, + "source": [ + "This code cell creates a high-level synthesis configuration that uses the `layers` plugin\n", + "for synthesizing [Clifford](/api/qiskit/qiskit.quantum_info.Clifford#clifford) objects and the `pmh` plugin for synthesizing\n", + "[LinearFunction](/api/qiskit/qiskit.circuit.library.LinearFunction#linearfunction) objects. The names of the keyword arguments correspond to the\n", + "[`name`](/api/qiskit/qiskit.circuit.Operation#name) attribute of the [Operation](/api/qiskit/qiskit.circuit.Operation) class representing the type of object being synthesized.\n", + "For each high-level object, the list of given plugins are tried in sequence until one of them\n", + "succeeds (in the example above, each list only contains a single plugin).\n", + "\n", + "In addition to specifying\n", + "a plugin by its name, you can instead pass a `(name, options)` tuple, where the second element of the tuple is a dictionary containing options for the plugin. The documentation of the synthesis method should explain the options it supports. See [this list](/api/qiskit/transpiler_synthesis_plugins#high-level-synthesis-plugins-2) for links to the documentation of the built-in high-level synthesis plugins.\n", + "\n", + "Once you have created the `HLSConfig` object, pass it as the\n", + "`hls_config` argument to [`generate_preset_pass_manager`](/api/qiskit/transpiler_preset#generate_preset_pass_manager) or [`transpile`](/api/qiskit/compiler#qiskit.compiler.transpile):" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a9245ac1-af79-48cf-85e3-16b27417f5b3", + "metadata": {}, + "outputs": [], + "source": [ + "pass_manager = generate_preset_pass_manager(\n", + " optimization_level=3, backend=backend, hls_config=hls_config\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "b1c5f3a7-01f1-4424-a8f8-f174f1e14905", + "metadata": {}, + "source": [ + "High-level synthesis is used in the `init`, `translation`, and `optimization` stages of the staged pass manager returned by [`generate_preset_pass_manager`](/api/qiskit/transpiler_preset#generate_preset_pass_manager) or used in [`transpile`](/api/qiskit/compiler#qiskit.compiler.transpile). See [Transpiler stages](transpiler-stages) for a description of these stages." + ] + }, + { + "cell_type": "markdown", + "id": "de7bff27-0e8e-48ac-9546-757b8ba36384", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + " - [Create a transpiler plugin](create-a-transpiler-plugin).\n", + " - Check out the tutorials on [IBM Quantum Learning](https://learning.quantum.ibm.com/catalog/tutorials) for examples of transpiling and running quantum circuits.\n", + "" + ] + } + ], + "metadata": { + "description": "How to install and use transpiler plugins in Qiskit.", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + }, + "title": "Install and use transpiler plugins" + }, + "nbformat": 4, + "nbformat_minor": 4 +}