Skip to content

Commit

Permalink
Fix handling of global ideal gates for target build_coupling_map() (#…
Browse files Browse the repository at this point in the history
…8977)

* Fix handling of global ideal gates for target build_coupling_map()

This commit fixes the handling of the `build_coupling_map()` method so
that it correctly handles edge cases around a mix of ideal globally
available gates and gates that have qubit/connectivity constraints.
Previously, the method would incorrectly treat the presence of a
globally defined ideal gate as meaning there were no connectivity
constraints, even if that global gate was only on 1 qubit. While if the
gate operates on two qubits this is correct behavior.

Fixes #8971

* Add test for single qarg non-ideal case
  • Loading branch information
mtreinish committed Oct 31, 2022
1 parent 77588ff commit f6a343b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 6 deletions.
28 changes: 22 additions & 6 deletions qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,10 @@ def update_from_instruction_schedule_map(self, inst_map, inst_name_map=None, err
@property
def qargs(self):
"""The set of qargs in the target."""
if None in self._qarg_gate_map:
qargs = set(self._qarg_gate_map)
if len(qargs) == 1 and next(iter(qargs)) is None:
return None
return self._qarg_gate_map.keys()
return qargs

def qargs_for_operation_name(self, operation):
"""Get the qargs for a given operation name
Expand Down Expand Up @@ -813,8 +814,16 @@ def _build_coupling_graph(self):
self._coupling_graph = rx.PyDiGraph(multigraph=False)
self._coupling_graph.add_nodes_from([{} for _ in range(self.num_qubits)])
for gate, qarg_map in self._gate_map.items():
if qarg_map is None:
if self._gate_name_map[gate].num_qubits == 2:
self._coupling_graph = None
return
continue
for qarg, properties in qarg_map.items():
if qarg is None:
if self._gate_name_map[gate].num_qubits == 2:
self._coupling_graph = None
return
continue
if len(qarg) == 1:
self._coupling_graph[qarg[0]] = properties
Expand All @@ -830,23 +839,31 @@ def _build_coupling_graph(self):
def build_coupling_map(self, two_q_gate=None):
"""Get a :class:`~qiskit.transpiler.CouplingMap` from this target.
If there is a mix of two qubit operations that have a connectivity
constraint and those that are globally defined this will also return
``None`` because the globally connectivity means there is no contstraint
on the target. If you wish to see the constraints of the two qubit
operations that have constraints you should use the ``two_q_gate``
argument to limit the output to the gates which have a constraint.
Args:
two_q_gate (str): An optional gate name for a two qubit gate in
the Target to generate the coupling map for. If specified the
output coupling map will only have edges between qubits where
this gate is present.
Returns:
CouplingMap: The :class:`~qiskit.transpiler.CouplingMap` object
for this target.
for this target. If there are no connectivity constraints in
the target this will return ``None``.
Raises:
ValueError: If a non-two qubit gate is passed in for ``two_q_gate``.
IndexError: If an Instruction not in the Target is passed in for
``two_q_gate``.
"""
if None in self._qarg_gate_map:
if self.qargs is None:
return None
if any(len(x) > 2 for x in self.qargs):
if None not in self.qargs and any(len(x) > 2 for x in self.qargs):
logger.warning(
"This Target object contains multiqubit gates that "
"operate on > 2 qubits. This will not be reflected in "
Expand All @@ -865,7 +882,6 @@ def build_coupling_map(self, two_q_gate=None):
cmap = CouplingMap()
cmap.graph = coupling_graph
return cmap

if self._coupling_graph is None:
self._build_coupling_graph()
# if there is no connectivity constraints in the coupling graph treat it as not
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
fixes:
- |
Fixed an issue with the :meth:`.Target.build_coupling_map` method where
it would incorrectly return ``None`` for a :class:`~.Target` object
with a mix of ideal globally available instructions and instructions
that have qubit constraints. Now in such cases the
:meth:`.Target.build_coupling_map` will return a coupling map for the
constrained instruction (unless it's a 2 qubit operation which will
return ``None`` because globally there is no connectivity constraint).
Fixed `#8971 <https://github.com/Qiskit/qiskit-terra/issues/8971>`__
- |
Fixed an issue with the :attr:`.Target.qargs` attribute where it would
incorrectly return ``None`` for a :class:`~.Target` object that contained
any globally available ideal instruction.
34 changes: 34 additions & 0 deletions test/python/transpiler/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,30 @@ def test_coupling_map_3q_gate(self):
with self.assertRaises(ValueError):
fake_target.build_coupling_map("ccx")

def test_coupling_map_mixed_ideal_global_1q_and_2q_gates(self):
n_qubits = 3
target = Target()
target.add_instruction(CXGate(), {(i, i + 1): None for i in range(n_qubits - 1)})
target.add_instruction(RXGate(Parameter("theta")), {None: None})
cmap = target.build_coupling_map()
self.assertEqual([(0, 1), (1, 2)], cmap.get_edges())

def test_coupling_map_mixed_global_1q_and_2q_gates(self):
n_qubits = 3
target = Target()
target.add_instruction(CXGate(), {(i, i + 1): None for i in range(n_qubits - 1)})
target.add_instruction(RXGate(Parameter("theta")))
cmap = target.build_coupling_map()
self.assertEqual([(0, 1), (1, 2)], cmap.get_edges())

def test_coupling_map_mixed_ideal_global_2q_and_real_2q_gates(self):
n_qubits = 3
target = Target()
target.add_instruction(CXGate(), {(i, i + 1): None for i in range(n_qubits - 1)})
target.add_instruction(ECRGate())
cmap = target.build_coupling_map()
self.assertIsNone(cmap)

def test_physical_qubits(self):
self.assertEqual([], self.empty_target.physical_qubits)
self.assertEqual(list(range(5)), self.ibm_target.physical_qubits)
Expand Down Expand Up @@ -1388,6 +1412,16 @@ def test_qargs(self):
self.assertEqual(expected_aqt, self.aqt_target.qargs)
self.assertEqual(None, self.target_global_gates_only.qargs)

def test_qargs_single_qarg(self):
target = Target()
target.add_instruction(XGate(), {(0,): None})
self.assertEqual(
{
(0,),
},
target.qargs,
)

def test_qargs_for_operation_name(self):
self.assertEqual(
self.ibm_target.qargs_for_operation_name("rz"), {(0,), (1,), (2,), (3,), (4,)}
Expand Down

0 comments on commit f6a343b

Please sign in to comment.