Skip to content

Commit 576efcf

Browse files
Bug fix in HoareOptimizer (#13083)
* bug fix, test, reno * suggestions from code review; lint
1 parent dff9e81 commit 576efcf

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

qiskit/transpiler/passes/optimization/hoare_opt.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,24 @@ def _traverse_dag(self, dag):
203203
"""
204204
import z3
205205

206-
for node in dag.topological_op_nodes():
206+
# Pre-generate all DAG nodes, since we later iterate over them, while
207+
# potentially modifying and removing some of them.
208+
nodes = list(dag.topological_op_nodes())
209+
for node in nodes:
207210
gate = node.op
208-
ctrlqb, ctrlvar, trgtqb, trgtvar = self._seperate_ctrl_trgt(node)
211+
_, ctrlvar, trgtqb, trgtvar = self._seperate_ctrl_trgt(node)
209212

210213
ctrl_ones = z3.And(*ctrlvar)
211214

212-
remove_ctrl, new_dag, qb_idx = self._remove_control(gate, ctrlvar, trgtvar)
215+
remove_ctrl, new_dag, _ = self._remove_control(gate, ctrlvar, trgtvar)
213216

214217
if remove_ctrl:
215-
dag.substitute_node_with_dag(node, new_dag)
216-
gate = gate.base_gate
217-
node.op = gate.to_mutable()
218-
node.name = gate.name
219-
node.qargs = tuple((ctrlqb + trgtqb)[qi] for qi in qb_idx)
218+
# We are replacing a node by a new node over a smaller number of qubits.
219+
# This can be done using substitute_node_with_dag, which furthermore returns
220+
# a mapping from old node ids to new nodes.
221+
mapped_nodes = dag.substitute_node_with_dag(node, new_dag)
222+
node = next(iter(mapped_nodes.values()))
223+
gate = node.op
220224
_, ctrlvar, trgtqb, trgtvar = self._seperate_ctrl_trgt(node)
221225

222226
ctrl_ones = z3.And(*ctrlvar)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed a bug in :class:`.HoareOptimizer` where a controlled gate was simplified
5+
by removing its controls but the new gate was not handled correctly.

test/python/transpiler/test_hoare_opt.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,41 @@ def test_multiple_pass(self):
661661

662662
self.assertEqual(result2, circuit_to_dag(expected))
663663

664+
def test_remove_control_then_identity(self):
665+
"""This first simplifies a gate by removing its control, then removes the
666+
simplified gate by canceling it with another gate.
667+
See: https://github.com/Qiskit/qiskit-terra/issues/13079
668+
"""
669+
# ┌───┐┌───┐┌───┐
670+
# q_0: ┤ X ├┤ X ├┤ X ├
671+
# └─┬─┘└───┘└─┬─┘
672+
# q_1: ──■─────────┼──
673+
# ┌───┐ │
674+
# q_2: ┤ X ├───────■──
675+
# └───┘
676+
circuit = QuantumCircuit(3)
677+
circuit.cx(1, 0)
678+
circuit.x(2)
679+
circuit.x(0)
680+
circuit.cx(2, 0)
681+
682+
simplified = HoareOptimizer()(circuit)
683+
684+
# The CX(1, 0) gate is removed as the control qubit q_1 is initially 0.
685+
# The CX(2, 0) gate is first replaced by X(0) gate as the control qubit q_2 is at 1,
686+
# then the two X(0) gates are removed.
687+
#
688+
# q_0: ─────
689+
#
690+
# q_1: ─────
691+
# ┌───┐
692+
# q_2: ┤ X ├
693+
# └───┘
694+
expected = QuantumCircuit(3)
695+
expected.x(2)
696+
697+
self.assertEqual(simplified, expected)
698+
664699

665700
if __name__ == "__main__":
666701
unittest.main()

0 commit comments

Comments
 (0)