Repository: quantumlib/Cirq

Issue:
`random_rotations_between_grid_interaction_layers_circuit` output depends on the order of qubits in a pair
**Description of the issue**
Depending on the order of qubits and the qubits in the pairs you can get different outputs, which I don't think is the expected behavior. Basically if `qubits = [q0, q1]`, but the pair in `pattern`  is reversed, i.e. `pattern = [[(q1, q0)], []]` then no two qubit gate will show up in the circuit. See below for example

**How to reproduce the issue**

``` python
import cirq
import cirq.experiments.random_quantum_circuit_generation as rqcg

qubits = (cirq.q(0, 0), cirq.q(0, 1))

pattern_1 = [[(cirq.q(0, 1), cirq.q(0, 0))], []]
rqcg.random_rotations_between_grid_interaction_layers_circuit(
    qubits,
    depth=2,
    two_qubit_op_factory=lambda q1, q2, _: cirq.CZ(q1, q2),
    single_qubit_gates=(cirq.X,),
    pattern=pattern_1
)
```
outputs 
```
(0, 0): ───X───X───X───

(0, 1): ───X───X───X───
```
, which is missing the CZ gates. However, if I sort the pair then
```python
pattern_2 = [[(cirq.q(0, 0), cirq.q(0, 1))], []]

rqcg.random_rotations_between_grid_interaction_layers_circuit(
    qubits,
    depth=2,
    two_qubit_op_factory=lambda q1, q2, _: cirq.CZ(q1, q2),
    single_qubit_gates=(cirq.X,),
    pattern=pattern_2
)
```
outputs 
```
(0, 0): ───X───@───X───X───
               │
(0, 1): ───X───@───X───X───
```
which is what I expected.



**Cirq version**
'1.3.0.dev20230802160330'




Discussion:
Nevermind, I realized this is intended behavior.
Apologize for the confusion. I now convinced myself this is a bug. I think the problem lies here
https://github.com/quantumlib/Cirq/blob/7ed95aa3177d3936fcda9a79747372c34f35cbee/cirq-core/cirq/experiments/random_quantum_circuit_generation.py#L621-L633
and
https://github.com/quantumlib/Cirq/blob/7ed95aa3177d3936fcda9a79747372c34f35cbee/cirq-core/cirq/experiments/random_quantum_circuit_generation.py#L687-L697

where we only construct pairs `(a, b)`, but not `(b_a)`

Resolving patch:
diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py
--- a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py
+++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py
@@ -693,5 +693,5 @@ def _two_qubit_layer(
     prng: 'np.random.RandomState',
 ) -> 'cirq.OP_TREE':
     for a, b in coupled_qubit_pairs:
-        if (a, b) in layer:
+        if (a, b) in layer or (b, a) in layer:
             yield two_qubit_op_factory(a, b, prng)


Test patch:
diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py
--- a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py
+++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py
@@ -259,6 +259,30 @@ def __init__(self):
     'seed, expected_circuit_length, single_qubit_layers_slice, '
     'two_qubit_layers_slice',
     (
+        (
+            (cirq.q(0, 0), cirq.q(0, 1), cirq.q(0, 2)),
+            4,
+            lambda a, b, _: cirq.CZ(a, b),
+            [[(cirq.q(0, 0), cirq.q(0, 1))], [(cirq.q(0, 1), cirq.q(0, 2))]],
+            (cirq.X**0.5,),
+            True,
+            1234,
+            9,
+            slice(None, None, 2),
+            slice(1, None, 2),
+        ),
+        (
+            (cirq.q(0, 0), cirq.q(0, 1), cirq.q(0, 2)),
+            4,
+            lambda a, b, _: cirq.CZ(a, b),
+            [[(cirq.q(0, 1), cirq.q(0, 0))], [(cirq.q(0, 1), cirq.q(0, 2))]],
+            (cirq.X**0.5,),
+            True,
+            1234,
+            9,
+            slice(None, None, 2),
+            slice(1, None, 2),
+        ),
         (
             cirq.GridQubit.rect(4, 3),
             20,
@@ -406,7 +430,10 @@ def _validate_two_qubit_layers(
             # Operation is two-qubit
             assert cirq.num_qubits(op) == 2
             # Operation fits pattern
-            assert op.qubits in pattern[i % len(pattern)]
+            assert (
+                op.qubits in pattern[i % len(pattern)]
+                or op.qubits[::-1] in pattern[i % len(pattern)]
+            )
             active_pairs.add(op.qubits)
         # All interactions that should be in this layer are present
         assert all(