-
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
Improvements to HighLevelSynthesis
transpiler pass
#13605
base: main
Are you sure you want to change the base?
Conversation
The interface is now quite ugly, and we also have an extra isinstance check for each gate, we will see if it can be removed.
This argument is not needed as the qubit tracker already restricts allowed ancilla qubits.
One or more of the following people are relevant to this code:
|
transpiled_circuit = HighLevelSynthesis(basis_gates=["cx", "u"])(circuit) | ||
self.assertEqual(transpiled_circuit.count_ops().keys(), {"cx", "u"}) |
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.
This actually fixes a problem: previously the circuit was not fully synthesized, now it is.
Pull Request Test Coverage Report for Build 12493914300Details
💛 - Coveralls |
@@ -143,7 +142,7 @@ def test_plugins(self): | |||
"""Test setting the HLS plugins for the modular adder.""" | |||
|
|||
# all gates with the plugins we check, including an expected operation | |||
plugins = [("cumulative_h18", "ccircuit-.*"), ("qft_r17", "qft")] | |||
plugins = [("cumulative_h18", "ch"), ("qft_r17", "mcphase")] |
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.
When simplifying the code for handling controlled-annotated operations (in particular wrapping a single operation into a circuit so that we only need to consider circuits and not both circuits and operations), I have changed the code on how a control
is applied to a circuit: instead of first converting the circuit into a gate and applying control to this gate, we create a new quantum circuit with the controlled version of each gate. This makes sure that we do not deteriorate the results in the case of a single iteration (and may actually improve the results in the case of a circuit). Hmm, now that I am writing this, they may be a slight problem when controlling a circuit that has a global phase, I will need to fix this.
UPDATE: that was indeed a bug, which is fixed in c5f0f0e.
Summary
This PR reimplements the internals of the
HighLevelSynthesis
transpiler pass, still in Python. The pass is now arguably much simpler and cleaner.Features:
AnnotatedOperations
inHighLevelSynthesis
to a plugin #13565.HighLevelSynthesis
transpiler pass needs to do something nontrivial, then except for the top-levelcircuit_to_dag
anddag_to_circuit
conversions, all the internal recursive functions work only withQuantumCircuit
s, greatly simplifying the API and avoiding constant back-and-forth conversions.The PR is based on multiple discussions with @Cryoris.
Since most of the changes are internal implementation details, the only thing I am emphasizing in the release notes is the plugin interface for annotated operations.
Details and comments
One particular implementation detail that has changed significantly is how we reason about ancilla qubits in combination with the highly recursive nature of the pass. Take for example a quantum circuit containing a custom gate whose definition involves another custom gate whose definition involves an MCX gate that we can synthesize more efficiently using the auxiliary qubits from the global circuit. This was already possible, and to support this we have introduced the
QubitContext
class that represents a mapping from the current (internal) circuit's qubits to the global qubits (i.e. qubits of the original circuit). If synthesizing an operation required more auxiliary variables, these mappings were extended to include these new variables. With this PR, all the internal functions directly work with the global qubits instead of these mappings. For instance, the internal_synthesize_operation
function now receives anOperation
and a list of global qubits it's defined on and returns.aQuantumCircuit
and a possibly extended list of global qubits on which the new quantum circuit is defined. This makes the booktracking significantly simpler. (In particular, the internalQubitContext
class will probably be deleted, or repurposed to speed up certain internal computations).A lot of this refactoring was needed to turn handling of annotated operations into a plugin. The way this was done before (and is pretty much the same way it's still done now) is first to synthesize the base operation of such an annotated operation using the full power of recursive synthesis, and then apply control, power and inverse modifiers to the obtained circuit. However, synthesizing the base operation requires the internal data and options of the
HighLevelSynthesis
pass itself, the qubits on which the base operation is defined, and the qubit tracker -- the last two are needed to allow using ancilla qubits. This motivated making the function_run
,_synthesize_operation
, etc. as global functions rather than belonging to a specificHighLevelSynthesis
object (alternatively we could make these staticfunctions). In addition, this flattening also simplifies moving these function to Rust in a followup.Alternatively, above we could have instantiate a new
HighLevelSynthesis
pass from within the current pass, however the initialization (i.e.HighLevelSynthesis__init__
) incurs a certain overhead in the (getting the lists of available plugins, etc.) . Instead, all the immutable entries are stored in the newHLSData
struct which is passed from one recursive function to another (and also to the plugin for annotated operations using theplugin_args
dict).Follow-up plans:
There are several follow-up items that will be handled in follow-up PRs.
None
or the synthesized result in the form of another triple (synthesized circuit, possibly extended list of global qubits on which this circuit is defined, possibly updated state of the qubits). Right now all the plugins only know how many clean/dirty ancilla qubits are available, but not what these qubits actually are, and only return the synthesized quantum circuit, without specifying ancilla qubits actually used. This might be important if we want to resynthesize a quantum circuit using ancilla qubits, when the connectivity map is provided, and the choice of an actual ancilla qubit actually matters.