Repository: Qiskit/qiskit

Issue:
Can transpile a multi-qubit circuit on a 1Q device with cmap=None
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: latest
- **Python version**:
- **Operating system**:

### What is the current behavior?
```
qc = QuantumCircuit(2, 1)
qc.h(0)
qc.ry(0.11,1)
qc.measure([0], [0])
```
will be properly transpiled even if the backend has `n_qubits=1` and `coupling_map = None`



### Steps to reproduce the problem



### What is the expected behavior?



### Suggested solutions





Discussion:

Resolving patch:
diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py
--- a/qiskit/compiler/transpile.py
+++ b/qiskit/compiler/transpile.py
@@ -185,15 +185,21 @@ def callback_func(**kwargs):
     # Check circuit width against number of qubits in coupling_map(s)
     coupling_maps_list = list(config.coupling_map for config in transpile_configs)
     for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list):
-        # If coupling_map is not None
+        # If coupling_map is not None or n_qubits == 1
+        n_qubits = len(circuit.qubits)
+        max_qubits = None
         if isinstance(parsed_coupling_map, CouplingMap):
-            n_qubits = len(circuit.qubits)
             max_qubits = parsed_coupling_map.size()
-            if n_qubits > max_qubits:
-                raise TranspilerError('Number of qubits ({}) '.format(n_qubits) +
-                                      'in {} '.format(circuit.name) +
-                                      'is greater than maximum ({}) '.format(max_qubits) +
-                                      'in the coupling_map')
+
+        # If coupling_map is None, the limit might be in the backend (like in 1Q devices)
+        elif backend is not None:
+            max_qubits = backend.configuration().n_qubits
+
+        if max_qubits is not None and (n_qubits > max_qubits):
+            raise TranspilerError('Number of qubits ({}) '.format(n_qubits) +
+                                  'in {} '.format(circuit.name) +
+                                  'is greater than maximum ({}) '.format(max_qubits) +
+                                  'in the coupling_map')
     # Transpile circuits in parallel
     circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs)))
 


Test patch:
diff --git a/qiskit/test/mock/__init__.py b/qiskit/test/mock/__init__.py
--- a/qiskit/test/mock/__init__.py
+++ b/qiskit/test/mock/__init__.py
@@ -32,3 +32,4 @@
 from .fake_qasm_simulator import FakeQasmSimulator
 from .fake_openpulse_2q import FakeOpenPulse2Q
 from .fake_openpulse_3q import FakeOpenPulse3Q
+from .fake_1q import Fake1Q
diff --git a/qiskit/test/mock/fake_1q.py b/qiskit/test/mock/fake_1q.py
new file mode 100644
--- /dev/null
+++ b/qiskit/test/mock/fake_1q.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 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.
+
+"""
+Fake 1Q device (1 qubit).
+"""
+import datetime
+
+from qiskit.providers.models.backendproperties import BackendProperties, Gate, Nduv
+from qiskit.test.mock.fake_backend import FakeBackend
+
+
+class Fake1Q(FakeBackend):
+    """A fake 1Q backend."""
+
+    def __init__(self):
+        """
+          0
+        """
+        mock_time = datetime.datetime.now()
+        dt = 1.3333  # pylint: disable=invalid-name
+        configuration = BackendProperties(
+            backend_name='fake_1q',
+            backend_version='0.0.0',
+            n_qubits=1,
+            basis_gates=['u1', 'u2', 'u3', 'cx'],
+            simulator=False,
+            local=True,
+            conditional=False,
+            memory=False,
+            max_shots=1024,
+            qubits=[
+                [Nduv(date=mock_time, name='T1', unit='µs', value=71.9500421005539),
+                 Nduv(date=mock_time, name='frequency', unit='MHz', value=4919.96800692)]
+            ],
+            gates=[
+                Gate(gate='u1', name='u1_0', qubits=[0],
+                     parameters=[
+                         Nduv(date=mock_time, name='gate_error', unit='', value=1.0),
+                         Nduv(date=mock_time, name='gate_length', unit='ns', value=0.)]),
+                Gate(gate='u3', name='u3_0', qubits=[0],
+                     parameters=[
+                         Nduv(date=mock_time, name='gate_error', unit='', value=1.0),
+                         Nduv(date=mock_time, name='gate_length', unit='ns', value=2 * dt)]),
+                Gate(gate='u3', name='u3_1', qubits=[1],
+                     parameters=[
+                         Nduv(date=mock_time, name='gate_error', unit='', value=1.0),
+                         Nduv(date=mock_time, name='gate_length', unit='ns', value=4 * dt)]),
+                Gate(gate='cx', name='cx0_1', qubits=[0, 1],
+                     parameters=[
+                         Nduv(date=mock_time, name='gate_error', unit='', value=1.0),
+                         Nduv(date=mock_time, name='gate_length', unit='ns', value=22 * dt)]),
+            ],
+            coupling_map=None,
+            n_registers=1,
+            last_update_date=mock_time,
+            general=[]
+        )
+        super().__init__(configuration)
+
+    def defaults(self):
+        """ defaults == configuration """
+        return self._configuration
+
+    def properties(self):
+        """ properties == configuration """
+        return self._configuration
diff --git a/test/python/transpiler/test_1q.py b/test/python/transpiler/test_1q.py
new file mode 100644
--- /dev/null
+++ b/test/python/transpiler/test_1q.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 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.
+
+"""Tests preset pass managers with 1Q backend"""
+from test import combine
+from ddt import ddt
+
+from qiskit import QuantumCircuit
+from qiskit.compiler import transpile
+from qiskit.test import QiskitTestCase
+from qiskit.test.mock import Fake1Q
+from qiskit.transpiler import TranspilerError
+
+
+def emptycircuit():
+    """Empty circuit"""
+    return QuantumCircuit()
+
+
+def circuit_3516():
+    """Circuit from https://github.com/Qiskit/qiskit-terra/issues/3516 should fail"""
+    circuit = QuantumCircuit(2, 1)
+    circuit.h(0)
+    circuit.ry(0.11, 1)
+    circuit.measure([0], [0])
+    return circuit
+
+
+@ddt
+class Test1QFailing(QiskitTestCase):
+    """1Q tests that should fail."""
+
+    @combine(circuit=[circuit_3516],
+             level=[0, 1, 2, 3],
+             dsc='Transpiling {circuit.__name__} at level {level} should fail',
+             name='{circuit.__name__}_level{level}_fail')
+    def test(self, circuit, level):
+        """All the levels with all the 1Q backend"""
+        with self.assertRaises(TranspilerError):
+            transpile(circuit(), backend=Fake1Q(), optimization_level=level, seed_transpiler=42)
+
+
+@ddt
+class Test1QWorking(QiskitTestCase):
+    """1Q tests that should work."""
+
+    @combine(circuit=[emptycircuit],
+             level=[0, 1, 2, 3],
+             dsc='Transpiling {circuit.__name__} at level {level} should work',
+             name='{circuit.__name__}_level{level}_valid')
+    def test(self, circuit, level):
+        """All the levels with all the 1Q backend"""
+        result = transpile(circuit(), backend=Fake1Q(), optimization_level=level,
+                           seed_transpiler=42)
+        self.assertIsInstance(result, QuantumCircuit)