609 lines
28 KiB
Plaintext
609 lines
28 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "58b1f0a7-d62e-4f71-ba01-b8f0beaeeb83",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Create a transpiler plugin"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5ec7b7a4-5318-4422-ab3d-c88cee551eaa",
|
|
"metadata": {
|
|
"tags": [
|
|
"version-info"
|
|
]
|
|
},
|
|
"source": [
|
|
"<details>\n",
|
|
"<summary><b>Package versions</b></summary>\n",
|
|
"\n",
|
|
"The code on this page was developed using the following requirements.\n",
|
|
"We recommend using these versions or newer.\n",
|
|
"\n",
|
|
"```\n",
|
|
"qiskit[all]~=1.2.4\n",
|
|
"qiskit-aer~=0.15.1\n",
|
|
"qiskit-ibm-runtime~=0.31.0\n",
|
|
"qiskit-serverless~=0.17.1\n",
|
|
"qiskit-ibm-catalog~=0.1\n",
|
|
"```\n",
|
|
"</details>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a7f87a76-b7a1-4dbb-ad45-55ff63c4665b",
|
|
"metadata": {},
|
|
"source": [
|
|
"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. The Qiskit SDK 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": [
|
|
"# This import is needed for python versions prior to 3.10\n",
|
|
"from __future__ import annotations\n",
|
|
"\n",
|
|
"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 (\n",
|
|
" PassManagerStagePlugin,\n",
|
|
")\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(\n",
|
|
" pass_manager_config.coupling_map\n",
|
|
" )\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",
|
|
"<Tabs>\n",
|
|
" <TabItem value=\"package-table-toml\" label=\"pyproject.toml\" default>\n",
|
|
" ```toml\n",
|
|
" [project.entry-points.\"qiskit.transpiler.layout\"]\n",
|
|
" \"my_layout\" = \"my_qiskit_plugin:MyLayoutPlugin\"\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-cfg\" label=\"setup.cfg\">\n",
|
|
" ```ini\n",
|
|
" [options.entry_points]\n",
|
|
" qiskit.transpiler.layout =\n",
|
|
" my_layout = my_qiskit_plugin:MyLayoutPlugin\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-py\" label=\"setup.py\">\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",
|
|
" </TabItem>\n",
|
|
"</Tabs>"
|
|
]
|
|
},
|
|
{
|
|
"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.\n",
|
|
"\n",
|
|
"If you want to use a built-in transpiler stage as the starting point for your transpiler stage plugin, you can obtain the pass manager for a built-in transpiler stage using [PassManagerStagePluginManager](/api/qiskit/qiskit.transpiler.preset_passmanagers.plugin.PassManagerStagePluginManager#passmanagerstagepluginmanager). The following code cell shows how to do this to obtain the built-in optimization stage for optimization level 3."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "f4d578d6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from qiskit.transpiler.preset_passmanagers.plugin import (\n",
|
|
" PassManagerStagePluginManager,\n",
|
|
")\n",
|
|
"\n",
|
|
"# Initialize the plugin manager\n",
|
|
"plugin_manager = PassManagerStagePluginManager()\n",
|
|
"\n",
|
|
"# Here we create a pass manager config to use as an example.\n",
|
|
"# Instead, you should use the pass manager config that you already received as input\n",
|
|
"# to the pass_manager method of your PassManagerStagePlugin.\n",
|
|
"pass_manager_config = PassManagerConfig()\n",
|
|
"\n",
|
|
"# Obtain the desired built-in transpiler stage\n",
|
|
"optimization = plugin_manager.get_passmanager_stage(\n",
|
|
" \"optimization\", \"default\", pass_manager_config, optimization_level=3\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"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": 4,
|
|
"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",
|
|
"<Admonition type=\"note\" title=\"Note\">\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",
|
|
"</Admonition>\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",
|
|
"<Tabs>\n",
|
|
" <TabItem value=\"package-table-toml\" label=\"pyproject.toml\" default>\n",
|
|
" ```toml\n",
|
|
" [project.entry-points.\"qiskit.unitary_synthesis\"]\n",
|
|
" \"my_unitary_synthesis\" = \"my_qiskit_plugin:MyUnitarySynthesisPlugin\"\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-cfg\" label=\"setup.cfg\">\n",
|
|
" ```ini\n",
|
|
" [options.entry_points]\n",
|
|
" qiskit.unitary_synthesis =\n",
|
|
" my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-py\" label=\"setup.py\">\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",
|
|
" </TabItem>\n",
|
|
"</Tabs>\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": 5,
|
|
"id": "31bfaf30",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"['aqc', 'default', 'sk']"
|
|
]
|
|
},
|
|
"execution_count": 5,
|
|
"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": 6,
|
|
"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,\n",
|
|
" high_level_object,\n",
|
|
" coupling_map=None,\n",
|
|
" target=None,\n",
|
|
" qubits=None,\n",
|
|
" **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",
|
|
"<Tabs>\n",
|
|
" <TabItem value=\"package-table-toml\" label=\"pyproject.toml\" default>\n",
|
|
" ```toml\n",
|
|
" [project.entry-points.\"qiskit.synthesis\"]\n",
|
|
" \"clifford.my_clifford_synthesis\" = \"my_qiskit_plugin:MyCliffordSynthesisPlugin\"\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-cfg\" label=\"setup.cfg\">\n",
|
|
" ```ini\n",
|
|
" [options.entry_points]\n",
|
|
" qiskit.synthesis =\n",
|
|
" clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin\n",
|
|
" ```\n",
|
|
" </TabItem>\n",
|
|
" <TabItem value=\"package-table-py\" label=\"setup.py\">\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",
|
|
" </TabItem>\n",
|
|
"</Tabs>\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-synthesis-plugins) for listing installed plugins, and ensure that your plugin appears in the list:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "fbe1f265",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"['ag', 'bm', 'default', 'greedy', 'layers', 'lnn']"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"from qiskit.transpiler.passes.synthesis import (\n",
|
|
" high_level_synthesis_plugin_names,\n",
|
|
")\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": [
|
|
"<Admonition type=\"tip\" title=\"Recommendation\">\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",
|
|
"</Admonition>"
|
|
]
|
|
}
|
|
],
|
|
"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
|
|
}
|