Repository: Qiskit/qiskit

Issue:
CSPLayout is biased towards "low" qubits
Take the following example:
```python
from qiskit import QuantumCircuit

qc = QuantumCircuit(3)
qc.cz(0, 1)
qc.cz(1, 2)
qc.draw()
```
If this circuit is laid-out in a 5-qubit linear coupling map, using `CSPLayout`, the result is the following:
```python
from qiskit.transpiler import CouplingMap
from qiskit.converters import circuit_to_dag
from qiskit.transpiler.passes import CSPLayout

coupling_map = CouplingMap.from_line(5)

dag = circuit_to_dag(qc)
pass_ = CSPLayout(coupling_map)
pass_.run(dag)

print(pass_.property_set['layout'])
``` 
```
Layout({0: Qubit(QuantumRegister(3, 'q'), 0),
        1: Qubit(QuantumRegister(3, 'q'), 1),
        2: Qubit(QuantumRegister(3, 'q'), 2)})
```
Notice that there are other possible layout allocations besides the trivial one here. However, `CSPLayout` always choose this one, even when `CSPLayout(..., seed=...)` is set.

### What is the expected enhancement?

I would expect a uniformly distribution of all possible allocations, controlled by the seed parameter.  




Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/layout/csp_layout.py b/qiskit/transpiler/passes/layout/csp_layout.py
--- a/qiskit/transpiler/passes/layout/csp_layout.py
+++ b/qiskit/transpiler/passes/layout/csp_layout.py
@@ -100,6 +100,7 @@ def __init__(self, coupling_map, strict_direction=False, seed=None, call_limit=1
         self.seed = seed
 
     def run(self, dag):
+        """ run the layout method """
         qubits = dag.qubits
         cxs = set()
 
@@ -113,8 +114,12 @@ def run(self, dag):
         else:
             solver = CustomSolver(call_limit=self.call_limit, time_limit=self.time_limit)
 
+        variables = list(range(len(qubits)))
+        variable_domains = list(self.coupling_map.physical_qubits)
+        random.Random(self.seed).shuffle(variable_domains)
+
         problem = Problem(solver)
-        problem.addVariables(list(range(len(qubits))), self.coupling_map.physical_qubits)
+        problem.addVariables(variables, variable_domains)
         problem.addConstraint(AllDifferentConstraint())  # each wire is map to a single qubit
 
         if self.strict_direction:
@@ -127,7 +132,6 @@ def constraint(control, target):
         for pair in cxs:
             problem.addConstraint(constraint, [pair[0], pair[1]])
 
-        random.seed(self.seed)
         solution = problem.getSolution()
 
         if solution is None:
diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py
--- a/qiskit/transpiler/preset_passmanagers/level2.py
+++ b/qiskit/transpiler/preset_passmanagers/level2.py
@@ -101,7 +101,7 @@ def _choose_layout_condition(property_set):
         return not property_set['layout']
 
     _choose_layout_1 = [] if pass_manager_config.layout_method \
-        else CSPLayout(coupling_map, call_limit=1000, time_limit=10)
+        else CSPLayout(coupling_map, call_limit=1000, time_limit=10, seed=seed_transpiler)
     if layout_method == 'trivial':
         _choose_layout_2 = TrivialLayout(coupling_map)
     elif layout_method == 'dense':
diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py
--- a/qiskit/transpiler/preset_passmanagers/level3.py
+++ b/qiskit/transpiler/preset_passmanagers/level3.py
@@ -107,7 +107,7 @@ def _choose_layout_condition(property_set):
         return not property_set['layout']
 
     _choose_layout_1 = [] if pass_manager_config.layout_method \
-        else CSPLayout(coupling_map, call_limit=10000, time_limit=60)
+        else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler)
     if layout_method == 'trivial':
         _choose_layout_2 = TrivialLayout(coupling_map)
     elif layout_method == 'dense':


Test patch:
diff --git a/test/python/transpiler/test_csp_layout.py b/test/python/transpiler/test_csp_layout.py
--- a/test/python/transpiler/test_csp_layout.py
+++ b/test/python/transpiler/test_csp_layout.py
@@ -30,7 +30,7 @@ class TestCSPLayout(QiskitTestCase):
     def test_2q_circuit_2q_coupling(self):
         """ A simple example, without considering the direction
           0 - 1
-        qr0 - qr1
+        qr1 - qr0
         """
         qr = QuantumRegister(2, 'qr')
         circuit = QuantumCircuit(qr)
@@ -41,8 +41,8 @@ def test_2q_circuit_2q_coupling(self):
         pass_.run(dag)
         layout = pass_.property_set['layout']
 
-        self.assertEqual(layout[qr[0]], 0)
-        self.assertEqual(layout[qr[1]], 1)
+        self.assertEqual(layout[qr[0]], 1)
+        self.assertEqual(layout[qr[1]], 0)
         self.assertEqual(pass_.property_set['CSPLayout_stop_reason'], 'solution found')
 
     def test_3q_circuit_5q_coupling(self):
@@ -66,9 +66,9 @@ def test_3q_circuit_5q_coupling(self):
         pass_.run(dag)
         layout = pass_.property_set['layout']
 
-        self.assertEqual(layout[qr[0]], 0)
-        self.assertEqual(layout[qr[1]], 1)
-        self.assertEqual(layout[qr[2]], 2)
+        self.assertEqual(layout[qr[0]], 3)
+        self.assertEqual(layout[qr[1]], 2)
+        self.assertEqual(layout[qr[2]], 4)
         self.assertEqual(pass_.property_set['CSPLayout_stop_reason'], 'solution found')
 
     def test_9q_circuit_16q_coupling(self):
@@ -91,15 +91,15 @@ def test_9q_circuit_16q_coupling(self):
         pass_.run(dag)
         layout = pass_.property_set['layout']
 
-        self.assertEqual(layout[qr0[0]], 2)
-        self.assertEqual(layout[qr0[1]], 1)
-        self.assertEqual(layout[qr0[2]], 0)
-        self.assertEqual(layout[qr0[3]], 4)
-        self.assertEqual(layout[qr1[0]], 5)
-        self.assertEqual(layout[qr1[1]], 6)
-        self.assertEqual(layout[qr1[2]], 7)
-        self.assertEqual(layout[qr1[3]], 3)
-        self.assertEqual(layout[qr1[4]], 15)
+        self.assertEqual(layout[qr0[0]], 9)
+        self.assertEqual(layout[qr0[1]], 6)
+        self.assertEqual(layout[qr0[2]], 7)
+        self.assertEqual(layout[qr0[3]], 5)
+        self.assertEqual(layout[qr1[0]], 14)
+        self.assertEqual(layout[qr1[1]], 12)
+        self.assertEqual(layout[qr1[2]], 1)
+        self.assertEqual(layout[qr1[3]], 8)
+        self.assertEqual(layout[qr1[4]], 10)
         self.assertEqual(pass_.property_set['CSPLayout_stop_reason'], 'solution found')
 
     def test_2q_circuit_2q_coupling_sd(self):
@@ -166,15 +166,15 @@ def test_9q_circuit_16q_coupling_sd(self):
         pass_.run(dag)
         layout = pass_.property_set['layout']
 
-        self.assertEqual(layout[qr0[0]], 2)
-        self.assertEqual(layout[qr0[1]], 1)
-        self.assertEqual(layout[qr0[2]], 0)
-        self.assertEqual(layout[qr0[3]], 4)
-        self.assertEqual(layout[qr1[0]], 5)
-        self.assertEqual(layout[qr1[1]], 6)
-        self.assertEqual(layout[qr1[2]], 7)
-        self.assertEqual(layout[qr1[3]], 3)
-        self.assertEqual(layout[qr1[4]], 15)
+        self.assertEqual(layout[qr0[0]], 9)
+        self.assertEqual(layout[qr0[1]], 6)
+        self.assertEqual(layout[qr0[2]], 7)
+        self.assertEqual(layout[qr0[3]], 5)
+        self.assertEqual(layout[qr1[0]], 14)
+        self.assertEqual(layout[qr1[1]], 12)
+        self.assertEqual(layout[qr1[2]], 1)
+        self.assertEqual(layout[qr1[3]], 10)
+        self.assertEqual(layout[qr1[4]], 8)
         self.assertEqual(pass_.property_set['CSPLayout_stop_reason'], 'solution found')
 
     def test_5q_circuit_16q_coupling_no_solution(self):
@@ -263,6 +263,30 @@ def test_call_limit(self):
         self.assertLess(runtime, 1)
         self.assertEqual(pass_.property_set['CSPLayout_stop_reason'], 'call limit reached')
 
+    def test_seed(self):
+        """Different seeds yield different results"""
+        seed_1 = 42
+        seed_2 = 43
+
+        cmap5 = FakeTenerife().configuration().coupling_map
+
+        qr = QuantumRegister(3, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[1], qr[0])  # qr1 -> qr0
+        circuit.cx(qr[0], qr[2])  # qr0 -> qr2
+        circuit.cx(qr[1], qr[2])  # qr1 -> qr2
+        dag = circuit_to_dag(circuit)
+
+        pass_1 = CSPLayout(CouplingMap(cmap5), seed=seed_1)
+        pass_1.run(dag)
+        layout_1 = pass_1.property_set['layout']
+
+        pass_2 = CSPLayout(CouplingMap(cmap5), seed=seed_2)
+        pass_2.run(dag)
+        layout_2 = pass_2.property_set['layout']
+
+        self.assertNotEqual(layout_1, layout_2)
+
 
 if __name__ == '__main__':
     unittest.main()
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
@@ -407,26 +407,26 @@ def test_layout_tokyo_2845(self, level):
                         18: Qubit(QuantumRegister(15, 'ancilla'), 13),
                         19: Qubit(QuantumRegister(15, 'ancilla'), 14)}
 
-        csp_layout = {0: Qubit(QuantumRegister(3, 'qr1'), 1),
-                      1: Qubit(QuantumRegister(3, 'qr1'), 2),
-                      2: Qubit(QuantumRegister(2, 'qr2'), 0),
-                      5: Qubit(QuantumRegister(3, 'qr1'), 0),
-                      6: Qubit(QuantumRegister(2, 'qr2'), 1),
-                      3: Qubit(QuantumRegister(15, 'ancilla'), 0),
-                      4: Qubit(QuantumRegister(15, 'ancilla'), 1),
-                      7: Qubit(QuantumRegister(15, 'ancilla'), 2),
-                      8: Qubit(QuantumRegister(15, 'ancilla'), 3),
-                      9: Qubit(QuantumRegister(15, 'ancilla'), 4),
-                      10: Qubit(QuantumRegister(15, 'ancilla'), 5),
-                      11: Qubit(QuantumRegister(15, 'ancilla'), 6),
-                      12: Qubit(QuantumRegister(15, 'ancilla'), 7),
-                      13: Qubit(QuantumRegister(15, 'ancilla'), 8),
-                      14: Qubit(QuantumRegister(15, 'ancilla'), 9),
-                      15: Qubit(QuantumRegister(15, 'ancilla'), 10),
-                      16: Qubit(QuantumRegister(15, 'ancilla'), 11),
-                      17: Qubit(QuantumRegister(15, 'ancilla'), 12),
-                      18: Qubit(QuantumRegister(15, 'ancilla'), 13),
-                      19: Qubit(QuantumRegister(15, 'ancilla'), 14)}
+        csp_layout = {13: Qubit(QuantumRegister(3, 'qr1'), 0),
+                      19: Qubit(QuantumRegister(3, 'qr1'), 1),
+                      14: Qubit(QuantumRegister(3, 'qr1'), 2),
+                      18: Qubit(QuantumRegister(2, 'qr2'), 0),
+                      17: Qubit(QuantumRegister(2, 'qr2'), 1),
+                      0: Qubit(QuantumRegister(15, 'ancilla'), 0),
+                      1: Qubit(QuantumRegister(15, 'ancilla'), 1),
+                      2: Qubit(QuantumRegister(15, 'ancilla'), 2),
+                      3: Qubit(QuantumRegister(15, 'ancilla'), 3),
+                      4: Qubit(QuantumRegister(15, 'ancilla'), 4),
+                      5: Qubit(QuantumRegister(15, 'ancilla'), 5),
+                      6: Qubit(QuantumRegister(15, 'ancilla'), 6),
+                      7: Qubit(QuantumRegister(15, 'ancilla'), 7),
+                      8: Qubit(QuantumRegister(15, 'ancilla'), 8),
+                      9: Qubit(QuantumRegister(15, 'ancilla'), 9),
+                      10: Qubit(QuantumRegister(15, 'ancilla'), 10),
+                      11: Qubit(QuantumRegister(15, 'ancilla'), 11),
+                      12: Qubit(QuantumRegister(15, 'ancilla'), 12),
+                      15: Qubit(QuantumRegister(15, 'ancilla'), 13),
+                      16: Qubit(QuantumRegister(15, 'ancilla'), 14)}
 
         # Trivial layout
         expected_layout_level0 = trivial_layout