From fecdcc786d653f7af1584d65eca9a9b0f4aa8dc2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 30 Apr 2024 14:33:46 -0400 Subject: [PATCH 1/7] Add equivalence library entry for swap to ECR or CZ This commit adds two new equivalence library entries to cover the conversion from a SWAP gate to either ecr or cz directly. These are common 2q basis gates and without these entries in the equivalence library the path found from a lookup ends up with a much less efficient translation. This commit adds the two new entries so that the BasisTranslator will use a more efficient decomposition from the start when targeting these basis. This will hopefully result in less work for the optimization stage as the output will already be optimal and not require simplification. Testing for this PR is handled automatically by the built-in testing harness in test_gate_definitions.py that evaluates all the entries in the standard equivalence library for unitary equivalence. --- .../standard_gates/equivalence_library.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index 9793dc0202a8..51f1688a0e7e 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -850,6 +850,90 @@ def _cnot_rxx_decompose(plus_ry: bool = True, plus_rxx: bool = True): def_swap.append(inst, qargs, cargs) _sel.add_equivalence(SwapGate(), def_swap) +# SwapGate +# +# q_0: ─X─ +# │ ≡ +# q_1: ─X─ +# +# global phase: π/2 +# ┌─────────┐┌────┐ ┌──────┐┌──────────┐┌────┐┌────────┐┌──────┐» +# q_0: ┤ Rz(π/2) ├┤ √X ├──────────┤0 ├┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π) ├┤0 ├» +# ├─────────┤├────┤┌────────┐│ Ecr │├─────────┬┘├────┤└────────┘│ Ecr │» +# q_1: ┤ Rz(π/2) ├┤ √X ├┤ Rz(-π) ├┤1 ├┤ Rz(π/2) ├─┤ √X ├──────────┤1 ├» +# └─────────┘└────┘└────────┘└──────┘└─────────┘ └────┘ └──────┘» +# « ┌─────────┐ ┌────┐┌─────────┐┌──────┐┌──────────┐┌────┐┌──────────┐ +# «q_0: ┤ Rz(π/2) ├─┤ √X ├┤ Rz(π/2) ├┤0 ├┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π/2) ├ +# « ├─────────┴┐└────┘└─────────┘│ Ecr │├─────────┬┘├────┤├──────────┤ +# «q_1: ┤ Rz(-π/2) ├─────────────────┤1 ├┤ Rz(π/2) ├─┤ √X ├┤ Rz(-π/2) ├ +# « └──────────┘ └──────┘└─────────┘ └────┘└──────────┘ +# +q = QuantumRegister(2, "q") +def_swap_ecr = QuantumCircuit(q, global_phase=pi / 2) +def_swap_ecr.rz(pi / 2, 0) +def_swap_ecr.sx(0) +def_swap_ecr.rz(pi / 2, 1) +def_swap_ecr.sx(1) +def_swap_ecr.rz(-pi, 1) +def_swap_ecr.ecr(0, 1) +def_swap_ecr.rz(pi / 2, 1) +def_swap_ecr.sx(1) +def_swap_ecr.rz(pi / 2, 0) +def_swap_ecr.sx(0) +def_swap_ecr.rz(-pi, 0) +def_swap_ecr.ecr(0, 1) +def_swap_ecr.rz(pi / 2, 0) +def_swap_ecr.sx(0) +def_swap_ecr.rz(-pi / 2, 0) +def_swap_ecr.ecr(0, 1) +def_swap_ecr.rz(-pi / 2, 0) +def_swap_ecr.sx(0) +def_swap_ecr.rz(-pi / 2, 0) +def_swap_ecr.rz(pi / 2, 1) +def_swap_ecr.sx(1) +def_swap_ecr.rz(-pi / 2, 1) +_sel.add_equivalence(SwapGate(), def_swap_ecr) + +# SwapGate +# +# q_0: ─X─ +# │ ≡ +# q_1: ─X─ +# +# global phase: 7π/4 +# ┌─────────┐ ┌────┐ ┌────┐ ┌────────┐» +# q_0: ┤ Rz(π/2) ├──┤ √X ├───■────┤ √X ├──────────────────────■──┤ Rz(-π) ├» +# └──┬────┬─┘┌─┴────┴─┐ │ ┌──┴────┴──┐┌────┐┌──────────┐ │ ┌┴────────┤» +# q_1: ───┤ √X ├──┤ Rz(-π) ├─■─┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π/2) ├─■─┤ Rz(π/2) ├» +# └────┘ └────────┘ └──────────┘└────┘└──────────┘ └─────────┘» +# « ┌────┐┌──────────┐ ┌────┐ ┌─────────┐ +# «q_0: ┤ √X ├┤ Rz(-π/2) ├─■────┤ √X ├──┤ Rz(π/2) ├ +# « ├────┤└──────────┘ │ ┌──┴────┴─┐└─────────┘ +# «q_1: ┤ √X ├─────────────■─┤ Rz(π/2) ├─────────── +# « └────┘ └─────────┘ +q = QuantumRegister(2, "q") +def_swap_cz = QuantumCircuit(q, global_phase=7 * pi / 4) +def_swap_cz.rz(pi / 2, 0) +def_swap_cz.sx(0) +def_swap_cz.sx(1) +def_swap_cz.rz(-pi, 1) +def_swap_cz.cz(0, 1) +def_swap_cz.sx(0) +def_swap_cz.rz(-pi / 2, 1) +def_swap_cz.sx(1) +def_swap_cz.rz(-pi / 2, 1) +def_swap_cz.cz(0, 1) +def_swap_cz.rz(-pi, 0) +def_swap_cz.sx(0) +def_swap_cz.rz(-pi / 2, 0) +def_swap_cz.rz(pi / 2, 1) +def_swap_cz.sx(1) +def_swap_cz.cz(0, 1) +def_swap_cz.sx(0) +def_swap_cz.rz(pi / 2, 0) +def_swap_cz.rz(pi / 2, 1) +_sel.add_equivalence(SwapGate(), def_swap_cz) + # iSwapGate # # ┌────────┐ ┌───┐┌───┐ ┌───┐ From 5acc7807d018ef0424fa0fd6f8f7b4d45caf9c3d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 30 Apr 2024 15:38:49 -0400 Subject: [PATCH 2/7] Add name to annotated gate circuit in qpy backwards compat tests --- test/qpy_compat/test_qpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/qpy_compat/test_qpy.py b/test/qpy_compat/test_qpy.py index 58ee1abc2a2f..438b002f11bd 100755 --- a/test/qpy_compat/test_qpy.py +++ b/test/qpy_compat/test_qpy.py @@ -661,7 +661,7 @@ def generate_annotated_circuits(): CXGate(), [InverseModifier(), ControlModifier(1), PowerModifier(1.4), InverseModifier()] ) op2 = AnnotatedOperation(XGate(), InverseModifier()) - qc = QuantumCircuit(6, 1) + qc = QuantumCircuit(6, 1, name="Annotated circuits") qc.cx(0, 1) qc.append(op1, [0, 1, 2]) qc.h(4) From 4e90c0892b393c83485b4c290a9c4e834ee3227f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 1 May 2024 13:08:00 -0400 Subject: [PATCH 3/7] Fix equivalence library tests As fallout from the addition of SingletonGate and SingletonControlledGate we were accidentally not running large portions of the unit tests which validate the default session equivalence library. This test dynamically runs based on all members of the standard gate library by looking at all defined subclasses of Gate and ControlledGate. But with the introduction of SingletonGate and SingletonControlledGate all the unparameterized gates in the library were not being run through the tests. This commit fixes this to catch that the swap definition added in the previous commit on this PR branch used an incorrect definition of SwapGate using ECRGate. The definition will be fixed in a follow up PR. --- test/python/circuit/test_gate_definitions.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 8e608a163900..38bf7046cae5 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -21,6 +21,7 @@ from qiskit import QuantumCircuit, QuantumRegister from qiskit.quantum_info import Operator from qiskit.circuit import ParameterVector, Gate, ControlledGate +from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate from qiskit.circuit.library import standard_gates from qiskit.circuit.library import ( HGate, @@ -260,7 +261,12 @@ class TestGateEquivalenceEqual(QiskitTestCase): """Test the decomposition of a gate in terms of other gates yields the same matrix as the hardcoded matrix definition.""" - class_list = Gate.__subclasses__() + ControlledGate.__subclasses__() + class_list = ( + SingletonGate.__subclasses__() + + SingletonControlledGate.__subclasses__() + + Gate.__subclasses__() + + ControlledGate.__subclasses__() + ) exclude = { "ControlledGate", "DiagonalGate", @@ -313,7 +319,11 @@ def test_equivalence_phase(self, gate_class): with self.subTest(msg=gate.name + "_" + str(ieq)): op1 = Operator(gate) op2 = Operator(equivalency) - self.assertEqual(op1, op2) + msg = ( + f"Equivalence entry from '{gate.name}' to:\n" + f"{str(equivalency.draw('text'))}\nfailed" + ) + self.assertEqual(op1, op2, msg) @ddt From 1a0521e658efa8b8a1bff9447171b39f8ddd1163 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 1 May 2024 13:12:01 -0400 Subject: [PATCH 4/7] Use a more efficient and actually correct circuit for ECR target The previous definition of a swap gate using ECR rz and sx was incorrect and also not as efficient as possible. This was missed because the tests were accidently broken since #10314 which was fixed in the previous commit. This commit updates the definition to use one that is actually correct and also more efficient with fewer 1 qubit gates. Co-authored-by: Alexander Ivrii --- .../standard_gates/equivalence_library.py | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index 51f1688a0e7e..88afa3e54dac 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -869,29 +869,16 @@ def _cnot_rxx_decompose(plus_ry: bool = True, plus_rxx: bool = True): # « └──────────┘ └──────┘└─────────┘ └────┘└──────────┘ # q = QuantumRegister(2, "q") -def_swap_ecr = QuantumCircuit(q, global_phase=pi / 2) -def_swap_ecr.rz(pi / 2, 0) -def_swap_ecr.sx(0) -def_swap_ecr.rz(pi / 2, 1) -def_swap_ecr.sx(1) -def_swap_ecr.rz(-pi, 1) -def_swap_ecr.ecr(0, 1) -def_swap_ecr.rz(pi / 2, 1) -def_swap_ecr.sx(1) -def_swap_ecr.rz(pi / 2, 0) -def_swap_ecr.sx(0) -def_swap_ecr.rz(-pi, 0) -def_swap_ecr.ecr(0, 1) -def_swap_ecr.rz(pi / 2, 0) -def_swap_ecr.sx(0) +def_swap_ecr = QuantumCircuit(q) def_swap_ecr.rz(-pi / 2, 0) +def_swap_ecr.sx(1) def_swap_ecr.ecr(0, 1) -def_swap_ecr.rz(-pi / 2, 0) +def_swap_ecr.rz(-pi / 2, 1) def_swap_ecr.sx(0) +def_swap_ecr.ecr(1, 0) def_swap_ecr.rz(-pi / 2, 0) -def_swap_ecr.rz(pi / 2, 1) def_swap_ecr.sx(1) -def_swap_ecr.rz(-pi / 2, 1) +def_swap_ecr.ecr(0, 1) _sel.add_equivalence(SwapGate(), def_swap_ecr) # SwapGate From 1354c2f1593c691eb913caf8908600167ab8b3bc Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 1 May 2024 15:47:56 -0400 Subject: [PATCH 5/7] Update ECR circuit diagram in comment --- .../standard_gates/equivalence_library.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index 88afa3e54dac..cbfb6291a7b5 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -856,17 +856,11 @@ def _cnot_rxx_decompose(plus_ry: bool = True, plus_rxx: bool = True): # │ ≡ # q_1: ─X─ # -# global phase: π/2 -# ┌─────────┐┌────┐ ┌──────┐┌──────────┐┌────┐┌────────┐┌──────┐» -# q_0: ┤ Rz(π/2) ├┤ √X ├──────────┤0 ├┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π) ├┤0 ├» -# ├─────────┤├────┤┌────────┐│ Ecr │├─────────┬┘├────┤└────────┘│ Ecr │» -# q_1: ┤ Rz(π/2) ├┤ √X ├┤ Rz(-π) ├┤1 ├┤ Rz(π/2) ├─┤ √X ├──────────┤1 ├» -# └─────────┘└────┘└────────┘└──────┘└─────────┘ └────┘ └──────┘» -# « ┌─────────┐ ┌────┐┌─────────┐┌──────┐┌──────────┐┌────┐┌──────────┐ -# «q_0: ┤ Rz(π/2) ├─┤ √X ├┤ Rz(π/2) ├┤0 ├┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π/2) ├ -# « ├─────────┴┐└────┘└─────────┘│ Ecr │├─────────┬┘├────┤├──────────┤ -# «q_1: ┤ Rz(-π/2) ├─────────────────┤1 ├┤ Rz(π/2) ├─┤ √X ├┤ Rz(-π/2) ├ -# « └──────────┘ └──────┘└─────────┘ └────┘└──────────┘ +# ┌──────────┐┌──────┐ ┌────┐ ┌──────┐┌──────────┐┌──────┐ +# q_0: ┤ Rz(-π/2) ├┤0 ├───┤ √X ├───┤1 ├┤ Rz(-π/2) ├┤0 ├ +# └──┬────┬──┘│ Ecr │┌──┴────┴──┐│ Ecr │└──┬────┬──┘│ Ecr │ +# q_1: ───┤ √X ├───┤1 ├┤ Rz(-π/2) ├┤0 ├───┤ √X ├───┤1 ├ +# └────┘ └──────┘└──────────┘└──────┘ └────┘ └──────┘ # q = QuantumRegister(2, "q") def_swap_ecr = QuantumCircuit(q) From f261c9fd1e96bb477f211a0a53c21a9c6d5e516d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 2 May 2024 05:37:15 -0400 Subject: [PATCH 6/7] Simplify cz equivalent circuit --- .../standard_gates/equivalence_library.py | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index cbfb6291a7b5..3ac88148afaa 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -882,32 +882,30 @@ def _cnot_rxx_decompose(plus_ry: bool = True, plus_rxx: bool = True): # q_1: ─X─ # # global phase: 7π/4 -# ┌─────────┐ ┌────┐ ┌────┐ ┌────────┐» -# q_0: ┤ Rz(π/2) ├──┤ √X ├───■────┤ √X ├──────────────────────■──┤ Rz(-π) ├» -# └──┬────┬─┘┌─┴────┴─┐ │ ┌──┴────┴──┐┌────┐┌──────────┐ │ ┌┴────────┤» -# q_1: ───┤ √X ├──┤ Rz(-π) ├─■─┤ Rz(-π/2) ├┤ √X ├┤ Rz(-π/2) ├─■─┤ Rz(π/2) ├» -# └────┘ └────────┘ └──────────┘└────┘└──────────┘ └─────────┘» -# « ┌────┐┌──────────┐ ┌────┐ ┌─────────┐ -# «q_0: ┤ √X ├┤ Rz(-π/2) ├─■────┤ √X ├──┤ Rz(π/2) ├ -# « ├────┤└──────────┘ │ ┌──┴────┴─┐└─────────┘ -# «q_1: ┤ √X ├─────────────■─┤ Rz(π/2) ├─────────── -# « └────┘ └─────────┘ +# ┌─────────┐ ┌────┐ ┌────┐ ┌────────┐┌────┐┌──────────┐ » +# q_0: ┤ Rz(π/2) ├────┤ √X ├────■─┤ √X ├─■─┤ Rz(-π) ├┤ √X ├┤ Rz(-π/2) ├─■─» +# └──┬────┬─┘┌───┴────┴──┐ │ ├────┤ │ └─┬────┬─┘└────┘└──────────┘ │ » +# q_1: ───┤ √X ├──┤ Rz(-3π/2) ├─■─┤ √X ├─■───┤ √X ├─────────────────────■─» +# └────┘ └───────────┘ └────┘ └────┘ » +# « ┌────┐ ┌─────────┐ +# «q_0: ───┤ √X ├──┤ Rz(π/2) ├ +# « ┌──┴────┴─┐└─────────┘ +# «q_1: ┤ Rz(π/2) ├─────────── +# « └─────────┘ +# q = QuantumRegister(2, "q") def_swap_cz = QuantumCircuit(q, global_phase=7 * pi / 4) def_swap_cz.rz(pi / 2, 0) def_swap_cz.sx(0) def_swap_cz.sx(1) -def_swap_cz.rz(-pi, 1) +def_swap_cz.rz(-3 * pi / 2, 1) def_swap_cz.cz(0, 1) def_swap_cz.sx(0) -def_swap_cz.rz(-pi / 2, 1) def_swap_cz.sx(1) -def_swap_cz.rz(-pi / 2, 1) def_swap_cz.cz(0, 1) def_swap_cz.rz(-pi, 0) def_swap_cz.sx(0) def_swap_cz.rz(-pi / 2, 0) -def_swap_cz.rz(pi / 2, 1) def_swap_cz.sx(1) def_swap_cz.cz(0, 1) def_swap_cz.sx(0) From d8d819f52907dc913364592be0319584a4722f72 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 2 May 2024 05:47:09 -0400 Subject: [PATCH 7/7] Simplify cz circuit even more Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com> Co-authored-by: Alexander Ivrii --- .../standard_gates/equivalence_library.py | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index 3ac88148afaa..c4619ca27858 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -881,36 +881,23 @@ def _cnot_rxx_decompose(plus_ry: bool = True, plus_rxx: bool = True): # │ ≡ # q_1: ─X─ # -# global phase: 7π/4 -# ┌─────────┐ ┌────┐ ┌────┐ ┌────────┐┌────┐┌──────────┐ » -# q_0: ┤ Rz(π/2) ├────┤ √X ├────■─┤ √X ├─■─┤ Rz(-π) ├┤ √X ├┤ Rz(-π/2) ├─■─» -# └──┬────┬─┘┌───┴────┴──┐ │ ├────┤ │ └─┬────┬─┘└────┘└──────────┘ │ » -# q_1: ───┤ √X ├──┤ Rz(-3π/2) ├─■─┤ √X ├─■───┤ √X ├─────────────────────■─» -# └────┘ └───────────┘ └────┘ └────┘ » -# « ┌────┐ ┌─────────┐ -# «q_0: ───┤ √X ├──┤ Rz(π/2) ├ -# « ┌──┴────┴─┐└─────────┘ -# «q_1: ┤ Rz(π/2) ├─────────── -# « └─────────┘ -# +# global phase: 3π/2 +# ┌────┐ ┌────┐ ┌────┐ +# q_0: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─ +# ├────┤ │ ├────┤ │ ├────┤ │ +# q_1: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─ +# └────┘ └────┘ └────┘ q = QuantumRegister(2, "q") -def_swap_cz = QuantumCircuit(q, global_phase=7 * pi / 4) -def_swap_cz.rz(pi / 2, 0) +def_swap_cz = QuantumCircuit(q, global_phase=-pi / 2) def_swap_cz.sx(0) def_swap_cz.sx(1) -def_swap_cz.rz(-3 * pi / 2, 1) def_swap_cz.cz(0, 1) def_swap_cz.sx(0) def_swap_cz.sx(1) def_swap_cz.cz(0, 1) -def_swap_cz.rz(-pi, 0) def_swap_cz.sx(0) -def_swap_cz.rz(-pi / 2, 0) def_swap_cz.sx(1) def_swap_cz.cz(0, 1) -def_swap_cz.sx(0) -def_swap_cz.rz(pi / 2, 0) -def_swap_cz.rz(pi / 2, 1) _sel.add_equivalence(SwapGate(), def_swap_cz) # iSwapGate