Repository: Qiskit/qiskit

Issue:
Need Consistent Layout Representations During Transpile
There are a few bug reports recently with how Layout is applied. The source of confusion is that different passes do slightly different thigns with the layout. 

After internal discussions with @itoko @kdk @1ucian0, here is the plan:

- [x] Clean up the ApplyLayout pass (#2050) and merge it. This pass should be dumb, and just consume a virtual circuit + Layout object, and output a register-less circuit (for now, a flat register). This involves updating the dag nodes as well as they contain register pointers. @itoko 

- [x] Change BasicSwap and StochasticSwap to consume physical circuits (register-less where each index literally corresponds to the physical qubit). @1ucian0 

- [x] Change LookaheadSwap to remove the logic for flattening register. @kdk 

- [x] Remove LegacySwap and replace by StochasticSwap (after some more testing). @ajavadia 

- [x] Update the other passes that currently consume a Layout (e.g. CXDirection, CXCheck, ..) to just assume that the layout has been applied, so just consult the (physical) circuit. @ajavadia 

- [x] Add a circuit.layout property, so that the selected layout can be queried by the user later. This property will be populated by the layout selection pass (NoiseAdaptiveLayout, DenseLayout, TrivialLayout, SetLayout). So then, remove this from `pass_manager.property_set`. @1ucian0 

Fixing these should close  #1831, #2066, #2405, #2503, #2532.


Discussion:
BasicSwap PR #2590
While changing `BasicSwap` and `StochasticSwap`, shall they raise if the coupling map is bigger than the size of the physical circuit?

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -99,6 +99,8 @@ def __init__(self, *regs, name=None):
         # Parameter table tracks instructions with variable parameters.
         self._parameter_table = ParameterTable()
 
+        self.layout = None
+
     def __str__(self):
         return str(self.draw(output='text'))
 
diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py
--- a/qiskit/transpiler/passmanager.py
+++ b/qiskit/transpiler/passmanager.py
@@ -139,6 +139,7 @@ def run(self, circuit):
 
         circuit = dag_to_circuit(dag)
         circuit.name = name
+        circuit.layout = self.property_set['layout']
         return circuit
 
     def draw(self, filename, style=None, raw=False):


Test patch:
diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py
--- a/test/python/transpiler/test_preset_passmanagers.py
+++ b/test/python/transpiler/test_preset_passmanagers.py
@@ -70,12 +70,18 @@ def test_initial_layout_1(self):
         See: https://github.com/Qiskit/qiskit-terra/issues/1711
         """
         # build a circuit which works as-is on the coupling map, using the initial layout
-        qr = QuantumRegister(3)
+        qr = QuantumRegister(3, 'q')
         cr = ClassicalRegister(3)
+        ancilla = QuantumRegister(13, 'ancilla')
         qc = QuantumCircuit(qr, cr)
         qc.cx(qr[2], qr[1])
         qc.cx(qr[2], qr[0])
         initial_layout = {0: qr[1], 2: qr[0], 15: qr[2]}
+        final_layout = {0: qr[1], 1: ancilla[0], 2: qr[0], 3: ancilla[1], 4: ancilla[2],
+                        5: ancilla[3], 6: ancilla[4], 7: ancilla[5], 8: ancilla[6],
+                        9: ancilla[7], 10: ancilla[8], 11: ancilla[9], 12: ancilla[10],
+                        13: ancilla[11], 14: ancilla[12], 15: qr[2]}
+
         backend = FakeRueschlikon()
 
         for optimization_level in range(4):
@@ -83,6 +89,8 @@ def test_initial_layout_1(self):
                              optimization_level=optimization_level)
             qobj = assemble(qc_b)
 
+            self.assertEqual(qc_b.layout._p2v, final_layout)
+
             compiled_ops = qobj.experiments[0].instructions
             for operation in compiled_ops:
                 if operation.name == 'cx':
@@ -97,18 +105,24 @@ def test_initial_layout_2(self):
         See: https://github.com/Qiskit/qiskit-terra/issues/2532
         """
         # build a circuit which works as-is on the coupling map, using the initial layout
-        qr = QuantumRegister(5)
+        qr = QuantumRegister(5, 'q')
         cr = ClassicalRegister(2)
+        ancilla = QuantumRegister(9, 'ancilla')
         qc = QuantumCircuit(qr, cr)
         qc.cx(qr[2], qr[4])
         initial_layout = {qr[2]: 11, qr[4]: 3,  # map to [11, 3] connection
                           qr[0]: 1, qr[1]: 5, qr[3]: 9}
+        final_layout = {0: ancilla[0], 1: qr[0], 2: ancilla[1], 3: qr[4], 4: ancilla[2], 5: qr[1],
+                        6: ancilla[3], 7: ancilla[4], 8: ancilla[5], 9: qr[3], 10: ancilla[6],
+                        11: qr[2], 12: ancilla[7], 13: ancilla[8]}
         backend = FakeMelbourne()
 
         for optimization_level in range(4):
             qc_b = transpile(qc, backend, initial_layout=initial_layout,
                              optimization_level=optimization_level)
 
+            self.assertEqual(qc_b.layout._p2v, final_layout)
+
             for gate, qubits, _ in qc_b:
                 if gate.name == 'cx':
                     for qubit in qubits:
@@ -122,20 +136,31 @@ def test_initial_layout_3(self):
         See: https://github.com/Qiskit/qiskit-terra/issues/2503
         """
         # build a circuit which works as-is on the coupling map, using the initial layout
-        qr = QuantumRegister(3)
+        qr = QuantumRegister(3, 'q')
         cr = ClassicalRegister(2)
+        ancilla = QuantumRegister(17, 'ancilla')
+
         qc = QuantumCircuit(qr, cr)
         qc.u3(0.1, 0.2, 0.3, qr[0])
         qc.u2(0.4, 0.5, qr[2])
         qc.barrier()
         qc.cx(qr[0], qr[2])
         initial_layout = [6, 7, 12]
+
+        final_layout = {0: ancilla[0], 1: ancilla[1], 2: ancilla[2], 3: ancilla[3], 4: ancilla[4],
+                        5: ancilla[5], 6: qr[0], 7: qr[1], 8: ancilla[6], 9: ancilla[7],
+                        10: ancilla[8], 11: ancilla[9], 12: qr[2], 13: ancilla[10], 14: ancilla[11],
+                        15: ancilla[12], 16: ancilla[13], 17: ancilla[14], 18: ancilla[15],
+                        19: ancilla[16]}
+
         backend = FakePoughkeepsie()
 
         for optimization_level in range(4):
             qc_b = transpile(qc, backend, initial_layout=initial_layout,
                              optimization_level=optimization_level)
 
+            self.assertEqual(qc_b.layout._p2v, final_layout)
+
             gate_0, qubits_0, _ = qc_b[0]
             gate_1, qubits_1, _ = qc_b[1]