diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 0a1a57894ac0..ee4f889912b4 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -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 @@ -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 @@ -830,6 +839,13 @@ 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 @@ -837,16 +853,17 @@ def build_coupling_map(self, two_q_gate=None): 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 " @@ -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 diff --git a/releasenotes/notes/fix-mixed-ideal-target-coupling-map-7fca04f9c5139a49.yaml b/releasenotes/notes/fix-mixed-ideal-target-coupling-map-7fca04f9c5139a49.yaml new file mode 100644 index 000000000000..497183ec90c0 --- /dev/null +++ b/releasenotes/notes/fix-mixed-ideal-target-coupling-map-7fca04f9c5139a49.yaml @@ -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 `__ + - | + 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. diff --git a/test/python/transpiler/test_target.py b/test/python/transpiler/test_target.py index ccf879605420..efb3e4a2ab94 100644 --- a/test/python/transpiler/test_target.py +++ b/test/python/transpiler/test_target.py @@ -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) @@ -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,)}