Skip to content

Commit e3dc2bc

Browse files
jakelishmanmergify[bot]
authored andcommitted
Improve error messages in Target-based GateDirection pass (#9787)
* Improve error messages in `Target`-based `GateDirection` pass The `Target` version of `GateDirection` has more information available to make better error messages. It can distinguish cases where the failure is because a gate would be valid if flipped but the pass doesn't know how to do it, and the case where the gate wouldn't be valid in either direction. * Reword error message on failed flip --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit 648da26) # Conflicts: # qiskit/transpiler/passes/utils/gate_direction.py
1 parent 44cda51 commit e3dc2bc

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

qiskit/transpiler/passes/utils/gate_direction.py

+39-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from qiskit.converters import dag_to_circuit, circuit_to_dag
2121
from qiskit.circuit import QuantumRegister, ControlFlowOp
22-
from qiskit.dagcircuit import DAGCircuit
22+
from qiskit.dagcircuit import DAGCircuit, DAGOpNode
2323
from qiskit.circuit.library.standard_gates import (
2424
RYGate,
2525
HGate,
@@ -33,6 +33,10 @@
3333
)
3434

3535

36+
def _swap_node_qargs(node):
37+
return DAGOpNode(node.op, node.qargs[::-1], node.cargs)
38+
39+
3640
class GateDirection(TransformationPass):
3741
"""Modify asymmetric gates to match the hardware coupling direction.
3842
@@ -57,6 +61,8 @@ class GateDirection(TransformationPass):
5761
└──────┘ └───┘└──────┘└───┘
5862
"""
5963

64+
_KNOWN_REPLACEMENTS = frozenset(["cx", "cz", "ecr", "swap", "rzx", "rxx", "ryy", "rzz"])
65+
6066
def __init__(self, coupling_map, target=None):
6167
"""GateDirection pass.
6268
@@ -93,7 +99,23 @@ def __init__(self, coupling_map, target=None):
9399
self._cz_dag.add_qreg(qr)
94100
self._cz_dag.apply_operation_back(CZGate(), [qr[1], qr[0]], [])
95101

102+
<<<<<<< HEAD
96103
self._static_replacements = {"cx": self._cx_dag, "cz": self._cz_dag, "ecr": self._ecr_dag}
104+
=======
105+
self._swap_dag = DAGCircuit()
106+
qr = QuantumRegister(2)
107+
self._swap_dag.add_qreg(qr)
108+
self._swap_dag.apply_operation_back(SwapGate(), [qr[1], qr[0]], [])
109+
110+
# If adding more replacements (either static or dynamic), also update the class variable
111+
# `_KNOWN_REPLACMENTS` to include them in the error messages.
112+
self._static_replacements = {
113+
"cx": self._cx_dag,
114+
"cz": self._cz_dag,
115+
"ecr": self._ecr_dag,
116+
"swap": self._swap_dag,
117+
}
118+
>>>>>>> 648da26c6 (Improve error messages in `Target`-based `GateDirection` pass (#9787))
97119

98120
@staticmethod
99121
def _rzx_dag(parameter):
@@ -176,8 +198,9 @@ def _run_coupling_map(self, dag, wire_map, edges=None):
176198
dag.substitute_node_with_dag(node, self._rzz_dag(*node.op.params))
177199
else:
178200
raise TranspilerError(
179-
f"Flipping of gate direction is only supported "
180-
f"for {list(self._static_replacements)} at this time, not '{node.name}'."
201+
f"'{node.name}' would be supported on '{qargs}' if the direction were"
202+
f" swapped, but no rules are known to do that."
203+
f" {list(self._KNOWN_REPLACEMENTS)} can be automatically flipped."
181204
)
182205
return dag
183206

@@ -270,11 +293,22 @@ def _run_target(self, dag, wire_map):
270293
f"The circuit requires a connection between physical qubits {qargs}"
271294
f" for {node.name}"
272295
)
296+
elif self.target.instruction_supported(node.name, qargs):
297+
continue
298+
elif self.target.instruction_supported(node.name, swapped) or dag.has_calibration_for(
299+
_swap_node_qargs(node)
300+
):
301+
raise TranspilerError(
302+
f"'{node.name}' would be supported on '{qargs}' if the direction were"
303+
f" swapped, but no rules are known to do that."
304+
f" {list(self._KNOWN_REPLACEMENTS)} can be automatically flipped."
305+
)
273306
else:
274307
raise TranspilerError(
275-
f"Flipping of gate direction is only supported "
276-
f"for {list(self._static_replacements)} at this time, not '{node.name}'."
308+
f"'{node.name}' with parameters '{node.op.params}' is not supported on qubits"
309+
f" '{qargs}' in either direction."
277310
)
311+
278312
return dag
279313

280314
def run(self, dag):

test/python/transpiler/test_gate_direction.py

+43
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,49 @@ def test_target_control_flow(self):
430430
pass_ = GateDirection(None, target)
431431
self.assertEqual(pass_(circuit), expected)
432432

433+
def test_target_cannot_flip_message(self):
434+
"""A suitable error message should be emitted if the gate would be supported if it were
435+
flipped."""
436+
gate = Gate("my_2q_gate", 2, [])
437+
target = Target(num_qubits=2)
438+
target.add_instruction(gate, properties={(0, 1): None})
439+
440+
circuit = QuantumCircuit(2)
441+
circuit.append(gate, (1, 0))
442+
443+
pass_ = GateDirection(None, target)
444+
with self.assertRaisesRegex(TranspilerError, "'my_2q_gate' would be supported.*"):
445+
pass_(circuit)
446+
447+
def test_target_cannot_flip_message_calibrated(self):
448+
"""A suitable error message should be emitted if the gate would be supported if it were
449+
flipped."""
450+
target = Target(num_qubits=2)
451+
target.add_instruction(CXGate(), properties={(0, 1): None})
452+
453+
gate = Gate("my_2q_gate", 2, [])
454+
circuit = QuantumCircuit(2)
455+
circuit.append(gate, (1, 0))
456+
circuit.add_calibration(gate, (0, 1), pulse.ScheduleBlock())
457+
458+
pass_ = GateDirection(None, target)
459+
with self.assertRaisesRegex(TranspilerError, "'my_2q_gate' would be supported.*"):
460+
pass_(circuit)
461+
462+
def test_target_unknown_gate_message(self):
463+
"""A suitable error message should be emitted if the gate isn't valid in either direction on
464+
the target."""
465+
gate = Gate("my_2q_gate", 2, [])
466+
target = Target(num_qubits=2)
467+
target.add_instruction(CXGate(), properties={(0, 1): None})
468+
469+
circuit = QuantumCircuit(2)
470+
circuit.append(gate, (0, 1))
471+
472+
pass_ = GateDirection(None, target)
473+
with self.assertRaisesRegex(TranspilerError, "'my_2q_gate'.*not supported on qubits .*"):
474+
pass_(circuit)
475+
433476
def test_allows_calibrated_gates_coupling_map(self):
434477
"""Test that the gate direction pass allows a gate that's got a calibration to pass through
435478
without error."""

0 commit comments

Comments
 (0)