Repository: Qiskit/qiskit

Issue:
what are the restrictions for initial_layout? 
Getting deeper in https://github.com/Qiskit/qiskit-terra/issues/4757 a conversation about "valid" `initial_layout`s popped up.

There is a need to answer two questions:
 - What are the restrictions to set `initial_layout`?
 - Which component should validate `initial_layout`?

The example can be is the following, where `initial_layout` is *bigger* than the circuit:
```python
from qiskit import QuantumCircuit, transpile
from qiskit.test.mock import FakeTenerife

circuit = QuantumCircuit(3)
circuit.h(1)
transpiled = transpile(circuit, backend=FakeTenerife(), initial_layout=list(range(5)))
```

Should this be accepted?

If yes, why does layout (wrongly) [validate the size of the layout](https://github.com/Qiskit/qiskit-terra/blob/10ac1ecd2aa9b7a25e9735ff30339650d0e9b040/qiskit/transpiler/layout.py#L291) in the `from_intlist` static method?

If no, why does [`Layout` accept physical qubits pointing to `None` ](https://github.com/Qiskit/qiskit-terra/blob/10ac1ecd2aa9b7a25e9735ff30339650d0e9b040/test/python/transpiler/test_layout.py#L341)?

The intermediate solution is allow any `Layout` and `transpile` should do the check before creating the layout.


Discussion:
The question I have is: 
* Why `initial_layout` is not a `Layout` object?

This would solve this problem, which seems to originate from a miscommunication of what is a `initial_layout`. A `Layout` is a two-way mapping that can be created from a `dict` or a `list`. Actually, there are two methods to create it from a `list`:

1. The list represents the mapping `virtual qubits -> physical qubits` (virtual qubits as indexes, physical qubits as elements)
2. The list represents the mapping  `physical qubits -> virtual qubits` (physical qubits as indexes, virtual qubits as elements)

If we insist on having the `initial_layout` as a list that will be used to create a `Layout` object, we need to guarantee we are using the same semantic in all places.

> * What are the restrictions to set `initial_layout`?

At the very least, we know that the mapping between virtual qubits and physical qubits is 1:1, so we need to validate this.

> * Which component should validate `initial_layout`?

The `Layout` object. 

> If yes, why does layout (wrongly) [validate the size of the layout](https://github.com/Qiskit/qiskit-terra/blob/10ac1ecd2aa9b7a25e9735ff30339650d0e9b040/qiskit/transpiler/layout.py#L291) in the `from_intlist` static method?

This is definitely weird. Seems like the documentation and code are contradicting each other. If this function is doing what is documented to do, then this test should be for equality.

> If no, why does [`Layout` accept physical qubits pointing to `None` ](https://github.com/Qiskit/qiskit-terra/blob/10ac1ecd2aa9b7a25e9735ff30339650d0e9b040/test/python/transpiler/test_layout.py#L341)?

This is okay. If you have a physical device with more qubits than your circuit, then you need to allow physical qubits pointing to `None`. These physical qubits be using for routing during mapping and might hold a virtual qubit during the process or at it's end.


Some answers:

> * Why `initial_layout` is not a `Layout` object?

`initial_layout` can also be `Layout`. The `transpile` function is a very-high-level-user-facing, it is design to be very flexible in the input type.

> > * What are the restrictions to set `initial_layout`?
> 
> At the very least, we know that the mapping between virtual qubits and physical qubits is 1:1, so we need to validate this.

I think that's done when the `Layout` object is created. 

> > If no, why does [`Layout` accept physical qubits pointing to `None` ](https://github.com/Qiskit/qiskit-terra/blob/10ac1ecd2aa9b7a25e9735ff30339650d0e9b040/test/python/transpiler/test_layout.py#L341)?
> 
> This is okay. If you have a physical device with more qubits than your circuit, then you need to allow physical qubits pointing to `None`. These physical qubits be using for routing during mapping and might hold a virtual qubit during the process or at it's end.

I agree.


Resolving patch:
diff --git a/qiskit/transpiler/layout.py b/qiskit/transpiler/layout.py
--- a/qiskit/transpiler/layout.py
+++ b/qiskit/transpiler/layout.py
@@ -286,9 +286,8 @@ def from_intlist(int_list, *qregs):
             raise LayoutError('Duplicate values not permitted; Layout is bijective.')
         num_qubits = sum(reg.size for reg in qregs)
         # Check if list is too short to cover all qubits
-        if len(int_list) < num_qubits:
-            err_msg = 'Integer list length must equal number of qubits in circuit.'
-            raise LayoutError(err_msg)
+        if len(int_list) != num_qubits:
+            raise LayoutError('Integer list length must equal number of qubits in circuit.')
         out = Layout()
         main_idx = 0
         for qreg in qregs:


Test patch:
diff --git a/test/ipynb/mpl/test_circuit_matplotlib_drawer.py b/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
--- a/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
+++ b/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
@@ -349,7 +349,7 @@ def test_partial_layout(self):
         circuit = QuantumCircuit(3)
         circuit.h(1)
         transpiled = transpile(circuit, backend=FakeTenerife(),
-                               optimization_level=0, initial_layout=list(range(5)),
+                               optimization_level=0, initial_layout=list(range(3)),
                                seed_transpiler=0)
 
         self.circuit_drawer(transpiled, filename='partial_layout.png')
diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py
--- a/test/python/compiler/test_transpiler.py
+++ b/test/python/compiler/test_transpiler.py
@@ -618,7 +618,7 @@ def test_basis_subset(self):
         qc.x(qr[0])
         qc.t(qr[0])
 
-        layout = [4, 5, 6, 8, 9, 10]
+        layout = [4]
 
         cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9],
                 [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1],
diff --git a/test/python/transpiler/test_layout.py b/test/python/transpiler/test_layout.py
--- a/test/python/transpiler/test_layout.py
+++ b/test/python/transpiler/test_layout.py
@@ -338,7 +338,7 @@ def test_layout_from_intlist_numpy(self):
         self.assertDictEqual(layout._v2p, expected._v2p)
 
     def test_layout_from_intlist_short(self):
-        """If the intlist is longer that your quantum register, map them to None.
+        """Raise if the intlist is longer that your quantum register.
         virtual  physical
          q1_0  ->  4
          q2_0  ->  5
@@ -351,17 +351,8 @@ def test_layout_from_intlist_short(self):
         qr2 = QuantumRegister(2, 'qr2')
 
         intlist_layout = [4, 5, 6, 8, 9, 10]
-        layout = Layout.from_intlist(intlist_layout, qr1, qr2)
-
-        expected = Layout({4: qr1[0],
-                           5: qr2[0],
-                           6: qr2[1],
-                           8: None,
-                           9: None,
-                           10: None
-                           })
-        self.assertDictEqual(layout._p2v, expected._p2v)
-        self.assertDictEqual(layout._v2p, expected._v2p)
+        with self.assertRaises(LayoutError):
+            _ = Layout.from_intlist(intlist_layout, qr1, qr2)
 
     def test_layout_from_intlist_long(self):
         """If the intlist is shorter that your quantum register, fail.