Repository: Qiskit/qiskit

Issue:
DAGCircuit.predecessors do not return a list and order dependent of python version 
### Information

- **Qiskit Terra version**: master

### What is the current behavior?

It's the second time that I encounter a bug because different behavior  in python2 and python3 and `DAGCircuit.predecessors`. On top of that, I noticed that sometimes I used `next(predecessors)` because return an iterator, while the documentation says it should be a list.

### Steps to reproduce the problem

```python

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit.circuit import Measure
from qiskit.converters import circuit_to_dag


qr = QuantumRegister(1, 'qr')
cr = ClassicalRegister(1, 'cr')
circuit = QuantumCircuit(qr, cr)
circuit.rz(0.1, qr[0])
circuit.z(qr[0])
circuit.measure(qr[0], cr[0])

dag = circuit_to_dag(circuit)

for predecesor in dag.predecessors(dag.op_nodes(Measure)[0]):
    print(predecesor.type) # op / in

dag.remove_all_ops_named('z')

for predecesor in dag.predecessors(dag.op_nodes(Measure)[0]):
    print(predecesor.type) # Python2: op/in Python3: in/op
```

### What is the expected behavior?

Some order in both python versions. Also
```python
type(dag.predecessors(dag.op_nodes(Measure)[0]))                                                                                              
```
```
dict_keyiterator # should be a list, as promised in the docstring.
```

### Suggested solutions
Is it too okey to return this in `DAGCircuit.predecessors`?

```python
        return sorted(self._multi_graph.predecessors(node))
```



Discussion:
I have frequently experienced the same kind of python 2/3 issues with other DAG functions. However, I'm not sure that predecessors should have any inherent order, as according to the networkx docs `A predecessor of n is a node m such that there exists a directed edge from m to n`. This does not imply any inherent order in which the nodes are returned.

I think returning an iterator is preferable to a list, and it is what is returned from the networkx implementation, so the docstring should be adjusted rather than the code.
`quantum_predecessors` and `quantum_successors` return lists... I'm okey with any, but it should be the same :)
> This does not imply any inherent order in which the nodes are returned.

There are some order assumptions in the tests:
```
        successor_measure = self.dag.quantum_successors(
            self.dag.named_nodes('measure').pop())
        self.assertEqual(len(successor_measure), 1)
        cnot_node = successor_measure[0]
```
Maybe they should be fixed?
> quantum_predecessors and quantum_successors return lists.

I agree that consistency is needed! I'm not sure either way is better, as nodes will generally have up to 3 predecessors/successors the efficiency gain from iterators is, I assume, minimal. However, I do think that the order shouldn't matter, so perhaps a set would be better? The list is currently set-like, as if the node has 2 connections from a previous node (for example two `cx` nodes in a row), it only appears once in the list.

> There are some order assumptions in the tests:

I agree, I think these should be corrected.
I would also vote for iterators, and updating `quantum_predecessors`/`quantum_successors` to return iterators. Also let's make sure docstrings are correct.

Resolving patch:
diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py
--- a/qiskit/dagcircuit/dagcircuit.py
+++ b/qiskit/dagcircuit/dagcircuit.py
@@ -130,7 +130,7 @@ def _add_wire(self, wire):
         """Add a qubit or bit to the circuit.
 
         Args:
-            wire (tuple): (Register,int) containing a register instance and index
+            wire (Bit): the wire to be added
             This adds a pair of in and out nodes connected by an edge.
 
         Raises:
@@ -189,8 +189,8 @@ def _check_bits(self, args, amap):
         For each element of args, check that amap contains it.
 
         Args:
-            args (list): (register,idx) tuples
-            amap (dict): a dictionary keyed on bit objects
+            args (list[Bit]): the elements to be checked
+            amap (dict): a dictionary keyed on Qubits/Clbits
 
         Raises:
             DAGCircuitError: if a qubit is not contained in amap
@@ -217,8 +217,8 @@ def _add_op_node(self, op, qargs, cargs, condition=None):
 
         Args:
             op (Instruction): the operation associated with the DAG node
-            qargs (list): list of quantum wires to attach to.
-            cargs (list): list of classical wires to attach to.
+            qargs (list[Qubit]): list of quantum wires to attach to.
+            cargs (list[Clbit]): list of classical wires to attach to.
             condition (tuple or None): optional condition (ClassicalRegister, int)
         """
         node_properties = {
@@ -287,8 +287,8 @@ def apply_operation_front(self, op, qargs, cargs, condition=None):
 
         Args:
             op (Instruction): the operation associated with the DAG node
-            qargs (list[Qubit]): Qubits that op will be applied to
-            cargs (list[Clbit]): Clbits that op will be applied to
+            qargs (list[Qubit]): qubits that op will be applied to
+            cargs (list[Clbit]): cbits that op will be applied to
             condition (tuple or None): optional condition (ClassicalRegister, value)
 
         Returns:
@@ -329,7 +329,7 @@ def _check_edgemap_registers(self, edge_map, keyregs, valregs, valreg=True):
         it appears in both self and keyregs but not in edge_map.
 
         Args:
-            edge_map (dict): map from (reg,idx) in keyregs to (reg,idx) in valregs
+            edge_map (dict): map from Bit in keyregs to Bit in valregs
             keyregs (dict): a map from register names to Register objects
             valregs (dict): a map from register names to Register objects
             valreg (bool): if False the method ignores valregs and does not
@@ -379,8 +379,7 @@ def _check_wiremap_validity(self, wire_map, keymap, valmap):
         those wires have consistent types.
 
         Args:
-            wire_map (dict): map from (register,idx) in keymap to
-                (register,idx) in valmap
+            wire_map (dict): map from Bit in keymap to Bit in valmap
             keymap (dict): a map whose keys are wire_map keys
             valmap (dict): a map whose keys are wire_map values
 
@@ -403,7 +402,7 @@ def _map_condition(self, wire_map, condition):
 
         Args:
             wire_map (dict): a map from wires to wires
-            condition (tuple): (ClassicalRegister,int)
+            condition (tuple or None): (ClassicalRegister,int)
         Returns:
             tuple(ClassicalRegister,int): new condition
         """
@@ -624,7 +623,7 @@ def _check_wires_list(self, wires, node):
         Raise an exception otherwise.
 
         Args:
-            wires (list[register, index]): gives an order for (qu)bits
+            wires (list[Bit]): gives an order for (qu)bits
                 in the input circuit that is replacing the node.
             node (DAGNode): a node in the dag
 
@@ -727,7 +726,7 @@ def topological_op_nodes(self):
         Yield op nodes in topological order.
 
         Returns:
-            generator(DAGnode): op node in topological order
+            generator(DAGNode): op node in topological order
         """
         return (nd for nd in self.topological_nodes() if nd.type == 'op')
 
@@ -737,7 +736,7 @@ def substitute_node_with_dag(self, node, input_dag, wires=None):
         Args:
             node (DAGNode): node to substitute
             input_dag (DAGCircuit): circuit that will substitute the node
-            wires (list[(Register, index)]): gives an order for (qu)bits
+            wires (list[Bit]): gives an order for (qu)bits
                 in the input circuit. This order gets matched to the node wires
                 by qargs first, then cargs, then conditions.
 
@@ -907,7 +906,7 @@ def gate_nodes(self):
         """Get the list of gate nodes in the dag.
 
         Returns:
-            list: the list of node ids that represent gates.
+            list[DAGNode]: the list of DAGNodes that represent gates.
         """
         nodes = []
         for node in self.op_nodes():
@@ -944,22 +943,19 @@ def longest_path(self):
         return nx.dag_longest_path(self._multi_graph)
 
     def successors(self, node):
-        """Returns list of the successors of a node as DAGNodes."""
+        """Returns iterator of the successors of a node as DAGNodes."""
         return self._multi_graph.successors(node)
 
     def predecessors(self, node):
-        """Returns list of the predecessors of a node as DAGNodes."""
+        """Returns iterator of the predecessors of a node as DAGNodes."""
         return self._multi_graph.predecessors(node)
 
     def quantum_predecessors(self, node):
-        """Returns list of the predecessors of a node that are
+        """Returns iterator of the predecessors of a node that are
         connected by a quantum edge as DAGNodes."""
-
-        predecessors = []
         for predecessor in self.predecessors(node):
             if isinstance(self._multi_graph.get_edge_data(predecessor, node, key=0)['wire'], Qubit):
-                predecessors.append(predecessor)
-        return predecessors
+                yield predecessor
 
     def ancestors(self, node):
         """Returns set of the ancestors of a node as DAGNodes."""
@@ -977,14 +973,12 @@ def bfs_successors(self, node):
         return nx.bfs_successors(self._multi_graph, node)
 
     def quantum_successors(self, node):
-        """Returns list of the successors of a node that are
+        """Returns iterator of the successors of a node that are
         connected by a quantum edge as DAGNodes."""
-        successors = []
         for successor in self.successors(node):
             if isinstance(self._multi_graph.get_edge_data(
                     node, successor, key=0)['wire'], Qubit):
-                successors.append(successor)
-        return successors
+                yield successor
 
     def remove_op_node(self, node):
         """Remove an operation node n.
@@ -1198,7 +1192,7 @@ def nodes_on_wire(self, wire, only_ops=False):
         Iterator for nodes that affect a given wire
 
         Args:
-            wire (tuple(Register, index)): the wire to be looked at.
+            wire (Bit): the wire to be looked at.
             only_ops (bool): True if only the ops nodes are wanted
                         otherwise all nodes are returned.
         Yield:
diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py
--- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py
+++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py
@@ -35,7 +35,7 @@ def run(self, dag):
 
         nodes_to_remove = set()
         for measure in dag.op_nodes(Measure):
-            predecessor = dag.quantum_predecessors(measure)[0]
+            predecessor = next(dag.quantum_predecessors(measure))
 
             if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_1q_gates):
                 nodes_to_remove.add(predecessor)


Test patch:
diff --git a/test/python/test_dagcircuit.py b/test/python/test_dagcircuit.py
--- a/test/python/test_dagcircuit.py
+++ b/test/python/test_dagcircuit.py
@@ -279,15 +279,17 @@ def test_quantum_successors(self):
 
         successor_measure = self.dag.quantum_successors(
             self.dag.named_nodes('measure').pop())
-        self.assertEqual(len(successor_measure), 1)
-        cnot_node = successor_measure[0]
+        cnot_node = next(successor_measure)
+        with self.assertRaises(StopIteration):
+            next(successor_measure)
 
         self.assertIsInstance(cnot_node.op, CnotGate)
 
         successor_cnot = self.dag.quantum_successors(cnot_node)
-        self.assertEqual(len(successor_cnot), 2)
-        self.assertEqual(successor_cnot[0].type, 'out')
-        self.assertIsInstance(successor_cnot[1].op, Reset)
+        self.assertEqual(next(successor_cnot).type, 'out')
+        self.assertIsInstance(next(successor_cnot).op, Reset)
+        with self.assertRaises(StopIteration):
+            next(successor_cnot)
 
     def test_quantum_predecessors(self):
         """The method dag.quantum_predecessors() returns predecessors connected by quantum edges"""
@@ -297,15 +299,17 @@ def test_quantum_predecessors(self):
 
         predecessor_measure = self.dag.quantum_predecessors(
             self.dag.named_nodes('measure').pop())
-        self.assertEqual(len(predecessor_measure), 1)
-        cnot_node = predecessor_measure[0]
+        cnot_node = next(predecessor_measure)
+        with self.assertRaises(StopIteration):
+            next(predecessor_measure)
 
         self.assertIsInstance(cnot_node.op, CnotGate)
 
         predecessor_cnot = self.dag.quantum_predecessors(cnot_node)
-        self.assertEqual(len(predecessor_cnot), 2)
-        self.assertEqual(predecessor_cnot[1].type, 'in')
-        self.assertIsInstance(predecessor_cnot[0].op, Reset)
+        self.assertIsInstance(next(predecessor_cnot).op, Reset)
+        self.assertEqual(next(predecessor_cnot).type, 'in')
+        with self.assertRaises(StopIteration):
+            next(predecessor_cnot)
 
     def test_get_gates_nodes(self):
         """The method dag.gate_nodes() returns all gate nodes"""