diff --git a/crates/accelerate/src/synthesis/clifford/random_clifford.rs b/crates/accelerate/src/synthesis/clifford/random_clifford.rs index fee3ac8e9650..57531efcea55 100644 --- a/crates/accelerate/src/synthesis/clifford/random_clifford.rs +++ b/crates/accelerate/src/synthesis/clifford/random_clifford.rs @@ -125,17 +125,15 @@ pub fn random_clifford_tableau_inner(num_qubits: usize, seed: Option) -> Ar // Compute the full stabilizer tableau - // The code below is identical to the Python implementation, but is based on the original - // code in the paper. - + // The code below is based on the original code in the referenced paper. let mut table = Array2::from_elem((2 * num_qubits, 2 * num_qubits), false); // Apply qubit permutation for i in 0..num_qubits { - replace_row_inner(table.view_mut(), i, table2.slice(s![i, ..])); + replace_row_inner(table.view_mut(), i, table2.slice(s![perm[i], ..])); replace_row_inner( table.view_mut(), - perm[i] + num_qubits, + i + num_qubits, table2.slice(s![perm[i] + num_qubits, ..]), ); } diff --git a/releasenotes/notes/fix-random-clifford-c0394becbdd7db50.yaml b/releasenotes/notes/fix-random-clifford-c0394becbdd7db50.yaml new file mode 100644 index 000000000000..42a77da5210b --- /dev/null +++ b/releasenotes/notes/fix-random-clifford-c0394becbdd7db50.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a bug in :func:`~qiskit.quantum_info.random_clifford` that stopped it + from sampling the full Clifford group. diff --git a/test/python/quantum_info/operators/symplectic/test_clifford.py b/test/python/quantum_info/operators/symplectic/test_clifford.py index 253bc15852b6..177f9a9ed77d 100644 --- a/test/python/quantum_info/operators/symplectic/test_clifford.py +++ b/test/python/quantum_info/operators/symplectic/test_clifford.py @@ -473,7 +473,12 @@ def test_from_circuit_with_all_types(self): # and even circuits with other clifford objects. linear_function = LinearFunction([[0, 1], [1, 1]]) pauli_gate = PauliGate("YZ") - cliff = random_clifford(2, seed=777) + + qc_cliff = QuantumCircuit(2) + qc_cliff.h(0) + qc_cliff.cx(0, 1) + cliff = Clifford(qc_cliff) + qc = QuantumCircuit(2) qc.cx(0, 1) qc.append(random_clifford(1, seed=999), [1]) @@ -493,8 +498,8 @@ def test_from_circuit_with_all_types(self): # Additionally, make sure that it produces the correct clifford. expected_clifford_dict = { - "stabilizer": ["-IZX", "+XXZ", "-YYZ"], - "destabilizer": ["-YYI", "-XZI", "-ZXY"], + "stabilizer": ["-IZX", "+ZYZ", "+XZI"], + "destabilizer": ["+XZZ", "-XII", "+IXY"], } expected_clifford = Clifford.from_dict(expected_clifford_dict) self.assertEqual(combined_clifford, expected_clifford) diff --git a/test/python/quantum_info/operators/test_random.py b/test/python/quantum_info/operators/test_random.py index cb7e85ffabe3..bb1e65ba3d7d 100644 --- a/test/python/quantum_info/operators/test_random.py +++ b/test/python/quantum_info/operators/test_random.py @@ -190,6 +190,36 @@ def test_not_global_seed(self): rng_after = np.random.randint(1000, size=test_cases) self.assertFalse(np.all(rng_before == rng_after)) + def test_cliffords_2q(self): + """Test that we get all 2-qubit Cliffords (actually symplectic + matrices) with sufficiently many trials. + """ + seen = set() + for seed in range(10000): + cliff = random_clifford(2, seed) + seen.add(cliff.symplectic_matrix.tobytes()) + self.assertEqual(len(seen), 720) + + def test_clifford_2q_decompositions(self): + """Test that we get all possible CX-counts for 2q-random cliffords + with sufficiently many trials. + """ + seen = set() + for seed in range(100): + cliff = random_clifford(2, seed) + seen.add(cliff.to_circuit().count_ops().get("cx", 0)) + self.assertEqual(seen, {0, 1, 2, 3}) + + def test_clifford_3q_decompositions(self): + """Test that we get all possible CX-counts for 3q-random cliffords + with sufficiently many trials. + """ + seen = set() + for seed in range(10000): + cliff = random_clifford(3, seed) + seen.add(cliff.to_circuit().count_ops().get("cx", 0)) + self.assertEqual(seen, {0, 1, 2, 3, 4, 5, 6}) + @ddt class TestRandomPauliList(QiskitTestCase):