Add implicit iteration to CouplingMap (#9051)

* Add implicit iteration to CouplingMap

This adds an `__iter__` method that passes through to iteration through
the underlying rustworkx `EdgeList` (after construction).  This commit
does not add the other methods needed to make `CouplingMap` into a
Python sequence, such as `__getitem__` and `__len__`, because
constructing the `EdgeList` has a nonzero cost (the rustworkx
implementation eagerly collects the edge indices into a `Vec` to iterate
over), and could lead to hidden performance pitfalls.  The edges also
have no particular order associated with them, so implementing
`Sequence` would be a misnomer.

As a side-effect, this makes it legal to pass a `CouplingMap` instance
to the `CouplingMap` constructor, but this is not a true copy
constructor (which in Python are perhaps better spelled as
`CouplingMap.copy()` so immutable objects can avoid the new-object
allocation cost without messing around with `__new__`), since fields
like `description` would not be copied.  This commit takes no position
on whether a true copy constructor is a good idea.

* Fix terrible manual sorting

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Jake Lishman 2022-11-02 14:30:49 +00:00 committed by GitHub
parent fb16712ae4
commit a2ea9288a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 0 deletions

View File

@ -79,6 +79,9 @@ class CouplingMap:
"""
return self.graph.edge_list()
def __iter__(self):
return iter(self.graph.edge_list())
def add_physical_qubit(self, physical_qubit):
"""Add a physical qubit to the coupling graph as a node.

View File

@ -0,0 +1,15 @@
---
features:
- |
:class:`.CouplingMap` is now implicitly iterable, with the iteration being
identical to iterating through the output of :meth:`.CouplingMap.get_edges()`.
In other words,
.. code-block:: python
from qiskit.transpiler import CouplingMap
coupling = CouplingMap.from_line(3)
list(coupling) == list(coupling.get_edges())
will now function as expected, as will other iterations. This is purely a
syntactic convenience.

View File

@ -442,6 +442,12 @@ class CouplingTest(QiskitTestCase):
expected = [(0, 1), (1, 2), (2, 3)]
self.assertEqual(expected, edge_list, f"{edge_list} does not match {expected}")
def test_implements_iter(self):
"""Test that the object is implicitly iterable."""
coupling = CouplingMap.from_line(3)
expected = [(0, 1), (1, 0), (1, 2), (2, 1)]
self.assertEqual(sorted(coupling), expected)
class CouplingVisualizationTest(QiskitVisualizationTestCase):
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")