Repository: Qiskit/qiskit

Issue:
circuit.measure_all() and circuit.remove_final_measures()
It is a very common occurrence that a user wants to get the ideal statevector for a circuit, but also send it to a device to get counts. I'm seeing a lot of functions around (Ignis for example) that generate instances of `qc` and `qc_m` for these cases.

I think the QuantumCircuit class should have a simple method (`circuit.measure_all()`) that measures all qubits. (This would add a new ClassicalRegister to the circuit).

We probably also want a `measure_active()` to measure only non-idle qubits, because in some cases we don't want all the extra 0s that get measured.

In addition we may also consider a `circuit.remove_final_measurements()` that does the opposite.


Discussion:
If `circuit.measure_all()` would be adding a new ClassicalRegister to the circuit, would `circuit.remove_final_measurements()` remove the ClassicalRegister? If yes, would `circuit.remove_final_measurements()` be allowed to be called without first calling `circuit.measure_all()`?

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -852,6 +852,69 @@ def copy(self, name=None):
             cpy.name = name
         return cpy
 
+    def _create_creg(self, length, name):
+        """ Creates a creg, checking if ClassicalRegister with same name exists
+        """
+        if name in [creg.name for creg in self.cregs]:
+            save_prefix = ClassicalRegister.prefix
+            ClassicalRegister.prefix = name
+            new_creg = ClassicalRegister(length)
+            ClassicalRegister.prefix = save_prefix
+        else:
+            new_creg = ClassicalRegister(length, name)
+        return new_creg
+
+    def measure_active(self):
+        """Adds measurement to all non-idle qubits. Creates a new ClassicalRegister with
+        a size equal to the number of non-idle qubits being measured.
+        """
+        from qiskit.converters.circuit_to_dag import circuit_to_dag
+        dag = circuit_to_dag(self)
+        qubits_to_measure = [qubit for qubit in self.qubits if qubit not in dag.idle_wires()]
+        new_creg = self._create_creg(len(qubits_to_measure), 'measure')
+        self.add_register(new_creg)
+        self.barrier()
+        self.measure(qubits_to_measure, new_creg)
+
+    def measure_all(self):
+        """Adds measurement to all qubits. Creates a new ClassicalRegister with a
+        size equal to the number of qubits being measured.
+        """
+        new_creg = self._create_creg(len(self.qubits), 'measure')
+        self.add_register(new_creg)
+        self.barrier()
+        self.measure(self.qubits, new_creg)
+
+    def remove_final_measurements(self):
+        """Removes final measurement on all qubits if they are present.
+        Deletes the ClassicalRegister that was used to store the values from these measurements
+        if it is idle.
+        """
+        # pylint: disable=cyclic-import
+        from qiskit.transpiler.passes import RemoveFinalMeasurements
+        from qiskit.converters import circuit_to_dag
+        dag = circuit_to_dag(self)
+        remove_final_meas = RemoveFinalMeasurements()
+        new_dag = remove_final_meas.run(dag)
+
+        # Set self's cregs and instructions to match the new DAGCircuit's
+        self.data.clear()
+        self.cregs = list(new_dag.cregs.values())
+
+        for node in new_dag.topological_op_nodes():
+            qubits = []
+            for qubit in node.qargs:
+                qubits.append(new_dag.qregs[qubit.register.name][qubit.index])
+
+            clbits = []
+            for clbit in node.cargs:
+                clbits.append(new_dag.cregs[clbit.register.name][clbit.index])
+
+            # Get arguments for classical condition (if any)
+            inst = node.op.copy()
+            inst.condition = node.condition
+            self.append(inst, qubits, clbits)
+
     @staticmethod
     def from_qasm_file(path):
         """Take in a QASM file and generate a QuantumCircuit object.
diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py
--- a/qiskit/transpiler/passes/__init__.py
+++ b/qiskit/transpiler/passes/__init__.py
@@ -51,3 +51,4 @@
 from .mapping.lookahead_swap import LookaheadSwap
 from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure
 from .mapping.stochastic_swap import StochasticSwap
+from .remove_final_measurements import RemoveFinalMeasurements
diff --git a/qiskit/transpiler/passes/remove_final_measurements.py b/qiskit/transpiler/passes/remove_final_measurements.py
new file mode 100644
--- /dev/null
+++ b/qiskit/transpiler/passes/remove_final_measurements.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2017, 2019.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+
+"""
+This pass removes final barriers and final measurements, as well as the
+ClassicalRegisters they are connected to if the ClassicalRegister
+is unused. Measurements and barriers are considered final if they are
+followed by no other operations (aside from other measurements or barriers.)
+"""
+
+from qiskit.transpiler.basepasses import TransformationPass
+from qiskit.dagcircuit import DAGCircuit
+
+
+class RemoveFinalMeasurements(TransformationPass):
+    """Removes final measurements and barriers at the end of a circuit."""
+
+    def run(self, dag):
+        """Return a circuit with final measurements and barriers removed."""
+
+        final_op_types = ['measure', 'barrier']
+        final_ops = []
+        cregs_to_remove = dict()
+        clbits_with_final_measures = set()
+
+        for candidate_node in dag.named_nodes(*final_op_types):
+            is_final_op = True
+
+            for _, child_successors in dag.bfs_successors(candidate_node):
+                if any(suc.type == 'op' and suc.name not in final_op_types
+                       for suc in child_successors):
+                    is_final_op = False
+                    break
+
+            if is_final_op:
+                final_ops.append(candidate_node)
+
+        if not final_ops:
+            return dag
+
+        new_dag = DAGCircuit()
+
+        for node in final_ops:
+            for carg in node.cargs:
+                # Add the clbit that was attached to the measure we are removing
+                clbits_with_final_measures.add(carg)
+            dag.remove_op_node(node)
+
+        # If the clbit is idle, add its register to list of registers we may remove
+        for clbit in clbits_with_final_measures:
+            if clbit in dag.idle_wires():
+                if clbit.register in cregs_to_remove:
+                    cregs_to_remove[clbit.register] += 1
+                else:
+                    cregs_to_remove[clbit.register] = 1
+
+        # Remove creg if all of its clbits were added above
+        for key, val in zip(list(dag.cregs.keys()), list(dag.cregs.values())):
+            if val in cregs_to_remove and cregs_to_remove[val] == val.size:
+                del dag.cregs[key]
+
+        # Fill new DAGCircuit
+        for qreg in dag.qregs.values():
+            new_dag.add_qreg(qreg)
+        for creg in dag.cregs.values():
+            new_dag.add_creg(creg)
+
+        for node in dag.topological_op_nodes():
+            # copy the condition over too
+            if node.condition:
+                new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs,
+                                             condition=node.condition)
+            else:
+                new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs)
+
+        return new_dag


Test patch:
diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py
--- a/test/python/circuit/test_circuit_operations.py
+++ b/test/python/circuit/test_circuit_operations.py
@@ -158,6 +158,113 @@ def test_copy_circuit(self):
 
         self.assertEqual(qc, qc.copy())
 
+    def test_measure_active(self):
+        """Test measure_active
+        Applies measurements only to non-idle qubits. Creates a ClassicalRegister of size equal to
+        the amount of non-idle qubits to store the measured values.
+        """
+        qr = QuantumRegister(4)
+        cr = ClassicalRegister(2, 'measure')
+
+        circuit = QuantumCircuit(qr)
+        circuit.h(qr[0])
+        circuit.h(qr[2])
+        circuit.measure_active()
+
+        expected = QuantumCircuit(qr)
+        expected.h(qr[0])
+        expected.h(qr[2])
+        expected.add_register(cr)
+        expected.barrier()
+        expected.measure([qr[0], qr[2]], [cr[0], cr[1]])
+
+        self.assertEqual(expected, circuit)
+
+    def test_measure_active_repetition(self):
+        """Test measure_active in a circuit with a 'measure' creg.
+        measure_active should be aware that the creg 'measure' might exists.
+        """
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2, 'measure')
+
+        circuit = QuantumCircuit(qr, cr)
+        circuit.h(qr)
+        circuit.measure_active()
+
+        self.assertEqual(len(circuit.cregs), 2)  # Two cregs
+        self.assertEqual(len(circuit.cregs[0]), 2)  # Both length 2
+        self.assertEqual(len(circuit.cregs[1]), 2)
+
+    def test_measure_all(self):
+        """Test measure_all applies measurements to all qubits.
+        Creates a ClassicalRegister of size equal to the total amount of qubits to
+        store those measured values.
+        """
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2, 'measure')
+
+        circuit = QuantumCircuit(qr)
+        circuit.measure_all()
+
+        expected = QuantumCircuit(qr, cr)
+        expected.barrier()
+        expected.measure(qr, cr)
+
+        self.assertEqual(expected, circuit)
+
+    def test_measure_all_repetition(self):
+        """Test measure_all in a circuit with a 'measure' creg.
+        measure_all should be aware that the creg 'measure' might exists.
+        """
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2, 'measure')
+
+        circuit = QuantumCircuit(qr, cr)
+        circuit.measure_all()
+
+        self.assertEqual(len(circuit.cregs), 2)  # Two cregs
+        self.assertEqual(len(circuit.cregs[0]), 2)  # Both length 2
+        self.assertEqual(len(circuit.cregs[1]), 2)
+
+    def test_remove_final_measurements(self):
+        """Test remove_final_measurements
+        Removes all measurements at end of circuit.
+        """
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2, 'measure')
+
+        circuit = QuantumCircuit(qr, cr)
+        circuit.measure(qr, cr)
+        circuit.remove_final_measurements()
+
+        expected = QuantumCircuit(qr)
+
+        self.assertEqual(expected, circuit)
+
+    def test_remove_final_measurements_multiple_measures(self):
+        """Test remove_final_measurements only removes measurements at the end of the circuit
+        remove_final_measurements should not remove measurements in the beginning or middle of the
+        circuit.
+        """
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(1)
+
+        circuit = QuantumCircuit(qr, cr)
+        circuit.measure(qr[0], cr)
+        circuit.h(0)
+        circuit.measure(qr[0], cr)
+        circuit.h(0)
+        circuit.measure(qr[0], cr)
+        circuit.remove_final_measurements()
+
+        expected = QuantumCircuit(qr, cr)
+        expected.measure(qr[0], cr)
+        expected.h(0)
+        expected.measure(qr[0], cr)
+        expected.h(0)
+
+        self.assertEqual(expected, circuit)
+
     def test_mirror(self):
         """Test mirror method reverses but does not invert."""
         qc = QuantumCircuit(2, 2)