Skip to content
Open
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
17 changes: 13 additions & 4 deletions qiskit/transpiler/passes/optimization/hoare_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,19 @@ def _multigate_opt(self, dag, qubit, max_idx=None, dnt_rec=None):

for node in gates_tbr:
# for rec call, only look at qubits that haven't been optimized yet
new_qb = [x for x in node.qargs if x not in dnt_rec]
dnt_rec.update(new_qb)
for qbt in new_qb:
idx = self.gatecache[qbt].index(node)
candidate_qb = [x for x in node.qargs if x not in dnt_rec]
valid_qb = []
for qbt in candidate_qb:
try:
idx = self.gatecache[qbt].index(node)
except ValueError:
continue
else:
valid_qb.append((qbt, idx))

dnt_rec.update(qbt for qbt, _ in valid_qb)

for qbt, idx in valid_qb:
# recursive chain to optimize all gates in this qubit's cache
self._multigate_opt(dag, qbt, max_idx=idx, dnt_rec=dnt_rec)
# truncate gatecache for this qubit to after above gate
Expand Down
9 changes: 9 additions & 0 deletions releasenotes/notes/2.3/fix-hoare-optimizer-cache.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
The `HoareOptimizer` no longer raises `ValueError: DAGOpNode(...) is not in list`
when running with the default cache size on circuits that trigger multi-gate
optimization. The pass now ignores stale cache entries during recursive
optimization, ensuring circuits such as the one reported in
`#14969 <https://github.com/Qiskit/qiskit/issues/14969>`__ transpile without
error.
66 changes: 66 additions & 0 deletions test/python/transpiler/test_hoare_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
from numpy import pi

from qiskit.utils import optionals
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import HoareOptimizer
from qiskit.converters import circuit_to_dag
from qiskit import QuantumCircuit
from qiskit.circuit.library import XGate, RZGate, CSwapGate, SwapGate
from qiskit.dagcircuit import DAGOpNode
from qiskit.quantum_info import Statevector
from qiskit.qasm3 import loads as qasm3_loads
from test import QiskitTestCase # pylint: disable=wrong-import-order


Expand Down Expand Up @@ -313,6 +315,70 @@ def test_successive_identity_removal(self):

self.assertEqual(result, circuit_to_dag(expected))

def test_issue_14969_regression(self):
"""Regression test for issue 14969: HoareOptimizer should not crash."""

bug_qasm = """OPENQASM 3.0;
include \"stdgates.inc\";
gate rzx(p0) _gate_q_0, _gate_q_1 {
h _gate_q_1;
cx _gate_q_0, _gate_q_1;
rz(p0) _gate_q_1;
cx _gate_q_0, _gate_q_1;
h _gate_q_1;
}
gate cu1(p0) _gate_q_0, _gate_q_1 {
p(0.5*p0) _gate_q_0;
cx _gate_q_0, _gate_q_1;
p((-0.5)*p0) _gate_q_1;
cx _gate_q_0, _gate_q_1;
p(0.5*p0) _gate_q_1;
}
gate cu1_o0(p0) _gate_q_0, _gate_q_1 {
x _gate_q_0;
cu1(p0) _gate_q_0, _gate_q_1;
x _gate_q_0;
}
gate cz_o0 _gate_q_0, _gate_q_1 {
x _gate_q_0;
cz _gate_q_0, _gate_q_1;
x _gate_q_0;
}
bit[1] c;
qubit[4] q;
cu1_o0(6) q[3], q[2];
cy q[2], q[1];
rzx(2) q[1], q[3];
cx q[1], q[0];
rx(2) q[0];
cx q[1], q[0];
cy q[0], q[3];
sdg q[0];
x q[0];
cz_o0 q[3], q[0];
sdg q[0];
sdg q[0];
cz_o0 q[0], q[1];
cz q[3], q[2];
s q[2];
swap q[3], q[2];
x q[2];
rz(5) q[2];
id q[2];
y q[2];
id q[2];
c[0] = measure q[2];
"""

circuit = qasm3_loads(bug_qasm)
pm = PassManager(HoareOptimizer())

# The current bug manifests as a ValueError during the pass run. This test will
# fail until the pass is fixed to handle the circuit correctly.
result = pm.run(circuit)

self.assertIsNotNone(result)

def test_targetsuccessive_identity_removal(self):
"""Should remove pair of controlled target successive
which are the inverse of each other, if they can be
Expand Down