-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add unitary synthesis plugin interface #6124
Conversation
This commit adds the initial steps for a unitary synthesis plugin interface. It enables external packages to ship plugin packages that then integrate cleanly into qiskit's transpiler without any need for extra imports or qiskit changes. The user can then just specify the 'unitary_synthesis_method' kwarg on the transpile() call and use the name of the external plugin and the UnitarySynthesis pass will leverage that plugin for synthesizing the unitary.
4e06c2d
to
ad04aaa
Compare
@@ -60,7 +62,9 @@ class UnitarySynthesis(TransformationPass): | |||
|
|||
def __init__(self, | |||
basis_gates: List[str], | |||
approximation_degree: float = 1): | |||
approximation_degree: float = 1, | |||
coupling_map = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to pass a PassManagerConfig
to the plugin to avoid extending parameters further and further (for example, in case of a potential noise-aware synthesis)? Maybe approximation_degree
should be part of PassManagerConfig
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue with using a PassManagerConfig
is that it is only used by transpile and the preset pass managers? If a user wants to create a custom pass manager or directly call this pass they'll have to go an instantiate this config object to pass to the pass constructor which nothing else requires. For the preset pass manager case I think it makes things simpler but I think we have to consider the standalone case too.
The one thing I thought about doing was making the api free form and just have the abstract run()
signature take all kwargs and then the plugins can use them opportunistically so that gives us a bit more flexibility in the api (as we can add parameters over time). But I decided on being explicit like this so we can make the api a bit more well defined.
…into synthesis-plugins
The usefulness of the plugin for unitaries >2q in the default pass manager pipelines was limited by the fact that previously the pass managers were all setup to run unroll3q before the unitary synthesis pass is ever run. The unroll3q pass just calls the circuit.data to decompose gates >=3q which for unitary gate objects just calls an inlined equivalent of the 'default' plugin. The purpose of this is to ensure later passes only ever need to deal with 2q gates. However with plugins now if a plugin only works on > 2q we'll never actually be passing an >= 3q unitaries to the plugin in the default pipelines. To fix this issue, this commit calls unitary synthesis before unroll 3q to use the synthesis pass to unroll any unitary gates instead of relying on the gate class's internal decomposition method which will always use the qiskit decomposition techniques.
This commit adds the UnitarySynthesis pass to the preset pass manager anywhere unrolling of custom or wide gates was done. Previously, we only ever called the UnitarySynthesis pass in the preset pass managers if the basis translation method was set to 'synthesis' or we were using level3 and then it was part of the optimization pass (as part of 2q peephole optimization). This was an issue in that we're implicitly calling the _define() method of the class whenever we're unrolling gates >= 3q or gates with a custom definition. The _define() method is basically identical to the UnitarySynthesis pass except it doesn't expose the options to set a basis gate or approximation degree, which would result in the output gates from unitary gates in the unroll steps are always in the U3 basis. This meant we would be converting unitary gates to u3 (and cx) which would result in a conversion to the target basis later, even if we could just go to the target basis directly. This is also future proofing for Qiskit#6124 where a plugin interface is added to the UnitarySynthesis pass and can potentially be used for arbitrary sized unitaries. At the same time this change caught an issue qith the SingleQubitUnitary gate where the name was duplicated with the UnitaryGate which would result in errors when UnitarySynthesis was called because the UnitarySynthesis pass looks for gate named 'unitary' to run on. This is fixed and the SingleQubitUnitary gate's name is changed to 'squ' to differentiate it from the UnitaryGate.
This commit adds a new PassManager subclass, FullPassManager. This class is used to have a PassManager with a defined structure and stages for the normal transpile workflow. The preset pass managers are then updated to be FullPassManager objects they conform to the fixed structure. Having a class with defined phases gives us flexibility in the future for making the transpiler pluggable with external plugins (similar to what's done in PR Qiskit#6124) and also have backend hook points before or after different phases of the transpile. Fixes Qiskit#5978
Hi, I'm looking forward to see this plugin merged. I have a suggestion on the plugin interface. Here, https://github.com/Qiskit/qiskit-terra/pull/6124/files#diff-8e9b7b00856cfb221b326a03419bb4640f8c17f9da7612841224f2028602a9d4R163, there's a unitary matrix as an input in the interface. Matrix representation may be a limitation if we want to extend this plugin to a larger unitaries, say, 10 or more qubits. So, could it be better to pass a DAG or something else as a parameter? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost all minor documentation comments (mostly with click-able suggestions). The two non-trivial points are
- should
supported_basis
be the pluralsupported_bases
? - would it be better to have the keyword
coupling_map
be a 2-tuple of the map and what is currently the keywordqubits
?
Co-authored-by: Jake Lishman <jake@binhbar.com>
Co-authored-by: Jake Lishman <jake@binhbar.com>
This commit updates several aspects of the documentation to make it more clear how to use the plugin interface. It also updates the naming of supported_basis to be supported_bases as it's actually plural. The last change in this commit is making the default kwarg value for the unitary synthesis method default to 'default' (for the default built-in method) instead of None. This simplifies the logic in the unitary synthesis pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very close to approval from me. I pushed a commit fixing a couple of straggler basis
-> bases
typos. It looks like there's a minor bug in _choose_bases
, and probably one bit of extra documentation/behaviour definition we need about that, but other than those minor points, I'm ready to merge.
Co-authored-by: Jake Lishman <jake@binhbar.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me now.
Conflicts: constraints.txt Merged by keeping both new constraints at the bottom of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed constraints.txt
merge conflict.
In Qiskit#6124 which recently merged the UnitarySynthesis pass was added to preset passmanager to enable synthesis plugins that operate on > 2q from having an impact (otherwise they wouldn't be called). However that PR didn't have the option to avoid synthesizing 2q unitaries too early. This commit updates the base branch to the latest state of main which includes Qiskit#6124. This means this PR just adds a min_qubits option to UnitarySynthesis and leverages it for the unroll3q stage in the preset pass managers.
* Rely on UnitarySynthesis pass for unrolling UnitaryGates This commit adds the UnitarySynthesis pass to the preset pass manager anywhere unrolling of custom or wide gates was done. Previously, we only ever called the UnitarySynthesis pass in the preset pass managers if the basis translation method was set to 'synthesis' or we were using level3 and then it was part of the optimization pass (as part of 2q peephole optimization). This was an issue in that we're implicitly calling the _define() method of the class whenever we're unrolling gates >= 3q or gates with a custom definition. The _define() method is basically identical to the UnitarySynthesis pass except it doesn't expose the options to set a basis gate or approximation degree, which would result in the output gates from unitary gates in the unroll steps are always in the U3 basis. This meant we would be converting unitary gates to u3 (and cx) which would result in a conversion to the target basis later, even if we could just go to the target basis directly. This is also future proofing for #6124 where a plugin interface is added to the UnitarySynthesis pass and can potentially be used for arbitrary sized unitaries. At the same time this change caught an issue qith the SingleQubitUnitary gate where the name was duplicated with the UnitaryGate which would result in errors when UnitarySynthesis was called because the UnitarySynthesis pass looks for gate named 'unitary' to run on. This is fixed and the SingleQubitUnitary gate's name is changed to 'squ' to differentiate it from the UnitaryGate. * Add option to set a minimum size to unitarysynthesis pass This commit adds a new option to the UnitarySynthesis pass constructor, min_qubits, which is used to specify a minimimum size unitary to synthesize. If the unitary is smaller than that it will be skipped. This is then used by the UnitarySynthesis instance in the unroll3q phase so we don't decompose 1 or 2q unitaries before routing. * Fix rebase error * Correct oversights from rebase Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit tweaks the preset passmanager definitions to not include the UnitarySynthesis pass as part of basis translation if the 'unitary' gate is in the basis gates. Previously in Qiskit#6124 and Qiskit#6349 the preset passmanagers were updated to leverage the user specified unitary synthesis method instead of implicitly using the default as part of the unroll 3q and unroll custom definition passes. This however had the unintended side effect of always synthesizing unitary gates even if it was in the basis (which is the case on Aer). This commit fixes this by only running unitary synthesis as part of the basis translation step if it's not in the basis. A potential follow-on here is to make the unroll 3q transpiler pass basis aware (since right now it will implicitly run unitary synthesis internally) and add a similar logic around the use of the UnitarySynthesis pass for unrolling 3q or larger gates. The unroll 3q transpiler stage suffers from this same issue. However, this wasn't done here because the way 3q unrolling is used is to reduce the gates to be less than 3 qubits so the layout and routing phase can deal work with the gates in the circuit would likely cause issues if a unitary gate larger than 2 qubits was in the circuit being transpiled.
As was pointed out by @jakelishman in code review. Prior to Qiskit#6124 the unitary synthesis pass was checking the basis for the presence of unitary (and swap gates if pulse_optimize=True) in the basis set and skipping the gates in the pass directly instead of in the pass manager. This logic was lost when we added the plugin interface in Qiskit#6124 (probably as part of a rebase as it was a long lived branch). Since we used to have the logic in the pass itself this commit changes the approach to not adjust the preset passmanager usage and just make the pass detect if unitary is in the basis and not synthesize. This simplifies the logic and makes it less error prone. At the same time the test coverage is expanded to ensure we preserve this behavior moving forward (as aer's testing requires it).
) * Ensure UnitaryGate's are preserved by transpile if in basis gates This commit tweaks the preset passmanager definitions to not include the UnitarySynthesis pass as part of basis translation if the 'unitary' gate is in the basis gates. Previously in #6124 and #6349 the preset passmanagers were updated to leverage the user specified unitary synthesis method instead of implicitly using the default as part of the unroll 3q and unroll custom definition passes. This however had the unintended side effect of always synthesizing unitary gates even if it was in the basis (which is the case on Aer). This commit fixes this by only running unitary synthesis as part of the basis translation step if it's not in the basis. A potential follow-on here is to make the unroll 3q transpiler pass basis aware (since right now it will implicitly run unitary synthesis internally) and add a similar logic around the use of the UnitarySynthesis pass for unrolling 3q or larger gates. The unroll 3q transpiler stage suffers from this same issue. However, this wasn't done here because the way 3q unrolling is used is to reduce the gates to be less than 3 qubits so the layout and routing phase can deal work with the gates in the circuit would likely cause issues if a unitary gate larger than 2 qubits was in the circuit being transpiled. * Skip level 3 in tests The level 3 preset passmanager by default runs 2q block collection which is then synthesized into a unitary. As long as we're running that in the pass manager it's not really possible right now to preserve input unitaries through the transpilation. A potential follow on PR could potential skip the synthesis pass if 'unitary' is in the basis so the optimization pass would just collect 2q blocks into a unitary and pass that to the backend directly. However, that's not in scope for this PR (and it's not clear if we want that or not). * Revert to main preset passmanager logic As was pointed out by @jakelishman in code review. Prior to #6124 the unitary synthesis pass was checking the basis for the presence of unitary (and swap gates if pulse_optimize=True) in the basis set and skipping the gates in the pass directly instead of in the pass manager. This logic was lost when we added the plugin interface in #6124 (probably as part of a rebase as it was a long lived branch). Since we used to have the logic in the pass itself this commit changes the approach to not adjust the preset passmanager usage and just make the pass detect if unitary is in the basis and not synthesize. This simplifies the logic and makes it less error prone. At the same time the test coverage is expanded to ensure we preserve this behavior moving forward (as aer's testing requires it). * Add level 3 back to tests * Improve efficieny if gates to synthesize are in basis
* Add FullPassManager class for pass manager with defined stages This commit adds a new PassManager subclass, FullPassManager. This class is used to have a PassManager with a defined structure and stages for the normal transpile workflow. The preset pass managers are then updated to be FullPassManager objects they conform to the fixed structure. Having a class with defined phases gives us flexibility in the future for making the transpiler pluggable with external plugins (similar to what's done in PR #6124) and also have backend hook points before or after different phases of the transpile. Fixes #5978 * Add docs * Deduplicate preset passmanager construction * Update docs * Add dedicated scheduling stage to FullPassManager * Add missing new UnitarySynthesis kwargs after rebase * Use basis_translator as default method instead of basis Co-authored-by: Kevin Krsulich <kevin@krsulich.net> * Rename FullPassManager StructuredPassManager * Rename generate_scheduling_post_opt() generate_scheduling() * Fix missing and incorrect arguments * Fix more rebase issues * Fix even more rebase issues * Only run unroll3q on level 0-2 if coupling map is set To preserve the behavior prior to this reorganization this commit makes the Unroll3qorMore pass run if we have a coupling map set. This is because this pass is only needed to be run for these optimization levels so that the layout and routing passes can function (as they only work with 2q gates). If we're not running these passes we shouldn't be unrolling gates. This will fix the last 4 QAOA test failures post the recent rebase as that was failing because we were trying to unroll an unbound 4q hamiltonian gate when weren't before. * Rework StructuredPassManager as a more dynamic StagedPassManager This commit redefines the previous StructuredPassManager class into a more dynamic StagedPassmanager. The StagedPassManager doesn't have fixed hard coded stages anymore but instead lets users define their own stages. It also adds implicit 'pre_' and 'post_' hook points for each listed stage. This lets users dynamically define the stages based on a particular use case. * Fix docs * Update internal pass set on each access This commit updates the api for the StagedPassManager to refresh the internal pass list on each access. This adds a little overhead but with the tradeoff of making in place modifications to a stage's pass manager reflected without needing to manually call an update method. * Rename phases attribute to stages * Fix lint * Explicitly set name in qpy compat tests The rework of the preset passmanager construction changes the import order slightly and the number of circuits constructed in a session prior to the qpy compat deserialization side generating equivalent circuits for comparision has changed. This is causing all the deserialization side numbers to change and the tests are now failing because the circuit names are not equivalent. Since the exact name is a side effect of the import order (based on the number of unnamed circuits created in the session priort) it's not part of what the qpy tests are validating. We're trying to assert that the naem is preserved loading a qpy file across a version boundary. To fix this issues this commit adds an explicit name to the generation for all the circuits to ensure that we have a deterministic name for each circuit. * Apply suggestions from code review Co-authored-by: Luciano Bello <bel@zurich.ibm.com> * Run black * Update type hint * Remove out of date docstring note * Update copyright header date in qiskit/transpiler/preset_passmanagers/common.py Co-authored-by: Luciano Bello <bel@zurich.ibm.com> * Add check for invalid stage names * Add backwards compatibility note * Add docs on using StagedPassManager features with preset passmanagers Co-authored-by: Kevin Krsulich <kevin@krsulich.net> Co-authored-by: Luciano Bello <bel@zurich.ibm.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Summary
This commit adds the initial steps for a unitary synthesis plugin
interface. It enables external packages to ship plugin packages that
then integrate cleanly into qiskit's transpiler without any need for
extra imports or qiskit changes. The user can then just specify the
'unitary_synthesis_method' kwarg on the transpile() call and use the
name of the external plugin and the UnitarySynthesis pass will leverage
that plugin for synthesizing the unitary.
Details and comments
TODO: