-
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
[WIP] Add 2q fractional gates to the UnitarySynthesis
tranpiler pass
#13568
base: main
Are you sure you want to change the base?
Changes from 2 commits
89ca81e
077ecb9
18b20f6
967da13
3bb10fd
14c7aac
eeff4cd
043a795
b94070a
0a1644b
48fec9b
009d87e
85e2962
c5d0c97
d7b84e9
ea9a2d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -38,6 +38,7 @@ | |||
RXXGate, | ||||
RZXGate, | ||||
RZZGate, | ||||
RYYGate, | ||||
ECRGate, | ||||
RXGate, | ||||
SXGate, | ||||
|
@@ -50,6 +51,10 @@ | |||
U3Gate, | ||||
RYGate, | ||||
RGate, | ||||
CRXGate, | ||||
CRYGate, | ||||
CRZGate, | ||||
CPhaseGate, | ||||
) | ||||
from qiskit.converters import circuit_to_dag, dag_to_circuit | ||||
from qiskit.dagcircuit.dagcircuit import DAGCircuit | ||||
|
@@ -62,6 +67,7 @@ | |||
from qiskit.synthesis.two_qubit.two_qubit_decompose import ( | ||||
TwoQubitBasisDecomposer, | ||||
TwoQubitWeylDecomposition, | ||||
TwoQubitControlledUDecomposer, | ||||
) | ||||
from qiskit.transpiler.basepasses import TransformationPass | ||||
from qiskit.transpiler.coupling import CouplingMap | ||||
|
@@ -88,16 +94,32 @@ | |||
"u3": U3Gate._standard_gate, | ||||
"ry": RYGate._standard_gate, | ||||
"r": RGate._standard_gate, | ||||
"rzz": RZZGate._standard_gate, | ||||
"ryy": RYYGate._standard_gate, | ||||
"rxx": RXXGate._standard_gate, | ||||
"rzx": RXXGate._standard_gate, | ||||
"cp": CPhaseGate._standard_gate, | ||||
"crx": RXXGate._standard_gate, | ||||
"cry": RXXGate._standard_gate, | ||||
"crz": RXXGate._standard_gate, | ||||
} | ||||
|
||||
KAK_GATE_PARAM_NAMES = { | ||||
"rxx": RXXGate, | ||||
"rzz": RZZGate, | ||||
"ryy": RYYGate, | ||||
"rzx": RZXGate, | ||||
"cphase": CPhaseGate, | ||||
"crx": CRXGate, | ||||
"cry": CRYGate, | ||||
"crz": CRZGate, | ||||
} | ||||
|
||||
KAK_GATE_NAMES = { | ||||
"cx": CXGate(), | ||||
"cz": CZGate(), | ||||
"iswap": iSwapGate(), | ||||
"rxx": RXXGate(pi / 2), | ||||
"ecr": ECRGate(), | ||||
"rzx": RZXGate(pi / 4), # typically pi/6 is also available | ||||
} | ||||
|
||||
GateNameToGate = get_standard_gate_name_mapping() | ||||
|
@@ -107,7 +129,12 @@ def _choose_kak_gate(basis_gates): | |||
"""Choose the first available 2q gate to use in the KAK decomposition.""" | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: It seems we are not choosing the element according to some order, but using set kak_gate = sorted([KAK_GATE_PARAM_NAMES[kak_gate_param] for kak_gate_params in kak_gates_params])[0] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good point @gadial, I had always assumed that this randomness was a reflection of the fact that there can be several equally valid gates. And if that's case, I wonder if we should favor the one that comes first in alphabetical order? At the same time, I am not sure how conscious some of these decisions were. There are other parts of the code (the parts that were migrated to Rust) where we actually rely on insertion order when there are several valid decompositions, so at that point I guess it's as arbitrary as sorting alphabetically. It might be also worth noting that these cases don't come up often in real scenarios because we rarely see a basis set with more than one 2q gate (although it can definitely happen). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the comment. I'll look into it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you @ElePT. My approach is usually to prefer the arbitrary to the nondeterminisitic, since nondeterministic behavior makes testing more difficult (since it's harder to reproduce problems) but also less resilient (since we are only testing the conditions generated nondeterminisically on the current testing machines, whereas a future version of Python might behave differently). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @gadial's comment. If the user provides several 2q KAK gates, this "pop" just randomly chooses one of them. We would probably want a more deterministic behavior (perhaps favoring more "commonly" used 2q gates). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that sounds reasonable, it helps with benchmarking and bug-catching. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in eeff4cd |
||||
kak_gate = None | ||||
kak_gates = set(basis_gates or []).intersection(KAK_GATE_NAMES.keys()) | ||||
if kak_gates: | ||||
kak_gates_params = set(basis_gates or []).intersection(KAK_GATE_PARAM_NAMES.keys()) | ||||
|
||||
if kak_gates_params: | ||||
kak_gate = KAK_GATE_PARAM_NAMES[kak_gates_params.pop()] | ||||
|
||||
elif kak_gates: | ||||
kak_gate = KAK_GATE_NAMES[kak_gates.pop()] | ||||
|
||||
return kak_gate | ||||
|
@@ -151,14 +178,17 @@ def _decomposer_2q_from_basis_gates(basis_gates, pulse_optimize=None, approximat | |||
kak_gate = _choose_kak_gate(basis_gates) | ||||
euler_basis = _choose_euler_basis(basis_gates) | ||||
basis_fidelity = approximation_degree or 1.0 | ||||
if isinstance(kak_gate, RZXGate): | ||||
backup_optimizer = TwoQubitBasisDecomposer( | ||||
CXGate(), | ||||
basis_fidelity=basis_fidelity, | ||||
euler_basis=euler_basis, | ||||
pulse_optimize=pulse_optimize, | ||||
) | ||||
decomposer2q = XXDecomposer(euler_basis=euler_basis, backup_optimizer=backup_optimizer) | ||||
# if isinstance(kak_gate, RZXGate): | ||||
# backup_optimizer = TwoQubitBasisDecomposer( | ||||
# CXGate(), | ||||
# basis_fidelity=basis_fidelity, | ||||
# euler_basis=euler_basis, | ||||
# pulse_optimize=pulse_optimize, | ||||
# ) | ||||
# decomposer2q = XXDecomposer(euler_basis=euler_basis, backup_optimizer=backup_optimizer) | ||||
|
||||
if kak_gate in KAK_GATE_PARAM_NAMES.values(): | ||||
decomposer2q = TwoQubitControlledUDecomposer(kak_gate) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double checking our discussion from earlier in the week: for the target-aware code we discussed adding a third check (apart from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case of basis gates, I'm not sure if we can have 3 options or only 2 (this is why this code is only commented out and not removed yet). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another comment: I ran the tests in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not surprised, the coverage of these tests is not good because a lot of the tests are in the synthesis folder and not testing the transpilation pipeline, but calling the synthesis methods directly. Maybe this is a good opportunity to do some test refactoring before merging this PR. During the Rust migration I also noticed there weren't any tests for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this is up to us to decide and document the behavior change. I am not sure how many users actually rely or expect the fixed angle? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, I started the test cleanup with this PR: #13582, that is not much but I think can be helpful for future development. Let me know if you have any thoughts on it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another question is what to expect when given a parametrized gate in the target? see e.g this test. Currently, we replace the parametrized gates by gates with a constant angle, without even checking if this angle is actually calibrated in the target:
Again, I could not find any test that checks the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that |
||||
elif kak_gate is not None: | ||||
decomposer2q = TwoQubitBasisDecomposer( | ||||
kak_gate, | ||||
|
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 wonder what's the motivation for this change. Are
approximate
oruse_dag
used in the code? Could it be an oversight?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 calling the
decomposer2q
later in the code it assumes to have both parameters, see e.gqiskit/qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Line 1046 in 17648eb
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 see. I keep forgetting that we still have python code for the target aware case (this is kept in case someone uses a non-default plugin). In that case I think we should respect the keyword-only argument:
I also have realized that the
TwoQubitControlledUDecomposer
is not documented. If we are going to add it to the default pipeline, I believe we should, as right now it's not part of the public interface. So we should probably make sure to add it to the API docs with the other decomposers and add a release note listing the class as a new feature.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 plan to add
TwoQubitControlledUDecomposer
to the API docs following this PR (as I may need to update the API as part of adding it to the transpiler pass)