-
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
Ensure UnitaryGate's are preserved by transpile if in basis gates #7104
Conversation
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.
8208367
to
49fcc41
Compare
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).
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.
I couldn't put a comment on it because it's not in the diffs, but the if translation_method == "synthesis"
blocks will also always decompose unitary gates (for example, here's the level1 one): https://github.com/Qiskit/qiskit-terra/blob/f59a1fd4bca6c92683d9487c94db10e6c730ebfd/qiskit/transpiler/preset_passmanagers/level1.py#L193-L215
The first pass won't do it because it's limited to 3+ qubits, but the last pass will. This is probably desired behaviour (else why pass translation_method="synthesis"
- it's not the default?), but just bringing up the inconsistency.
backend_props=backend_properties, | ||
method=unitary_synthesis_method, | ||
), | ||
if basis_gates is not None and "unitary" not in basis_gates: |
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.
I think this changes the default behaviour if basis_gates
is None
- this is the relevant part of UnitarySynthesis.run
from immediately before #6124: https://github.com/Qiskit/qiskit-terra/blob/c202e460a7369fde41209c2f41ad1484a3810a31/qiskit/transpiler/passes/synthesis/unitary_synthesis.py#L158-L160
In there, if basis_gates is None
then the pass will run and synthesis unitary gates. I would think the old form is possibly what people would expect, but it's a bit of a funny one. If we want to maintain the exact behaviour, then it needs to be if (not basis_gates) or "unitary" not in basis_gates
(which also runs it if basis_gates=[]
), or perhaps more sensibly, if basis_gates is None or "unitary" not in basis_gates
.
Similar comments on every pass level.
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.
So, I've been testing this with 0.18.3 to compare the results with this PR:
qc = QuantumCircuit(2)
qc.unitary(random_unitary(4, seed=42), [0, 1])
qc.measure_all()
With result = transpile(qc, basis_gates=None)
:
0.18.3:
┌──────────┐ ░ ┌─┐
q_0: ┤0 ├─░─┤M├───
│ Unitary │ ░ └╥┘┌─┐
q_1: ┤1 ├─░──╫─┤M├
└──────────┘ ░ ║ └╥┘
meas: 2/════════════════╩══╩═
0 1
This PR:
┌──────────┐ ░ ┌─┐
q_0: ┤0 ├─░─┤M├───
│ Unitary │ ░ └╥┘┌─┐
q_1: ┤1 ├─░──╫─┤M├
└──────────┘ ░ ║ └╥┘
meas: 2/════════════════╩══╩═
0 1
With result = transpile(qc, basis_gates=[])
:
0.18.3:
qiskit.transpiler.exceptions.TranspilerError: "Unable to map source basis {('barrier', 2), ('u3', 1), ('measure', 1), ('cx', 2)} to target basis {'delay', 'reset', 'snapshot', 'barrier', 'measure'} over library <qiskit.circuit.equivalence.EquivalenceLibrary object at 0x7fbe564d2460>."
This PR:
qiskit.transpiler.exceptions.TranspilerError: "Unable to map source basis {('u3', 1), ('barrier', 2), ('measure', 1), ('cx', 2)} to target basis {'barrier', 'reset', 'measure', 'snapshot', 'delay'} over library <qiskit.circuit.equivalence.EquivalenceLibrary object at 0x7f2dcff347f0>."
So I think the translator method is ok as is. Unless I'm missing something or I need a different example circuit.
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.
For the record - the lines I highlighted above aren't the only relevant lines in 0.18.3. For one, the UnitarySynthesis
plugin wasn't even run at optimization_level=1
in 0.18.3, because that didn't come til #6349, and for two, even if you force it, there's an implicit skip because _choose_kak_gate(None)
would return None
, and cause no synthesis to be done.
For the qc = QuantumCircuit(2)
qc.unitary(random_unitary(4, seed=42), [0, 1])
qc.measure_all() With 0.18.3:
This PR:
So there is something more to fix in the synthesis path. At least it also works for the With 0.18.3:
This PR:
With 0.18.3:
This PR:
I'll add additional tests and come up with a fix for the synthesis path too. |
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).
…t-core into respect-unitary-in-basis
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.
I'm ticking this because I'm happy with this PR whether or not we merge the comment I made above, and I'm leaving work for the evening, so I don't want to hold up getting Aer's CI back online if we decide to go ahead without those changes.
If they are made, I'll re-approve again once I've seen them, or someone else can do it for me.
Summary
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.
Details and comments