Synthesis Plugins (qiskit.transpiler.passes.synthesis.plugin
)¶
This module defines the plugin interfaces for the synthesis transpiler passes
in Qiskit. These provide a hook point for external python packages to implement
their own synthesis techniques and have them seamlessly exposed as opt-in
options to users when they run transpile()
.
The plugin interfaces are built using setuptools entry points which enable packages external to qiskit to advertise they include a synthesis plugin.
See qiskit.transpiler.preset_passmanagers.plugin
for details on how
to write plugins for transpiler stages.
Writing Plugins¶
Unitary Synthesis Plugins¶
To write a unitary synthesis plugin there are 2 main steps. The first step is
to create a subclass of the abstract plugin class:
UnitarySynthesisPlugin
.
The plugin class defines the interface and contract for unitary synthesis
plugins. The primary method is
run()
which takes in a single positional argument, a unitary matrix as a numpy array,
and is expected to return a DAGCircuit
object
representing the synthesized circuit from that unitary matrix. Then to inform
the Qiskit transpiler about what information is necessary for the pass there
are several required property methods that need to be implemented such as
supports_basis_gates
and supports_coupling_map
depending on whether the
plugin supports and/or requires that input to perform synthesis. For the full
details refer to the
UnitarySynthesisPlugin
documentation for all the required fields. An example plugin class would look
something like:
from qiskit.transpiler.passes.synthesis import plugin
from qiskit_plugin_pkg.synthesis import generate_dag_circuit_from_matrix
class SpecialUnitarySynthesis(plugin.UnitarySynthesisPlugin):
@property
def supports_basis_gates(self):
return True
@property
def supports_coupling_map(self):
return False
@property
def supports_natural_direction(self):
return False
@property
def supports_pulse_optimize(self):
return False
@property
def supports_gate_lengths(self):
return False
@property
def supports_gate_errors(self):
return False
@property
def supports_gate_lengths_by_qubit(self):
return False
@property
def supports_gate_errors_by_qubit(self):
return False
@property
def min_qubits(self):
return None
@property
def max_qubits(self):
return None
@property
def supported_bases(self):
return None
def run(self, unitary, **options):
basis_gates = options['basis_gates']
dag_circuit = generate_dag_circuit_from_matrix(unitary, basis_gates)
return dag_circuit
If for some reason the available inputs to the
run()
method are insufficient please open an issue and we can discuss expanding the
plugin interface with new opt-in inputs that can be added in a backwards
compatible manner for future releases. Do note though that this plugin interface
is considered stable and guaranteed to not change in a breaking manner. If
changes are needed (for example to expand the available optional input options)
it will be done in a way that will not require changes from existing
plugins.
Note
All methods prefixed with supports_
are reserved on a
UnitarySynthesisPlugin
derived class for part of the interface. You
should not define any custom supports_*
methods on a subclass that
are not defined in the abstract class.
The second step is to expose the
UnitarySynthesisPlugin
as
a setuptools entry point in the package metadata. This is done by simply adding
an entry_points
entry to the setuptools.setup
call in the setup.py
for the plugin package with the necessary entry points under the
qiskit.unitary_synthesis
namespace. For example:
entry_points = {
'qiskit.unitary_synthesis': [
'special = qiskit_plugin_pkg.module.plugin:SpecialUnitarySynthesis',
]
},
(note that the entry point name = path
is a single string not a Python
expression). There isn’t a limit to the number of plugins a single package can
include as long as each plugin has a unique name. So a single package can
expose multiple plugins if necessary. The name default
is used by Qiskit
itself and can’t be used in a plugin.
Unitary Synthesis Plugin Configuration¶
For some unitary synthesis plugins that expose multiple options and tunables
the plugin interface has an option for users to provide a free form
configuration dictionary. This will be passed through to the run()
method
as the config
kwarg. If your plugin has these configuration options you
should clearly document how a user should specify these configuration options
and how they’re used as it’s a free form field.
High-Level Synthesis Plugins¶
Writing a high-level synthesis plugin is conceptually similar to writing a
unitary synthesis plugin. The first step is to create a subclass of the
abstract plugin class:
HighLevelSynthesisPlugin
,
which defines the interface and contract for high-level synthesis plugins.
The primary method is
run()
.
The positional argument high_level_object
specifies the “higher-level-object” to
be synthesized, which is any object of type Operation
(including, for example,
LinearFunction
or
Clifford
).
The keyword argument target
specifies the target backend, allowing the plugin
to access all target-specific information,
such as the coupling map, the supported gate set, and so on. The keyword argument
coupling_map
only specifies the coupling map, and is only used when target
is not specified.
The keyword argument qubits
specifies the list of qubits over which the
higher-level-object is defined, in case the synthesis is done on the physical circuit.
The 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.
Additionally, plugin-specific options and tunables can be specified via options
,
which is a free form configuration dictionary.
If your plugin has these configuration options you
should clearly document how a user should specify these configuration options
and how they’re used as it’s a free form field.
The method
run()
is expected to return a QuantumCircuit
object
representing the synthesized circuit from that higher-level-object.
It is also allowed to return None
representing that the synthesis method is
unable to synthesize the given higher-level-object.
The actual synthesis of higher-level objects is performed by
HighLevelSynthesis
transpiler pass.
For the full details refer to the
HighLevelSynthesisPlugin
documentation for all the required fields. An example plugin class would look
something like:
from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin
from qiskit.synthesis.clifford import synth_clifford_bm
class SpecialSynthesisClifford(HighLevelSynthesisPlugin):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
if higher_level_object.num_qubits <= 3:
return synth_clifford_bm(high_level_object)
else:
return None
The above example creates a plugin to synthesize objects of type
Clifford
that have
at most 3 qubits, using the method synth_clifford_bm
.
The second step is to expose the
HighLevelSynthesisPlugin
as
a setuptools entry point in the package metadata. This is done by adding
an entry_points
entry to the setuptools.setup
call in the setup.py
for the plugin package with the necessary entry points under the
qiskit.synthesis
namespace. For example:
entry_points = {
'qiskit.synthesis': [
'clifford.special = qiskit_plugin_pkg.module.plugin:SpecialSynthesisClifford',
]
},
(note that the entry point name = path
is a single string not a Python
expression). The name
consists of two parts separated by dot “.”: the
name of the
type of Operation
to which the synthesis plugin applies
(clifford
), and the name of the plugin (special
).
There isn’t a limit to the number of plugins a single package can
include as long as each plugin has a unique name.
Using Plugins¶
To use a plugin all you need to do is install the package that includes a
synthesis plugin. Then Qiskit will automatically discover the installed
plugins and expose them as valid options for the appropriate
transpile()
kwargs and pass constructors. If there are
any installed plugins which can’t be loaded/imported this will be logged to
Python logging.
To get the installed list of installed unitary synthesis plugins you can use the
qiskit.transpiler.passes.synthesis.plugin.unitary_synthesis_plugin_names()
function.
Plugin API¶
Unitary Synthesis Plugins¶
Abstract unitary synthesis plugin class |
|
Unitary Synthesis plugin manager class |
|
Return a list of installed unitary synthesis plugin names |
High-Level Synthesis Plugins¶
Abstract high-level synthesis plugin class. |
|
Class tracking the installed high-level-synthesis plugins. |