Repository: Qiskit/qiskit

Issue:
DAGCircuit node_counter variable doesn't update with transpiler passes
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit Terra version**: 0.7
- **Python version**: 3.6
- **Operating system**: Ubuntu 16

### What is the current behavior?
When executing the CX cancellation pass in transpiler, the actual number of nodes changed, but the variable in DAGCircuit called "node_counter" which tracks the number of nodes doesn't change with it.

### Steps to reproduce the problem
The following code can reproduce the error:

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler import PassManager, transpile_dag
from qiskit.transpiler.passes import CXCancellation

q = QuantumRegister(10)
q.cx(q[0], q[1])

pm = PassManager()
pm.add_passes(CXCancellation())

\# Number of nodes before
print(qdag.node_counter)
qdag = DAGCircuit.fromQuantumCircuit(q)
qdag = transpile_dag(qdag, pass_manager = pm)
\# Number of nodes after 
print(qdag.node_counter)

### What is the expected behavior?
The two printed out number to be different.

### Suggested solutions
Remove the variable "node_counter" in DAGCircuit class




Discussion:

Resolving patch:
diff --git a/qiskit/dagcircuit/_dagcircuit.py b/qiskit/dagcircuit/_dagcircuit.py
--- a/qiskit/dagcircuit/_dagcircuit.py
+++ b/qiskit/dagcircuit/_dagcircuit.py
@@ -55,8 +55,8 @@ def __init__(self):
         # Map from wire (Register,idx) to output nodes of the graph
         self.output_map = OrderedDict()
 
-        # Running count of the total number of nodes
-        self.node_counter = 0
+        # Stores the max id of a node added to the DAG
+        self._max_node_id = 0
 
         # Directed multigraph whose nodes are inputs, outputs, or operations.
         # Operation nodes have equal in- and out-degrees and carry
@@ -93,6 +93,14 @@ def clbits(self):
         """Return a list of bits as (ClassicalRegister, index) pairs."""
         return [(v, i) for k, v in self.cregs.items() for i in range(v.size)]
 
+    @property
+    def node_counter(self):
+        """Deprecated usage to return max node id, now returns size of DAG"""
+        warnings.warn('Usage of node_counter to return the maximum node id is deprecated,'
+                      ' it now returns the number of nodes in the current DAG',
+                      DeprecationWarning, 2)
+        return len(self.multi_graph)
+
     # TODO: unused function. is it needed?
     def rename_register(self, regname, newname):
         """Rename a classical or quantum register throughout the circuit.
@@ -179,10 +187,10 @@ def _add_wire(self, wire):
         """
         if wire not in self.wires:
             self.wires.append(wire)
-            self.node_counter += 1
-            self.input_map[wire] = self.node_counter
-            self.node_counter += 1
-            self.output_map[wire] = self.node_counter
+            self._max_node_id += 1
+            self.input_map[wire] = self._max_node_id
+            self._max_node_id += 1
+            self.output_map[wire] = self._max_node_id
             in_node = self.input_map[wire]
             out_node = self.output_map[wire]
             self.multi_graph.add_edge(in_node, out_node)
@@ -252,18 +260,18 @@ def _add_op_node(self, op, qargs, cargs, condition=None):
             condition (tuple or None): optional condition (ClassicalRegister, int)
         """
         # Add a new operation node to the graph
-        self.node_counter += 1
-        self.multi_graph.add_node(self.node_counter)
+        self._max_node_id += 1
+        self.multi_graph.add_node(self._max_node_id)
         # Update the operation itself. TODO: remove after qargs not connected to op
         op.qargs = qargs
         op.cargs = cargs
         # Update that operation node's data
-        self.multi_graph.node[self.node_counter]["type"] = "op"
-        self.multi_graph.node[self.node_counter]["op"] = op
-        self.multi_graph.node[self.node_counter]["name"] = op.name
-        self.multi_graph.node[self.node_counter]["qargs"] = qargs
-        self.multi_graph.node[self.node_counter]["cargs"] = cargs
-        self.multi_graph.node[self.node_counter]["condition"] = condition
+        self.multi_graph.node[self._max_node_id]["type"] = "op"
+        self.multi_graph.node[self._max_node_id]["op"] = op
+        self.multi_graph.node[self._max_node_id]["name"] = op.name
+        self.multi_graph.node[self._max_node_id]["qargs"] = qargs
+        self.multi_graph.node[self._max_node_id]["cargs"] = cargs
+        self.multi_graph.node[self._max_node_id]["condition"] = condition
 
     def apply_operation_back(self, op, qargs=None, cargs=None, condition=None):
         """Apply an operation to the output of the circuit.
@@ -275,8 +283,12 @@ def apply_operation_back(self, op, qargs=None, cargs=None, condition=None):
             cargs (list[tuple]): cbits that op will be applied to
             condition (tuple or None): optional condition (ClassicalRegister, int)
 
+        Returns:
+            int: the current max node id
+
         Raises:
             DAGCircuitError: if a leaf node is connected to multiple outputs
+
         """
         qargs = qargs or op.qargs
         cargs = cargs or op.cargs
@@ -298,12 +310,14 @@ def apply_operation_back(self, op, qargs=None, cargs=None, condition=None):
             if len(ie) != 1:
                 raise DAGCircuitError("output node has multiple in-edges")
 
-            self.multi_graph.add_edge(ie[0], self.node_counter,
+            self.multi_graph.add_edge(ie[0], self._max_node_id,
                                       name="%s[%s]" % (q[0].name, q[1]), wire=q)
             self.multi_graph.remove_edge(ie[0], self.output_map[q])
-            self.multi_graph.add_edge(self.node_counter, self.output_map[q],
+            self.multi_graph.add_edge(self._max_node_id, self.output_map[q],
                                       name="%s[%s]" % (q[0].name, q[1]), wire=q)
 
+        return self._max_node_id
+
     def apply_operation_front(self, op, qargs=None, cargs=None, condition=None):
         """Apply an operation to the input of the circuit.
         TODO: make `qargs` and `cargs` mandatory, when they are dropped from op.
@@ -314,6 +328,9 @@ def apply_operation_front(self, op, qargs=None, cargs=None, condition=None):
             cargs (list[tuple]): cbits that op will be applied to
             condition (tuple or None): optional condition (ClassicalRegister, value)
 
+        Returns:
+            int: the current max node id
+
         Raises:
             DAGCircuitError: if initial nodes connected to multiple out edges
         """
@@ -334,12 +351,14 @@ def apply_operation_front(self, op, qargs=None, cargs=None, condition=None):
             ie = list(self.multi_graph.successors(self.input_map[q]))
             if len(ie) != 1:
                 raise DAGCircuitError("input node has multiple out-edges")
-            self.multi_graph.add_edge(self.node_counter, ie[0],
+            self.multi_graph.add_edge(self._max_node_id, ie[0],
                                       name="%s[%s]" % (q[0].name, q[1]), wire=q)
             self.multi_graph.remove_edge(self.input_map[q], ie[0])
-            self.multi_graph.add_edge(self.input_map[q], self.node_counter,
+            self.multi_graph.add_edge(self.input_map[q], self._max_node_id,
                                       name="%s[%s]" % (q[0].name, q[1]), wire=q)
 
+        return self._max_node_id
+
     def _check_edgemap_registers(self, edge_map, keyregs, valregs, valreg=True):
         """Check that wiremap neither fragments nor leaves duplicate registers.
 
@@ -816,10 +835,10 @@ def substitute_circuit_all(self, op, input_circuit, wires=None):
                             al = [m_qargs, all_cbits]
                             for q in itertools.chain(*al):
                                 self.multi_graph.add_edge(full_pred_map[q],
-                                                          self.node_counter,
+                                                          self._max_node_id,
                                                           name="%s[%s]" % (q[0].name, q[1]),
                                                           wire=q)
-                                full_pred_map[q] = copy.copy(self.node_counter)
+                                full_pred_map[q] = copy.copy(self._max_node_id)
                     # Connect all predecessors and successors, and remove
                     # residual edges between input and output nodes
                     for w in full_pred_map:
@@ -933,10 +952,10 @@ def substitute_node_with_dag(self, node, input_dag, wires=None):
                 al = [m_qargs, all_cbits]
                 for q in itertools.chain(*al):
                     self.multi_graph.add_edge(full_pred_map[q],
-                                              self.node_counter,
+                                              self._max_node_id,
                                               name="%s[%s]" % (q[0].name, q[1]),
                                               wire=q)
-                    full_pred_map[q] = copy.copy(self.node_counter)
+                    full_pred_map[q] = copy.copy(self._max_node_id)
         # Connect all predecessors and successors, and remove
         # residual edges between input and output nodes
         for w in full_pred_map:
diff --git a/qiskit/transpiler/passes/mapping/barrier_before_final_measurements.py b/qiskit/transpiler/passes/mapping/barrier_before_final_measurements.py
--- a/qiskit/transpiler/passes/mapping/barrier_before_final_measurements.py
+++ b/qiskit/transpiler/passes/mapping/barrier_before_final_measurements.py
@@ -53,8 +53,7 @@ def run(self, dag):
         final_qubits = set(dag.multi_graph.node[final_op]['qargs'][0]
                            for final_op in final_ops)
 
-        barrier_layer.apply_operation_back(Barrier(qubits=final_qubits))
-        new_barrier_id = barrier_layer.node_counter
+        new_barrier_id = barrier_layer.apply_operation_back(Barrier(qubits=final_qubits))
 
         # Preserve order of final ops collected earlier from the original DAG.
         ordered_node_ids = [node_id for node_id in dag.node_nums_in_topological_order()
@@ -88,9 +87,8 @@ def run(self, dag):
                     and our_descendants.isdisjoint(their_ancestors)
             ):
                 merge_barrier = Barrier(qubits=(our_qubits | their_qubits))
-                barrier_layer.apply_operation_front(merge_barrier)
+                merge_barrier_id = barrier_layer.apply_operation_front(merge_barrier)
 
-                merge_barrier_id = barrier_layer.node_counter
                 our_ancestors = our_ancestors | their_ancestors
                 our_descendants = our_descendants | their_descendants
 


Test patch: