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

Bug fix in HoareOptimizer #13083

Merged
merged 4 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 12 additions & 8 deletions qiskit/transpiler/passes/optimization/hoare_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,20 +203,24 @@ def _traverse_dag(self, dag):
"""
import z3

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

ctrl_ones = z3.And(*ctrlvar)

remove_ctrl, new_dag, qb_idx = self._remove_control(gate, ctrlvar, trgtvar)
remove_ctrl, new_dag, _ = self._remove_control(gate, ctrlvar, trgtvar)

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

ctrl_ones = z3.And(*ctrlvar)
Expand Down
5 changes: 5 additions & 0 deletions releasenotes/notes/fix-hoare-opt-56d1ca6a07f07a2d.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
Fixed a bug in :class:`.HoareOptimizer` where a controlled gate was simplified
by removing its controls but the new gate was not handled correctly.
35 changes: 35 additions & 0 deletions test/python/transpiler/test_hoare_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,41 @@ def test_multiple_pass(self):

self.assertEqual(result2, circuit_to_dag(expected))

def test_remove_control_then_identity(self):
"""This first simplifies a gate by removing its control, then removes the
simplified gate by canceling it with another gate.
See: https://github.com/Qiskit/qiskit-terra/issues/13079
"""
# ┌───┐┌───┐┌───┐
# q_0: ┤ X ├┤ X ├┤ X ├
# └─┬─┘└───┘└─┬─┘
# q_1: ──■─────────┼──
# ┌───┐ │
# q_2: ┤ X ├───────■──
# └───┘
circuit = QuantumCircuit(3)
circuit.cx(1, 0)
circuit.x(2)
circuit.x(0)
circuit.cx(2, 0)

simplified = HoareOptimizer()(circuit)

# The CX(1, 0) gate is removed as the control qubit q_1 is initially 0.
# The CX(2, 0) gate is first replaced by X(0) gate as the control qubit q_2 is at 1,
# then the two X(0) gates are removed.
#
# q_0: ─────
#
# q_1: ─────
# ┌───┐
# q_2: ┤ X ├
# └───┘
expected = QuantumCircuit(3)
expected.x(2)

self.assertEqual(simplified, expected)


if __name__ == "__main__":
unittest.main()
Loading