Skip to content
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 2q fractional gates to the UnitarySynthesis tranpiler pass #13568

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
89ca81e
TwoQubitControlledUDecomposer to _decomposer_2q_from_basis_gates
ShellyGarion Dec 16, 2024
077ecb9
update (temporarily) basis gates in test
ShellyGarion Dec 16, 2024
18b20f6
minor fix
ShellyGarion Dec 18, 2024
967da13
add EulerBasis as a parameter to TwoQubitControlledUDecomposer
ShellyGarion Dec 19, 2024
3bb10fd
fix global_phase calculation in TwoQubitContolledUDecomposer
ShellyGarion Dec 22, 2024
14c7aac
add TwoQubitControlledUDecomposer to the docs
ShellyGarion Dec 22, 2024
eeff4cd
make the choice of kak_gate deterministic
ShellyGarion Dec 22, 2024
043a795
Merge branch 'main' into unitary_synth
ShellyGarion Jan 13, 2025
b94070a
remove XXDecomposer from _decomposer_2q_from_basis_gates
ShellyGarion Jan 13, 2025
0a1644b
make call_inner pub, add Clone, Debug
ShellyGarion Jan 15, 2025
48fec9b
add TwoQubitControlledUDecomposer to unitary_synthesis.rs
ShellyGarion Jan 15, 2025
009d87e
merge main branch, fix conflict
ShellyGarion Jan 15, 2025
85e2962
Fix exit condition for GOODBYE_SET and PARAM_SET
ElePT Jan 16, 2025
c5d0c97
fix conflict with main branch
ShellyGarion Jan 16, 2025
d7b84e9
make DEFAULT_ATOL public
ShellyGarion Jan 16, 2025
ea9a2d0
add TwoQubitControlledUDecomposer to synth_su4_sequence
ShellyGarion Jan 16, 2025
132a44f
Add support for parametrized decomposer gate in apply_synth_sequence
ElePT Jan 20, 2025
b1cb5a0
change DecomposerType enum to fix clippy error
ShellyGarion Jan 20, 2025
9cdf2da
add a random unitary test to test_parametrized_basis_gate_in_target
ShellyGarion Jan 20, 2025
1be749d
add public new_inner for TwoQubitControlledUDecomposer
ShellyGarion Jan 21, 2025
1495781
replace default 'ZYZ' by 'ZXZ' in TwoQubitControlledUDecomposer
ShellyGarion Jan 21, 2025
231af7f
remove using py in rust functions
ShellyGarion Jan 23, 2025
bb874c1
minor update to test
ShellyGarion Jan 23, 2025
c2a9ad4
make atol optional
ShellyGarion Jan 23, 2025
d39c005
fix conflict with main branch
ShellyGarion Jan 23, 2025
106ae4a
add a test with fractional gates in the backend
ShellyGarion Jan 23, 2025
9131e3d
add release notes
ShellyGarion Jan 23, 2025
6a82649
enhance tests following review
ShellyGarion Jan 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion qiskit/synthesis/two_qubit/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ def __init__(self, rxx_equivalent_gate: Type[Gate]):
self.rxx_equivalent_gate = rxx_equivalent_gate
self.scale = self._inner_decomposition.scale

def __call__(self, unitary: Operator | np.ndarray, *, atol=DEFAULT_ATOL) -> QuantumCircuit:
def __call__(
self, unitary: Operator | np.ndarray, approximate=False, use_dag=False, atol=DEFAULT_ATOL
) -> QuantumCircuit:
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
"""Returns the Weyl decomposition in circuit form.
Args:
unitary (Operator or ndarray): :math:`4 \times 4` unitary to synthesize.
Expand Down
52 changes: 41 additions & 11 deletions qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
RXXGate,
RZXGate,
RZZGate,
RYYGate,
ECRGate,
RXGate,
SXGate,
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -107,7 +129,12 @@ def _choose_kak_gate(basis_gates):
"""Choose the first available 2q gate to use in the KAK decomposition."""
ElePT marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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 is_controlled or is_supercontrolled) to differentiate cases for the 3 available decomposers: TwoQubitDecomposer, XXDecomposer, and TwoQubitControlledUDecomposer. However, for the Python code (just basis gates), it looks like we are simply substituting the XXDecomposer for the TwoQubitControlledUDecomposer. Is this the plan?

Copy link
Member Author

@ShellyGarion ShellyGarion Dec 17, 2024

Choose a reason for hiding this comment

The 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).
If a user provides the basis gates [rzz, rz, ry] how can we tell if they want an RZZGate with an arbitrary angle or an RZZGate with a fixed angle (and if so - which fixed value)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another comment: I ran the tests in qiskit/test/python/transpiler/test_unitary_synthesis.py (main branch), and couldn't find any test that goes into the following "if" statement (lines 181-188 above), so I wonder if the XXDecomposer here has ever been used.

Copy link
Contributor

@ElePT ElePT Dec 18, 2024

Choose a reason for hiding this comment

The 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 UnitarySynthesis class that used the QSD method with a target and had to add a couple of them. I think it would make sense to ensure that we have a couple of tests per decomposer, and that there are sufficient tests for the target (HW-aware) path (this I could do in a separate PR because I didn't have time to look more into it during the Rust migration).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user provides the basis gates [rzz, rz, ry] how can we tell if they want an RZZGate with an arbitrary angle or an RZZGate with a fixed angle?

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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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!

Copy link
Member Author

Choose a reason for hiding this comment

The 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:

def _replace_parameterized_gate(op):

Again, I could not find any test that checks the XXDecomposer with a given target, namely the tests don't go into here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that _decomposer_2q_from_target is not being tested at all, since the tests for UnitarySynthesis pass for the target use the rust code.
Should this function be removed or updated to include TwoQubitControlledUDecomposer ? (and then some tests should be added).

Copy link
Member Author

@ShellyGarion ShellyGarion Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the function _decomposer_2q_from_target is part of the DefaultUnitarySynthesis plugin we don't update it as part of this PR. We should consider porting this code to rust in a follow-up PR.

elif kak_gate is not None:
decomposer2q = TwoQubitBasisDecomposer(
kak_gate,
Expand Down
2 changes: 1 addition & 1 deletion test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,7 @@ def test_block_collection_reduces_1q_gate(self, basis_gates, gate_counts):
basis_gates=[
["u3", "cx"],
["rx", "rz", "iswap"],
["rx", "ry", "rxx"],
["ry", "rz", "rxx"],
],
)
def test_translation_method_synthesis(self, optimization_level, basis_gates):
Expand Down
3 changes: 2 additions & 1 deletion test/python/transpiler/test_unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ def test_empty_basis_gates(self):
@data(
["u3", "cx"],
["u1", "u2", "u3", "cx"],
["rx", "ry", "rxx"],
["ry", "rz", "rxx"],
["ry", "rz", "rzz"],
["rx", "rz", "iswap"],
["u3", "rx", "rz", "cz", "iswap"],
)
Expand Down
Loading