Skip to main contentIBM Quantum Documentation
This page is from the dev version of Qiskit SDK. Go to the stable version

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.


Synthesis Plugin API

Unitary Synthesis Plugin API

UnitarySynthesisPlugin()Abstract unitary synthesis plugin class
UnitarySynthesisPluginManager()Unitary Synthesis plugin manager class
unitary_synthesis_plugin_names()Return a list of installed unitary synthesis plugin names

High-Level Synthesis Plugin API

HighLevelSynthesisPlugin()Abstract high-level synthesis plugin class.
HighLevelSynthesisPluginManager()Class tracking the installed high-level-synthesis plugins.
high_level_synthesis_plugin_names(op_name)Return a list of plugin names installed for a given high level object name

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 table in pyproject.toml for the plugin package with the necessary entry points under the qiskit.unitary_synthesis namespace. For example:

[project.entry-points."qiskit.unitary_synthesis"]
"special" = "qiskit_plugin_pkg.module.plugin:SpecialUnitarySynthesis"

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 options 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 table in pyproject.toml for the plugin package with the necessary entry points under the qiskit.synthesis namespace. For example:

[project.entry-points."qiskit.synthesis"]
"clifford.special" = "qiskit_plugin_pkg.module.plugin:SpecialSynthesisClifford"

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

Unitary Synthesis 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.

High-level Synthesis Plugins

To use a high-level synthesis plugin, you first instantiate an HLSConfig to store the names of the plugins to use for various high-level objects. For example:

HLSConfig(permutation=["acg"], clifford=["layers"], linear_function=["pmh"])

creates a high-level synthesis configuration that uses the acg plugin for synthesizing PermutationGate objects, the layers plugin for synthesizing Clifford objects, and the pmh plugin for synthesizing LinearFunction objects. The keyword arguments are the Operation.name fields of the relevant objects. For example, all Clifford operations have the name clifford, so this is used as the keyword argument. You can specify any keyword argument here that you have installed plugins to handle, including custom user objects if you have plugins installed for them. See HLSConfig for more detail on alternate formats for configuring the plugins within each argument.

For each high-level object, the list of given plugins are tried in sequence until one of them succeeds (in the example above, each list only contains a single plugin). In addition to specifying 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.

Once created you then pass this HLSConfig object into the hls_config argument for transpile() or generate_preset_pass_manager() which will use the specified plugins as part of the larger compilation workflow.

To get a list of installed high level synthesis plugins for any given Operation.name, you can use the high_level_synthesis_plugin_names() function, passing the desired name as the argument:

high_level_synthesis_plugin_names("clifford")

will return a list of all the installed Clifford synthesis plugins.


Available Plugins

High-level synthesis plugins that are directly available in Qiskit include plugins for synthesizing Clifford objects, LinearFunction objects, and PermutationGate objects. Some of these plugins implicitly target all-to-all connectivity. This is not a practical limitation since HighLevelSynthesis typically runs before layout and routing, which will ensure that the final circuit adheres to the device connectivity by inserting additional SWAP gates. A good example is the permutation synthesis plugin ACGSynthesisPermutation which can synthesize any permutation with at most 2 layers of SWAP gates. On the other hand, some plugins implicitly target linear connectivity. Typically, the synthesizing circuits have larger depth and the number of gates, however no additional SWAP gates would be inserted if the following layout pass chose a consecutive line of qubits inside the topology of the device. A good example of this is the permutation synthesis plugin KMSSynthesisPermutation which can synthesize any permutation of n qubits in depth n. Typically, it is difficult to know in advance which of the two approaches: synthesizing circuits for all-to-all connectivity and inserting SWAP gates vs. synthesizing circuits for linear connectivity and inserting less or no SWAP gates lead a better final circuit, so it likely makes sense to try both and see which gives better results. Finally, some plugins can target a given connectivity, and hence should be run after the layout is set. In this case the synthesized circuit automatically adheres to the topology of the device. A good example of this is the permutation synthesis plugin TokenSwapperSynthesisPermutation which is able to synthesize arbitrary permutations with respect to arbitrary coupling maps. For more detail, please refer to description of each individual plugin.

Below are the synthesis plugin classes available in Qiskit. These classes should not be used directly, but instead should be used through the plugin interface documented above. The classes are listed here to ease finding the documentation for each of the included plugins and to ease the comparison between different synthesis methods for a given object.

Unitary Synthesis Plugins

AQC Synthesis Plugin (in qiskit.transpiler.passes.synthesis.aqc_plugin)

AQCSynthesisPlugin()An AQC-based Qiskit unitary synthesis plugin.

Unitary Synthesis Plugin (in qiskit.transpiler.passes.synthesis.unitary_synthesis)

DefaultUnitarySynthesis()The default unitary synthesis plugin.

Solovay-Kitaev Synthesis Plugin (in qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis)

SolovayKitaevSynthesis()A Solovay-Kitaev Qiskit unitary synthesis plugin.

High Level Synthesis

For each high-level object we give a table that lists all of its plugins available directly in Qiskit. We include the name of the plugin, the class of the plugin, the targeted connectivity map and optionally additional information. Recall the plugins should be used via the previously described HLSConfig, for example:

HLSConfig(permutation=["kms"])

creates a high-level synthesis configuration that uses the kms plugin for synthesizing PermutationGate objects – i.e. those with name = "permutation". In this case, the plugin name is “kms”, the plugin class is KMSSynthesisPermutation. This particular synthesis algorithm created a circuit adhering to the linear nearest-neighbor connectivity.

High Level Synthesis Plugins

Clifford Synthesis
Plugin namePlugin classTargeted connectivityDescription
"ag"AGSynthesisCliffordall-to-allgreedily optimizes CX-count
"bm"BMSynthesisCliffordall-to-alloptimal count for n=2,3; used in "default" for n=2,3
"greedy"GreedySynthesisCliffordall-to-allgreedily optimizes CX-count; used in "default" for n>=4
"layers"LayerSynthesisCliffordall-to-all
"lnn"LayerLnnSynthesisCliffordlinearmany CX-gates but guarantees CX-depth of at most 7*n+2
"default"DefaultSynthesisCliffordall-to-allusually best for optimizing CX-count (and optimal CX-count for n=2,3)
AGSynthesisClifford()Clifford synthesis plugin based on the Aaronson-Gottesman method.
BMSynthesisClifford()Clifford synthesis plugin based on the Bravyi-Maslov method.
GreedySynthesisClifford()Clifford synthesis plugin based on the greedy synthesis Bravyi-Hu-Maslov-Shaydulin method.
LayerSynthesisClifford()Clifford synthesis plugin based on the Bravyi-Maslov method to synthesize Cliffords into layers.
LayerLnnSynthesisClifford()Clifford synthesis plugin based on the Bravyi-Maslov method to synthesize Cliffords into layers, with each layer synthesized adhering to LNN connectivity.
DefaultSynthesisClifford()The default clifford synthesis plugin.
Linear Function Synthesis
Plugin namePlugin classTargeted connectivityDescription
"kms"KMSSynthesisLinearFunctionlinearmany CX-gates but guarantees CX-depth of at most 5*n
"pmh"PMHSynthesisLinearFunctionall-to-allgreedily optimizes CX-count; used in "default"
"default"DefaultSynthesisLinearFunctionall-to-allbest for optimizing CX-count
KMSSynthesisLinearFunction()Linear function synthesis plugin based on the Kutin-Moulton-Smithline method.
PMHSynthesisLinearFunction()Linear function synthesis plugin based on the Patel-Markov-Hayes method.
DefaultSynthesisLinearFunction()The default linear function synthesis plugin.
Permutation Synthesis
Plugin namePlugin classTargeted connectivityDescription
"basic"BasicSynthesisPermutationall-to-alloptimal SWAP-count; used in "default"
"acg"ACGSynthesisPermutationall-to-allguarantees SWAP-depth of at most 2
"kms"KMSSynthesisPermutationlinearmany SWAP-gates, but guarantees SWAP-depth of at most n
"token_swapper"TokenSwapperSynthesisPermutationanygreedily optimizes SWAP-count for arbitrary connectivity
"default"BasicSynthesisPermutationall-to-allbest for optimizing SWAP-count
BasicSynthesisPermutation()The permutation synthesis plugin based on sorting.
ACGSynthesisPermutation()The permutation synthesis plugin based on the Alon, Chung, Graham method.
KMSSynthesisPermutation()The permutation synthesis plugin based on the Kutin, Moulton, Smithline method.
TokenSwapperSynthesisPermutation()The permutation synthesis plugin based on the token swapper algorithm.
QFT Synthesis
Plugin namePlugin classTargeted connectivity
"full"QFTSynthesisFullall-to-all
"line"QFTSynthesisLinelinear
"default"QFTSynthesisFullall-to-all
QFTSynthesisFull()Synthesis plugin for QFT gates using all-to-all connectivity.
QFTSynthesisLine()Synthesis plugin for QFT gates using linear connectivity.
MCX Synthesis

The following table lists synthesis plugins available for an MCXGate gate with k control qubits. If the available number of clean/dirty auxiliary qubits is not sufficient, the corresponding synthesis method will return None.

Plugin namePlugin classNumber of clean ancillasNumber of dirty ancillasDescription
"gray_code"MCXSynthesisGrayCode00exponentially many CX gates; use only for small values of k
"noaux_v24"MCXSynthesisNoAuxV2400quadratic number of CX gates; use instead of "gray_code" for large values of k
"n_clean_m15"MCXSynthesisNCleanM15k-20at most 6*k-6 CX gates
"n_dirty_i15"MCXSynthesisNDirtyI150k-2at most 8*k-6 CX gates
"1_clean_b95"MCXSynthesis1CleanB9510at most 16*k-8 CX gates
"default"MCXSynthesisDefaultanyanychooses the best algorithm based on the ancillas available
MCXSynthesisGrayCode()Synthesis plugin for a multi-controlled X gate based on the Gray code.
MCXSynthesisNoAuxV24()Synthesis plugin for a multi-controlled X gate based on the implementation for MCPhaseGate, which is in turn based on the paper by Vale et al. (2024).
MCXSynthesisNCleanM15()Synthesis plugin for a multi-controlled X gate based on the paper by Maslov (2016).
MCXSynthesisNDirtyI15()Synthesis plugin for a multi-controlled X gate based on the paper by Iten et al. (2016).
MCXSynthesis1CleanB95()Synthesis plugin for a multi-controlled X gate based on the paper by Barenco et al. (1995).
MCXSynthesisDefault()The default synthesis plugin for a multi-controlled X gate.
MCMT Synthesis
Plugin namePlugin classNumber of clean ancillasNumber of dirty ancillasDescription
"vchain"MCMTSynthesisVChaink-10uses a linear number of Toffoli gates
"noaux"MCMTSynthesisNoAux00uses Qiskit’s standard control mechanism
"default"MCMTSynthesisDefaultanyanychooses the best algorithm based on the ancillas available
MCMTSynthesisVChain()A V-chain based synthesis for MCMTGate.
MCMTSynthesisNoAux()A V-chain based synthesis for MCMTGate.
MCMTSynthesisDefault()A default decomposition for MCMT gates.
Pauli Evolution Synthesis
Plugin namePlugin classDescriptionTargeted connectivity
"rustiq"PauliEvolutionSynthesisRustiquse the synthesis method from Rustiq circuit synthesis libraryall-to-all
"default"PauliEvolutionSynthesisDefaultuse a diagonalizing Clifford per Pauli termall-to-all
PauliEvolutionSynthesisDefault()Synthesize a PauliEvolutionGate using the default synthesis algorithm.
PauliEvolutionSynthesisRustiq()Synthesize a PauliEvolutionGate using Rustiq.
Modular Adder Synthesis
Plugin namePlugin classNumber of clean ancillasDescription
"ripple_cdkm"ModularAdderSynthesisC041a ripple-carry adder
"ripple_vbe"ModularAdderSynthesisV95n1n-1, for nn-bit numbersa ripple-carry adder
"qft"ModularAdderSynthesisD000a QFT-based adder
"default"ModularAdderSynthesisDefaultanychooses the best algorithm based on the ancillas available
ModularAdderSynthesisC04()A ripple-carry adder, modulo 2n2^n.
ModularAdderSynthesisD00()A QFT-based adder, modulo 2n2^n.
ModularAdderSynthesisV95()A ripple-carry adder, modulo 2n2^n.
ModularAdderSynthesisDefault()The default modular adder (no carry in, no carry out qubit) synthesis.
Half Adder Synthesis
Plugin namePlugin classNumber of clean ancillasDescription
"ripple_cdkm"HalfAdderSynthesisC041a ripple-carry adder
"ripple_vbe"HalfAdderSynthesisV95n1n-1, for nn-bit numbersa ripple-carry adder
"qft"HalfAdderSynthesisD000a QFT-based adder
"default"HalfAdderSynthesisDefaultanychooses the best algorithm based on the ancillas available
HalfAdderSynthesisC04()A ripple-carry adder with a carry-out bit.
HalfAdderSynthesisD00()A QFT-based adder with a carry-in and a carry-out bit.
HalfAdderSynthesisV95()A ripple-carry adder with a carry-out bit.
HalfAdderSynthesisDefault()The default half-adder (no carry in, but a carry out qubit) synthesis.
Full Adder Synthesis
Plugin namePlugin classNumber of clean ancillasDescription
"ripple_cdkm"FullAdderSynthesisC040a ripple-carry adder
"ripple_vbe"FullAdderSynthesisV95n1n-1, for nn-bit numbersa ripple-carry adder
"default"FullAdderSynthesisDefaultanychooses the best algorithm based on the ancillas available
FullAdderSynthesisC04()A ripple-carry adder with a carry-in and a carry-out bit.
FullAdderSynthesisV95()A ripple-carry adder with a carry-in and a carry-out bit.
FullAdderSynthesisDefault()A ripple-carry adder with a carry-in and a carry-out bit.
Multiplier Synthesis
Plugin namePlugin classNumber of clean ancillasDescription
"cumulative"MultiplierSynthesisH18depending on the AdderGate useda cumulative adder based on controlled adders
"qft"MultiplierSynthesisR170a QFT-based multiplier
MultiplierSynthesisH18()A cumulative multiplier based on controlled adders.
MultiplierSynthesisR17()A QFT-based multiplier.
Was this page helpful?
Report a bug or request content on GitHub.