-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
gate modifiers as first class semantics #6879
Comments
This issue is a perfect place to start the discussion on possible implementations of "lazy gate modifiers" that @jakelishman and I have been thinking about. All comments are highly welcome. Before describing possible implementations, here are a few things to keep in mind: First, we would like the changes to be mostly transparent to the users, that is, we do not want any existing code to start breaking, yet we do want to automatically get most benefit from whatever the lazy gates have to offer. Personally, I don't know how to perfectly achieve both at the same time. Second, we must make sure that the changes do not introduce bugs. At first, it seemed that the easiest implementation is to simply add extra fields (such as Third, we do not want to have lazy gates modifiers being part of the transpiler output, so somewhere in the transpiler flow we must stop procrastinating (i.e. remove the lazy modifiers). In what follows I will refer to the preliminary code in alexanderivrii#30. The proposed solution is to create a new class A very important question is how to create circuits with lazy ops. In the preliminary code, I have added new methods There is also a new I have not added I have also added a preliminary optimization pass Another point is that we now need better methods to detect if a gate is equal to another gate, or is an inverse of another gate, in particular that |
Continuing the above discussion, as per @jakelishman's suggestion, I was also experimenting with what may go wrong if we replace all The first thought is that if a gate implements its own On the positive side, the above example with a controlled-QFT adder is optimized and transpiled without explicitly changing the adder implementation. On the negative side, the change leads to a huge number of failing Qiskit tests, so this is probably not the direction we can follow in practice. In any case, let me describe some of the failure causes, in no particular order. A huge number of "controlled gate" tests fail because now we have A huge number of visualization tests fail, since the output drawing contains a box that says "Lazy" instead of the the expecting drawing of Things like We have tests that check that an
The Many tests fail because it's harder to detect that Many failing tests at the moment in |
Thanks for the detailed write up @alexanderivrii ! A few thoughts upon first reading, but I'm excited to discuss further!
I don't necessarily disagree, but I wonder if this is this strictly true. Simulators can have efficient implementations of e.g. controlled gates which, right now, we handle with specific instruction types, but I don't immediately see why these new modifiers couldn't make it into transpiler output.
Minor point on naming, "lazy" might be confusing to users, as it's only really "lazy" from the perspective of the compiler. That is, this isn't an operation that is applied lazily (as in, scheduled as-late-as-possible). The central difference I think from a user perspective is that it is a gate that's specified by a behavior rather than an implementation, so maybe something along the lines of abstract/high-level/behavioral operation?
This is a nice property to have, but I think the best place for this canonicalization is within the transpiler (as opposed to on the circuit). For one, this will be analysis the transpiler will need to know anyway, but also, canonicalizing early prevents the circuit from being able to read back to the user if they built a ctrl-power-inverse or a power-inverse-ctrl.
Is the existing
Would the long term plan be to keep both inverse and lazy_inverse indefinitely, or to remove the former in favor of the latter?
Is this something that could be handled by the high-level synthesis pass? I would think the overall structure (and the overall ability to dispatch to more than one synthesis method) would be similar for these.
Nice! @ajavadia will be excited :). Is there a path to generalizing this? It seems like a general pattern for controlling any subcircuit where a gate-inverse pair wraps another gate, like
This is also a good point. So far, we have relied upon
Thanks for compiling the comprehensive list of things this would break :) . Some of these I think we will need to resolve regardless, but I wouldn't necessarily rely on this too heavily to guide how to define the interface. That is, if tests are failing because they're expecting different types, or e.g. checking for a |
These are all great questions/suggestions, @kdk. I see multiple ways to improve my current implementation. I am now also convinced that it would be better to (1) keep a list of modifiers and have a "canonicalization" optimization for |
On the one hand, I very much like the idea that the higher-level-synthesis pass should handle both abstract mathematical objects (e.g., On the other hand, we have plans to extend higher-level-synthesis to support coupling map (and more generally Here is where things get confusing. Suppose we have a I am thinking that we can have a high-level-synthesis option like Thoughts/suggestions/comments are welcome! |
Say I build a circuit using the
power
andcontrol
modifier on gates.It is good that the circuit drawers support these modifiers natively:
The following issues exist:
I can't directly control the
quantum_info.random_unitary
U, have to build a UnitaryGate out of it first. i.e. quantum_info objects are not first class citizens in circuits.PhaseGate(θ).control(2)
yields aMCPhaseGate
under the hood (that's why the circuit drawer knows to draw it like that). I'm not sure we need thisMCPhaseGate
class, sincePhaseGate(θ).control(2)
already has well-defined semantics. The synthesis routine should know that this is a diagonal gate and thus decompose it appropriately. The drawer should also know that controlled phase gates can be drawn symmetrically.The decomposition gets greedily computed and stored in the gate itself when you modify a gate via power or control or inverse (https://github.com/Qiskit/qiskit-terra/blob/bdedeae59a5341b6ea4f399fe4a4f03c051e2e13/qiskit/circuit/library/standard_gates/p.py#L109). Instead we can keep them as powered- or controlled- or inverted- gates and make the rest of Qiskit work with that (e.g. quantum_info should be able to simulate this circuit at this high level without caring about the decomposition).
Similarly all gate optimization routines should work the same way even if the base gates happen to be controlled. For example optimize_1q_decomposition should work when we have ctrl-U and ctrl-V back-to-back (I did this in an old PR but never merged).
The text was updated successfully, but these errors were encountered: