diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 0b8a2311bd6..f38c0e6d179 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -370,8 +370,7 @@ def perform_measurement( state = self.copy() for qubit in qubits: - result = state.tableau._measure(self.qubit_map[qubit], prng) - state.ch_form.project_Z(self.qubit_map[qubit], result) + result = state.ch_form._measure(self.qubit_map[qubit], prng) results.append(result) return results diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index f5b597adc03..7e1b8ba8bcd 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -243,6 +243,22 @@ def to_state_vector(self) -> np.ndarray: return arr + def _measure(self, q, prng: np.random.RandomState) -> int: + """Measures the q'th qubit. + + Reference: Section 4.1 "Simulating measurements" + + Returns: Computational basis measurement as 0 or 1. + """ + w = self.s.copy() + for i, v_i in enumerate(self.v): + if v_i == 1: + w[i] = bool(prng.randint(2)) + x_i = sum(w & self.G[q, :]) % 2 + # Project the state to the above measurement outcome. + self.project_Z(q, x_i) + return x_i + def project_Z(self, q, z): """Applies a Z projector on the q'th qubit. diff --git a/cirq/sim/clifford/stabilizer_state_ch_form_test.py b/cirq/sim/clifford/stabilizer_state_ch_form_test.py index dfce42acee9..2007f9ffe91 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form_test.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form_test.py @@ -36,3 +36,41 @@ def test_initial_state(): expected_state_vector = np.zeros(32) expected_state_vector[23] = 1 np.testing.assert_allclose(state.state_vector(), expected_state_vector) + + +def test_run(): + (q0, q1, q2) = (cirq.LineQubit(0), cirq.LineQubit(1), cirq.LineQubit(2)) + + """ + 0: ───H───@───────────────X───M─────────── + │ + 1: ───────X───@───────X───────────X───M─── + │ │ + 2: ───────────X───M───────────────@─────── + + After the third moment, before the measurement, the state is |000> + |111>. + After measurement of q2, q0 and q1 both get a bit flip, so the q0 + measurement always yields opposite of the q2 measurement. q1 has an + additional controlled not from q2, making it yield 1 always when measured. + If there were no measurements in the circuit, the final state would be + |110> + |011>. + """ + circuit = cirq.Circuit( + cirq.H(q0), + cirq.CNOT(q0, q1), + cirq.CNOT(q1, q2), + cirq.measure(q2), + cirq.X(q1), + cirq.X(q0), + cirq.measure(q0), + cirq.CNOT(q2, q1), + cirq.measure(q1), + strategy=cirq.InsertStrategy.NEW, + ) + # CliffordSimulator uses StabilizerStateChForm internally. + # TODO: Use StabilizerStateChForm directly through `act_on` once + # MeasurementGate is updated to use `_measure` from StabilizerStateChForm. + simulator = cirq.CliffordSimulator() + result = simulator.run(circuit, repetitions=10) + assert all(result.measurements['1'] == 1) + assert all(result.measurements['0'] != result.measurements['2'])