Repository: Qiskit/qiskit

Issue:
BasicAer qasm_simulator not detecting that sample measurement can be used
<!-- ⚠️ 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.0
- **Python version**: 3.6.6
- **Operating system**: Ubuntu

### What is the current behavior?

Currently, it can detect that it can sample if there is only measurments at the end, but sometimes the measurment instruction is put further up, and it's not detected even when there is no more operations applied to the qubit.

### Steps to reproduce the problem

If you look at what gets called with the following:

```python
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, BasicAer

qubit_reg = QuantumRegister(4)
clbit_reg = ClassicalRegister(4)

qc1 = QuantumCircuit(qubit_reg, clbit_reg)
qc1.h(qubit_reg[0])
qc1.cx(qubit_reg[0], qubit_reg[1])
qc1.measure(qubit_reg, clbit_reg)

job_sim = execute(qc1, BasicAer.get_backend('qasm_simulator'))
sim_result = job_sim.result()
```

The function `_add_sample_measure` is never called. 

### What is the expected behavior?

It should just sample measure, which would be quicker.

### Suggested solutions

Change the `_validate_measure_sampling` function in `qiskit/providers/builtinsimulators/qasm_simulator.py` to be something like:

```python
    def _validate_measure_sampling(self, experiment):
        """Determine if sampling can be used for an experiment

        Args:
            experiment (QobjExperiment): a qobj experiment
        """
        measure_flags = {}
        if hasattr(experiment.config, 'allows_measure_sampling'):
            self._sample_measure = experiment.config.allows_measure_sampling
        else:

            for instruction in experiment.instructions:
                if instruction.name == "reset":
                    measure_flags[instruction.qubits[0]] = False
                    self._sample_measure = False
                    return

                if measure_flags.get(instruction.qubits[0], False):
                    if instruction.name not in ["measure", "barrier", "id", "u0"]:
                        for qubit in instruction.qubits:
                            measure_flags[qubit] = False
                            return
                elif instruction.name == "measure":
                     for qubit in instruction.qubits:
                            measure_flags[qubit] = True
        
        self._sample_measure = True

        for key, value in measure_flags.items():
            if value == False:
                self._sample_measure = False
                return
```


Discussion:
I can make a pull request if needed.
This has been discussed in #666 previously. The question is whether to implement this in the simulator (as you have done here), or whether to have the intelligence in the transpiler. The consensus seems to have been the latter.

It is natural for the transpiler to figure this out and either a) set a `allows_measure_sampling` flag, or b) by construction put the final measurements at the end of the generated qobj. I think @kdk is working on this.

Resolving patch:
diff --git a/qiskit/transpiler/_transpiler.py b/qiskit/transpiler/_transpiler.py
--- a/qiskit/transpiler/_transpiler.py
+++ b/qiskit/transpiler/_transpiler.py
@@ -187,8 +187,11 @@ def transpile_dag(dag, basis_gates='u1,u2,u3,cx,id', coupling_map=None,
         # default set of passes
         # TODO: move each step here to a pass, and use a default passmanager below
         basis = basis_gates.split(',') if basis_gates else []
-        dag = Unroller(basis).run(dag)
         name = dag.name
+
+        dag = Unroller(basis).run(dag)
+        dag = BarrierBeforeFinalMeasurements().run(dag)
+
         # if a coupling map is given compile to the map
         if coupling_map:
             logger.info("pre-mapping properties: %s",
@@ -196,7 +199,7 @@ def transpile_dag(dag, basis_gates='u1,u2,u3,cx,id', coupling_map=None,
             # Insert swap gates
             coupling = CouplingMap(coupling_map)
             logger.info("initial layout: %s", initial_layout)
-            dag = BarrierBeforeFinalMeasurements().run(dag)
+
             dag, final_layout = swap_mapper(
                 dag, coupling, initial_layout, trials=20, seed=seed_mapper)
             logger.info("final layout: %s", final_layout)
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
@@ -23,7 +23,7 @@ def run(self, dag):
         last_measures = []
         for measure in dag.get_named_nodes('measure'):
             is_last_measurement = all([after_measure in dag.output_map.values() for after_measure in
-                                       dag.quantum_successors(measure)])
+                                       dag.successors(measure)])
             if is_last_measurement:
                 last_measures.append(measure)
 


Test patch:
diff --git a/test/python/test_mapper.py b/test/python/test_mapper.py
--- a/test/python/test_mapper.py
+++ b/test/python/test_mapper.py
@@ -8,12 +8,14 @@
 # pylint: disable=missing-docstring,redefined-builtin
 
 import unittest
+from unittest.mock import patch
 
 from qiskit import compile, execute
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit import BasicAer
 from qiskit.qobj import Qobj
 from qiskit.transpiler._transpiler import transpile_dag
+from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
 from qiskit.mapper._compiling import two_qubit_kak
 from qiskit.tools.qi.qi import random_unitary_matrix
 from qiskit.mapper._mapping import MapperError
@@ -228,6 +230,27 @@ def test_kak_decomposition(self):
                 except MapperError as ex:
                     self.fail(str(ex))
 
+    barrier_pass = BarrierBeforeFinalMeasurements()
+
+    @patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=barrier_pass.run)
+    def test_final_measurement_barrier_for_devices(self, mock_pass):
+        """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices."""
+
+        circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS))
+        dag_circuit = circuit_to_dag(circ)
+        transpile_dag(dag_circuit, coupling_map=FakeRueschlikon().configuration().coupling_map)
+
+        self.assertTrue(mock_pass.called)
+
+    @patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=barrier_pass.run)
+    def test_final_measurement_barrier_for_simulators(self, mock_pass):
+        """Verify BarrierBeforeFinalMeasurements pass is in default pipeline for simulators."""
+        circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS))
+        dag_circuit = circuit_to_dag(circ)
+        transpile_dag(dag_circuit)
+
+        self.assertTrue(mock_pass.called)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/python/transpiler/test_add_barrier_before_measuremets.py b/test/python/transpiler/test_add_barrier_before_measuremets.py
--- a/test/python/transpiler/test_add_barrier_before_measuremets.py
+++ b/test/python/transpiler/test_add_barrier_before_measuremets.py
@@ -125,22 +125,57 @@ def test_two_qregs_to_a_single_creg(self):
                     |                      |   |
          q1:--------|--[m]--  -> q1:-------|---|--[m]--
                     |   |                  |   |   |
-         c0:--------.---.---      c0:----------.---.---
+         c0:--------.---|---     c0:-----------.---|---
+            ------------.---        ---------------.---
         """
         qr0 = QuantumRegister(1, 'q0')
         qr1 = QuantumRegister(1, 'q1')
-        cr0 = ClassicalRegister(1, 'c0')
+        cr0 = ClassicalRegister(2, 'c0')
 
         circuit = QuantumCircuit(qr0, qr1, cr0)
         circuit.h(qr0)
-        circuit.measure(qr0, cr0)
-        circuit.measure(qr1, cr0)
+        circuit.measure(qr0, cr0[0])
+        circuit.measure(qr1, cr0[1])
 
         expected = QuantumCircuit(qr0, qr1, cr0)
         expected.h(qr0)
         expected.barrier(qr0, qr1)
+        expected.measure(qr0, cr0[0])
+        expected.measure(qr1, cr0[1])
+
+        pass_ = BarrierBeforeFinalMeasurements()
+        result = pass_.run(circuit_to_dag(circuit))
+
+        self.assertEqual(result, circuit_to_dag(expected))
+
+    def test_preserve_measure_for_conditional(self):
+        """ Test barrier is inserted after any measurements used for conditionals
+
+         q0:--[H]--[m]------------     q0:--[H]--[m]-------|-------
+                    |                             |        |
+         q1:--------|--[ z]--[m]--  -> q1:--------|--[ z]--|--[m]--
+                    |    |    |                   |    |       |
+         c0:--------.--[=1]---|---     c0:--------.--[=1]------|---
+                              |                                |
+         c1:------------------.---     c1:---------------------.---
+        """
+        qr0 = QuantumRegister(1, 'q0')
+        qr1 = QuantumRegister(1, 'q1')
+        cr0 = ClassicalRegister(1, 'c0')
+        cr1 = ClassicalRegister(1, 'c1')
+        circuit = QuantumCircuit(qr0, qr1, cr0, cr1)
+
+        circuit.h(qr0)
+        circuit.measure(qr0, cr0)
+        circuit.z(qr1).c_if(cr0, 1)
+        circuit.measure(qr1, cr1)
+
+        expected = QuantumCircuit(qr0, qr1, cr0, cr1)
+        expected.h(qr0)
         expected.measure(qr0, cr0)
-        expected.measure(qr1, cr0)
+        expected.z(qr1).c_if(cr0, 1)
+        expected.barrier(qr1)
+        expected.measure(qr1, cr1)
 
         pass_ = BarrierBeforeFinalMeasurements()
         result = pass_.run(circuit_to_dag(circuit))
@@ -181,22 +216,23 @@ def test_remove_barrier_in_different_qregs(self):
                   |                  |   |
          q1:--|---|--[m]--  -> q1:---|---|--[m]--
                   |   |              |   |   |
-         c0:------.---.---     c0:-------.---.---
+         c0:------.---|---     c0:-------.---|---
+            ----------.---        -----------.---
         """
         qr0 = QuantumRegister(1, 'q0')
         qr1 = QuantumRegister(1, 'q1')
-        cr0 = ClassicalRegister(1, 'c0')
+        cr0 = ClassicalRegister(2, 'c0')
 
         circuit = QuantumCircuit(qr0, qr1, cr0)
         circuit.barrier(qr0)
         circuit.barrier(qr1)
-        circuit.measure(qr0, cr0)
-        circuit.measure(qr1, cr0)
+        circuit.measure(qr0, cr0[0])
+        circuit.measure(qr1, cr0[1])
 
         expected = QuantumCircuit(qr0, qr1, cr0)
         expected.barrier(qr0, qr1)
-        expected.measure(qr0, cr0)
-        expected.measure(qr1, cr0)
+        expected.measure(qr0, cr0[0])
+        expected.measure(qr1, cr0[1])
 
         pass_ = BarrierBeforeFinalMeasurements()
         result = pass_.run(circuit_to_dag(circuit))