Skip to content

Commit d2d3cf7

Browse files
Fix ConsolidateBlocks pass for collecting non-CX KAK gate (#14417) (#14424)
A bug was introduced into the ConsolidateBlocks pass in #13884 around how the KAK gate name was passed to rust from the Python pass. The consolidate blocks internal logic around whether to consolidate a pass or not is based on the estimate from the selected decomposer of whether the estimated number of gates used to synthesis the unitary would exceed the number of basis gates in the block. To do this the pass counts the gate with the synthesis target name in the block. However, in the case of the decomposer being the TwoQubitBasisDecomposer the name being used for this look wasn't the name of the gate, but instead was an internal sentinel value string "USER_GATE" which is used to handle arbitrary user gate definitions in the decomposer. This led to the consolidate blocks pass missing opportunities for consolidation. This commit fixes the oversight so that the name we use for making this determination is the actual basis gate name and the pass functions correctly. Fixes #14413 (cherry picked from commit 4e9231d) Co-authored-by: Matthew Treinish <[email protected]>
1 parent 1e9114d commit d2d3cf7

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

qiskit/transpiler/passes/optimization/consolidate_blocks.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def __init__(
9191
"""
9292
super().__init__()
9393
self.basis_gates = None
94+
self.basis_gate_name = None
9495
# Bypass target if it doesn't contain any basis gates (i.e. it's a _FakeTarget), as this
9596
# not part of the official target model.
9697
self.target = target if target is not None and len(target.operation_names) > 0 else None
@@ -99,21 +100,25 @@ def __init__(
99100
self.force_consolidate = force_consolidate
100101
if kak_basis_gate is not None:
101102
self.decomposer = TwoQubitBasisDecomposer(kak_basis_gate)
103+
self.basis_gate_name = kak_basis_gate.name
102104
elif basis_gates is not None:
103105
kak_gates = KAK_GATE_NAMES.keys() & (basis_gates or [])
104106
kak_param_gates = KAK_GATE_PARAM_NAMES.keys() & (basis_gates or [])
105107
if kak_param_gates:
106108
self.decomposer = TwoQubitControlledUDecomposer(
107109
KAK_GATE_PARAM_NAMES[list(kak_param_gates)[0]]
108110
)
111+
self.basis_gate_name = list(kak_param_gates)[0]
109112
elif kak_gates:
110113
self.decomposer = TwoQubitBasisDecomposer(
111114
KAK_GATE_NAMES[list(kak_gates)[0]], basis_fidelity=approximation_degree or 1.0
112115
)
116+
self.basis_gate_name = list(kak_gates)[0]
113117
else:
114118
self.decomposer = None
115119
else:
116120
self.decomposer = TwoQubitBasisDecomposer(CXGate())
121+
self.basis_gate_name = "cx"
117122

118123
def run(self, dag):
119124
"""Run the ConsolidateBlocks pass on `dag`.
@@ -134,7 +139,7 @@ def run(self, dag):
134139
consolidate_blocks(
135140
dag,
136141
self.decomposer._inner_decomposer,
137-
self.decomposer.gate_name,
142+
self.basis_gate_name,
138143
self.force_consolidate,
139144
target=self.target,
140145
basis_gates=self.basis_gates,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed an issue in the :class:`.ConsolidateBlocks` transpiler pass where it
5+
would fail to consolidate some blocks if the KAK gate selected (either
6+
directly or via the target) is supercontrolled and not :class:`.CXGate`.
7+
Fixed `#14413 <https://github.com/Qiskit/qiskit/issues/14413>`__

test/python/transpiler/test_consolidate_blocks.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
Tests for the ConsolidateBlocks transpiler pass.
1515
"""
1616

17-
import unittest
1817
import numpy as np
1918
from ddt import ddt, data
2019

@@ -24,10 +23,12 @@
2423
SwapGate,
2524
CXGate,
2625
CZGate,
26+
ECRGate,
2727
UnitaryGate,
2828
SXGate,
2929
XGate,
3030
RZGate,
31+
RZZGate,
3132
)
3233
from qiskit.converters import circuit_to_dag
3334
from qiskit.quantum_info.operators import Operator
@@ -352,12 +353,13 @@ def test_single_gate_block_outside_basis(self):
352353
expected.unitary(np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]), [0, 1])
353354
self.assertEqual(expected, pass_manager.run(qc))
354355

355-
def test_single_gate_block_outside_basis_with_target(self):
356+
@data(CXGate, CZGate, ECRGate)
357+
def test_single_gate_block_outside_basis_with_target(self, basis_gate):
356358
"""Test a gate outside basis defined in target gets converted."""
357359
qc = QuantumCircuit(2)
358360
target = Target(num_qubits=2)
359361
# Add ideal basis gates to all qubits
360-
target.add_instruction(CXGate())
362+
target.add_instruction(basis_gate())
361363
qc.swap(0, 1)
362364
consolidate_block_pass = ConsolidateBlocks(target=target)
363365
pass_manager = PassManager()
@@ -367,12 +369,13 @@ def test_single_gate_block_outside_basis_with_target(self):
367369
expected.unitary(np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]), [0, 1])
368370
self.assertEqual(expected, pass_manager.run(qc))
369371

370-
def test_single_gate_block_outside_local_basis_with_target(self):
372+
@data(CXGate, CZGate, ECRGate)
373+
def test_single_gate_block_outside_local_basis_with_target(self, basis_gate):
371374
"""Test that a gate in basis but outside valid qubits is treated as outside basis with target."""
372375
qc = QuantumCircuit(2)
373376
target = Target(num_qubits=2)
374-
# Add ideal cx to (1, 0) only
375-
target.add_instruction(CXGate(), {(1, 0): None})
377+
# Add ideal basis to (1, 0) only
378+
target.add_instruction(basis_gate(), {(1, 0): None})
376379
qc.cx(0, 1)
377380
consolidate_block_pass = ConsolidateBlocks(target=target)
378381
pass_manager = PassManager()
@@ -382,15 +385,16 @@ def test_single_gate_block_outside_local_basis_with_target(self):
382385
expected.unitary(np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]), [0, 1])
383386
self.assertEqual(expected, pass_manager.run(qc))
384387

385-
def test_single_gate_block_outside_target_with_matching_basis_gates(self):
388+
@data("cx", "ecr", "cz")
389+
def test_single_gate_block_outside_target_with_matching_basis_gates(self, basis_gate):
386390
"""Ensure the target is the source of truth with basis_gates also set."""
387391
qc = QuantumCircuit(2)
388392
target = Target(num_qubits=2)
389393
# Add ideal cx to (1, 0) only
390394
target.add_instruction(SwapGate())
391395
qc.swap(0, 1)
392396
consolidate_block_pass = ConsolidateBlocks(
393-
basis_gates=["id", "cx", "rz", "sx", "x"], target=target
397+
basis_gates=["id", basis_gate, "rz", "sx", "x"], target=target
394398
)
395399
pass_manager = PassManager()
396400
pass_manager.append(Collect2qBlocks())
@@ -669,6 +673,21 @@ def test_collect_and_synthesize_rzz(self, basis_gates):
669673
tqc = pm.run(qc)
670674
self.assertEqual(tqc.count_ops()["rzz"], 1)
671675

676+
@data(CXGate, CZGate, ECRGate)
677+
def test_rzz_collection(self, basis_gate):
678+
"""Test that a parameterized gate outside the target is consolidated."""
679+
phi = Parameter("phi")
680+
target = Target(num_qubits=2)
681+
target.add_instruction(SXGate(), {(0,): None, (1,): None})
682+
target.add_instruction(XGate(), {(0,): None, (1,): None})
683+
target.add_instruction(RZGate(phi), {(0,): None, (1,): None})
684+
target.add_instruction(basis_gate(), {(0, 1): None, (1, 0): None})
685+
consolidate_pass = ConsolidateBlocks(target=target)
672686

673-
if __name__ == "__main__":
674-
unittest.main()
687+
for angle in [np.pi / 2, np.pi]:
688+
qc = QuantumCircuit(2)
689+
qc.rzz(angle, 0, 1)
690+
res = consolidate_pass(qc)
691+
expected = QuantumCircuit(2)
692+
expected.unitary(np.asarray(RZZGate(angle)), [0, 1])
693+
self.assertEqual(res, expected)

0 commit comments

Comments
 (0)