From 6fac9862de660184235783f5e3bd8fbada568b01 Mon Sep 17 00:00:00 2001 From: Smit Date: Tue, 1 Dec 2020 23:49:18 -0800 Subject: [PATCH 1/7] Add measurement to StabilizerStateChForm and replace tableau with it for measurement --- cirq/sim/clifford/clifford_simulator.py | 2 +- cirq/sim/clifford/stabilizer_state_ch_form.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index faf39624686..c473c752f43 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -366,7 +366,7 @@ def perform_measurement(self, state = self.copy() for qubit in qubits: - result = state.tableau._measure(self.qubit_map[qubit], prng) + result = state.ch_form._measure(self.qubit_map[qubit], prng) state.ch_form.project_Z(self.qubit_map[qubit], result) results.append(result) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index 8364000634a..6a565dedd4e 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -242,6 +242,13 @@ def to_state_vector(self) -> np.ndarray: return arr + def _measure(self, q, prng: np.random.RandomState): + w = self.s.copy() + for i, vi in enumerate(self.v): + if vi == 1: + w[i] = bool(prng.randint(2)) + return sum(w & self.G[q, :]) % 2 + def project_Z(self, q, z): """ Applies a Z projector on the q'th qubit. From 9b2238d9fd16ceb8cdb9fca8606492d5b5be8d10 Mon Sep 17 00:00:00 2001 From: Smit Date: Wed, 2 Dec 2020 00:46:24 -0800 Subject: [PATCH 2/7] Add docstring --- cirq/sim/clifford/stabilizer_state_ch_form.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index 6a565dedd4e..0ebb2227bad 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -242,10 +242,16 @@ def to_state_vector(self) -> np.ndarray: return arr - def _measure(self, q, prng: np.random.RandomState): + 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, vi in enumerate(self.v): - if vi == 1: + for i, v_i in enumerate(self.v): + if v_i == 1: w[i] = bool(prng.randint(2)) return sum(w & self.G[q, :]) % 2 From 1aa87fcbdcfb7944cdc0f543331ee705d285c0ff Mon Sep 17 00:00:00 2001 From: Smit Date: Wed, 2 Dec 2020 01:07:32 -0800 Subject: [PATCH 3/7] Fix formatting --- cirq/sim/clifford/stabilizer_state_ch_form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index 8ba5d1ca4e6..a27adcaef21 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -244,7 +244,7 @@ def to_state_vector(self) -> np.ndarray: return arr def _measure(self, q, prng: np.random.RandomState) -> int: - """ Measures the q'th qubit. + """Measures the q'th qubit. Reference: Section 4.1 "Simulating measurements" From 48cb25cf256f3a040a2081036251ee78197fc361 Mon Sep 17 00:00:00 2001 From: Smit Date: Sun, 13 Dec 2020 15:04:08 -0800 Subject: [PATCH 4/7] Add projection to `_measure` itself --- cirq/sim/clifford/clifford_simulator.py | 1 - cirq/sim/clifford/stabilizer_state_ch_form.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 26d474f98e6..f38c0e6d179 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -371,7 +371,6 @@ def perform_measurement( for qubit in qubits: result = state.ch_form._measure(self.qubit_map[qubit], prng) - state.ch_form.project_Z(self.qubit_map[qubit], result) 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 a27adcaef21..e35facd96fc 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -254,7 +254,9 @@ def _measure(self, q, prng: np.random.RandomState) -> int: for i, v_i in enumerate(self.v): if v_i == 1: w[i] = bool(prng.randint(2)) - return sum(w & self.G[q, :]) % 2 + x_i = sum(w & self.G[q, :]) % 2 + self.project_Z(q, x_i) + return x_i def project_Z(self, q, z): """Applies a Z projector on the q'th qubit. From f95d19bfd8fc08a6b62a1960818a21074ec093eb Mon Sep 17 00:00:00 2001 From: Smit Date: Sun, 13 Dec 2020 17:27:02 -0800 Subject: [PATCH 5/7] Add test and comments --- cirq/sim/clifford/stabilizer_state_ch_form.py | 1 + .../clifford/stabilizer_state_ch_form_test.py | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form.py b/cirq/sim/clifford/stabilizer_state_ch_form.py index e35facd96fc..7e1b8ba8bcd 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form.py @@ -255,6 +255,7 @@ def _measure(self, q, prng: np.random.RandomState) -> int: 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 diff --git a/cirq/sim/clifford/stabilizer_state_ch_form_test.py b/cirq/sim/clifford/stabilizer_state_ch_form_test.py index dfce42acee9..edbd218fd29 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form_test.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form_test.py @@ -36,3 +36,39 @@ 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 + simulator = cirq.CliffordSimulator() + result = simulator.run(circuit, repetitions=10) + assert all(result.measurements['1'] == 1) + assert all(result.measurements['0'] != result.measurements['2']) From 13a3092ccc8c94257d2f04b0e58f5d42452c3c48 Mon Sep 17 00:00:00 2001 From: Smit Date: Sun, 13 Dec 2020 17:45:54 -0800 Subject: [PATCH 6/7] Minor formatting fix --- cirq/sim/clifford/stabilizer_state_ch_form_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form_test.py b/cirq/sim/clifford/stabilizer_state_ch_form_test.py index edbd218fd29..32b1c5cde28 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form_test.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form_test.py @@ -67,7 +67,7 @@ def test_run(): cirq.measure(q1), strategy=cirq.InsertStrategy.NEW, ) - # Cliffordsimulator uses StabilizerStateChForm internally + # CliffordSimulator uses StabilizerStateChForm internally. simulator = cirq.CliffordSimulator() result = simulator.run(circuit, repetitions=10) assert all(result.measurements['1'] == 1) From 9a5df0daecd46ca19883fcd0f05d15238ef0beac Mon Sep 17 00:00:00 2001 From: Smit Date: Sun, 13 Dec 2020 17:50:36 -0800 Subject: [PATCH 7/7] Add a TODO --- cirq/sim/clifford/stabilizer_state_ch_form_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cirq/sim/clifford/stabilizer_state_ch_form_test.py b/cirq/sim/clifford/stabilizer_state_ch_form_test.py index 32b1c5cde28..2007f9ffe91 100644 --- a/cirq/sim/clifford/stabilizer_state_ch_form_test.py +++ b/cirq/sim/clifford/stabilizer_state_ch_form_test.py @@ -68,6 +68,8 @@ def test_run(): 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)