Repository: Qiskit/qiskit

Issue:
A check_map analysis pass is needed
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

In https://github.com/Qiskit/qiskit-terra/pull/1270#issuecomment-439951585 @ajavadia raised the need to have a pass to check if a circuit is compatible with a coupling map. 


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
@@ -1108,6 +1108,16 @@ def get_named_nodes(self, name):
         return {node_id for node_id, data in self.multi_graph.nodes(data=True)
                 if data["type"] == "op" and data["name"] == name}
 
+    def get_cnot_nodes(self):
+        """Get the set of Cnot."""
+        cx_names = ['cx', 'CX']
+        cxs_nodes = []
+        for cx_name in cx_names:
+            if cx_name in self.basis:
+                for cx_id in self.get_named_nodes(cx_name):
+                    cxs_nodes.append(self.multi_graph.node[cx_id])
+        return cxs_nodes
+
     def _remove_op_node(self, n):
         """Remove an operation node n.
 
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
@@ -9,3 +9,4 @@
 
 from .cx_cancellation import CXCancellation
 from .fixed_point import FixedPoint
+from .check_map import CheckMap
diff --git a/qiskit/transpiler/passes/check_map.py b/qiskit/transpiler/passes/check_map.py
new file mode 100644
--- /dev/null
+++ b/qiskit/transpiler/passes/check_map.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+"""
+This pass checks if a DAG is mapped to a coupling map.
+"""
+
+from qiskit.transpiler._basepasses import AnalysisPass
+from qiskit.mapper import Layout
+
+
+class CheckMap(AnalysisPass):
+    """
+    Checks if a DAGCircuit is mapped to `coupling_map`.
+    """
+
+    def __init__(self, coupling_map, initial_layout=None):
+        """
+        Checks if a DAGCircuit is mapped to `coupling_map`.
+        Args:
+            coupling_map (Coupling): Directed graph represented a coupling map.
+            initial_layout (Layout): The initial layout of the DAG to analyze.
+        """
+        super().__init__()
+        self.layout = initial_layout
+        self.coupling_map = coupling_map
+
+    def run(self, dag):
+        """
+        If `dag` is mapped to coupling_map, the property `is_mapped` is
+        set to True (or to False otherwise).
+        Args:
+            dag (DAGCircuit): DAG to map.
+        """
+        if self.layout is None:
+            self.layout = Layout()
+            for qreg in dag.qregs.values():
+                self.layout.add_register(qreg)
+
+        self.property_set['is_mapped'] = None
+        for layer in dag.serial_layers():
+            subdag = layer['graph']
+
+            for a_cx in subdag.get_cnot_nodes():
+                physical_q0 = ('q', self.layout[a_cx['op'].qargs[0]])
+                physical_q1 = ('q', self.layout[a_cx['op'].qargs[1]])
+                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
+                    self.property_set['is_mapped'] = False
+                    return
+        self.property_set['is_mapped'] = True


Test patch:
diff --git a/test/python/transpiler/test_check_map.py b/test/python/transpiler/test_check_map.py
new file mode 100644
--- /dev/null
+++ b/test/python/transpiler/test_check_map.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2018, IBM.
+#
+# This source code is licensed under the Apache License, Version 2.0 found in
+# the LICENSE.txt file in the root directory of this source tree.
+
+"""Test the Check Map pass"""
+
+import unittest
+
+from qiskit import QuantumRegister, QuantumCircuit
+from qiskit.transpiler.passes import CheckMap
+from qiskit.mapper import Coupling
+from qiskit.dagcircuit import DAGCircuit
+from ..common import QiskitTestCase
+
+
+class TestCheckMap(QiskitTestCase):
+    """ Tests the CheckMap pass."""
+
+    def test_trivial_map(self):
+        """ Trivial map in a circuit without entanglement
+         qr0:---[H]---
+
+         qr1:---[H]---
+
+         qr2:---[H]---
+
+         Coupling map: None
+        """
+        qr = QuantumRegister(3, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.h(qr)
+        coupling = Coupling()
+        dag = DAGCircuit.fromQuantumCircuit(circuit)
+        pass_ = CheckMap(coupling)
+        pass_.run(dag)
+        self.assertTrue(pass_.property_set['is_mapped'])
+
+    def test_true_map(self):
+        """ Mapped is easy to check
+         qr0:--(+)-[H]-(+)-
+                |       |
+         qr1:---.-------|--
+                        |
+         qr2:-----------.--
+
+         Coupling map: [1]--[0]--[2]
+        """
+        qr = QuantumRegister(3, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.h(qr[0])
+        circuit.cx(qr[0], qr[2])
+        coupling = Coupling(couplingdict={0: [1, 2]})
+        dag = DAGCircuit.fromQuantumCircuit(circuit)
+
+        pass_ = CheckMap(coupling)
+        pass_.run(dag)
+
+        self.assertTrue(pass_.property_set['is_mapped'])
+
+    def test_true_map_in_same_layer(self):
+        """ Two CXs distance 1 to each other, in the same layer
+         qr0:--(+)--
+               |
+         qr1:---.---
+
+         qr2:--(+)--
+               |
+         qr3:---.---
+
+         Coupling map: [0]--[1]--[2]--[3]
+        """
+        qr = QuantumRegister(4, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[2], qr[3])
+        coupling = Coupling(couplingdict={0: [1], 1: [2], 2: [3]})
+        dag = DAGCircuit.fromQuantumCircuit(circuit)
+
+        pass_ = CheckMap(coupling)
+        pass_.run(dag)
+
+        self.assertTrue(pass_.property_set['is_mapped'])
+
+    def test_false_map(self):
+        """ Needs [0]-[1] in a [0]--[2]--[1]
+         qr0:--(+)--
+                |
+         qr1:---.---
+
+         Coupling map: [0]--[2]--[1]
+        """
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        coupling = Coupling(couplingdict={0: [2], 2: [1]})
+        dag = DAGCircuit.fromQuantumCircuit(circuit)
+
+        pass_ = CheckMap(coupling)
+        pass_.run(dag)
+
+        self.assertFalse(pass_.property_set['is_mapped'])
+
+
+if __name__ == '__main__':
+    unittest.main()