Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add measurement support to ActOnStabilizerChFormArgs and use it in MeasurementGate #3610

Merged
merged 6 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cirq/ops/common_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,12 @@ def test_act_on_ch_form(input_gate_sequence, outcome):
else:
assert num_qubits == 2
axes = [0, 1]
args = cirq.ActOnStabilizerCHFormArgs(state=original_state.copy(), axes=axes)
args = cirq.ActOnStabilizerCHFormArgs(
state=original_state.copy(),
axes=axes,
prng=np.random.RandomState(),
log_of_measurement_results={},
)

flipped_state = cirq.StabilizerStateChForm(num_qubits=5, initial_state=23)

Expand Down
7 changes: 6 additions & 1 deletion cirq/ops/global_phase_op_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ def test_act_on_tableau(phase):
@pytest.mark.parametrize('phase', [1, 1j, -1])
def test_act_on_ch_form(phase):
state = cirq.StabilizerStateChForm(0)
args = cirq.ActOnStabilizerCHFormArgs(state, [])
args = cirq.ActOnStabilizerCHFormArgs(
state,
[],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(cirq.GlobalPhaseOperation(phase), args, allow_decompose=False)
assert state.state_vector() == [[phase]]

Expand Down
7 changes: 7 additions & 0 deletions cirq/ops/measurement_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ def _act_on_(self, args: Any) -> bool:
args.record_measurement_result(self.key, corrected)
return True

if isinstance(args, sim.clifford.ActOnStabilizerCHFormArgs):
invert_mask = self.full_invert_mask()
bits = [args.state._measure(q, args.prng) for q in args.axes]
corrected = [bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask)]
args.record_measurement_result(self.key, corrected)
return True

return NotImplemented


Expand Down
41 changes: 41 additions & 0 deletions cirq/ops/measurement_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,47 @@ def test_act_on_clifford_tableau():
cirq.act_on(m, args)


def test_act_on_stabilizer_ch_form():
a, b = cirq.LineQubit.range(2)
m = cirq.measure(a, b, key='out', invert_mask=(True,))
# The below assertion does not fail since it ignores non-unitary operations
cirq.testing.assert_all_implemented_act_on_effects_match_unitary(m)

with pytest.raises(TypeError, match="Failed to act"):
cirq.act_on(m, object())

args = cirq.ActOnStabilizerCHFormArgs(
state=cirq.StabilizerStateChForm(num_qubits=5, initial_state=0),
axes=[3, 1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [1, 0]}

args = cirq.ActOnStabilizerCHFormArgs(
state=cirq.StabilizerStateChForm(num_qubits=5, initial_state=8),
axes=[3, 1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)

cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [1, 1]}

args = cirq.ActOnStabilizerCHFormArgs(
state=cirq.StabilizerStateChForm(num_qubits=5, initial_state=10),
axes=[3, 1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [0, 1]}

with pytest.raises(ValueError, match="already logged to key"):
cirq.act_on(m, args)


def test_act_on_qutrit():
a, b = cirq.LineQid.range(2, dimension=3)
m = cirq.measure(a, b, key='out', invert_mask=(True,))
Expand Down
30 changes: 27 additions & 3 deletions cirq/sim/clifford/act_on_stabilizer_ch_form_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Iterable, TYPE_CHECKING
from typing import Any, Dict, Iterable, TYPE_CHECKING

import numpy as np

Expand All @@ -31,19 +31,43 @@ class ActOnStabilizerCHFormArgs:

To act on this object, directly edit the `state` property, which is
storing the stabilizer state of the quantum system with one axis per qubit.
Measurements are currently not supported on this object.
"""

def __init__(self, state: StabilizerStateChForm, axes: Iterable[int]):
def __init__(
self,
state: StabilizerStateChForm,
axes: Iterable[int],
prng: np.random.RandomState,
balopat marked this conversation as resolved.
Show resolved Hide resolved
log_of_measurement_results: Dict[str, Any],
):
"""Initializes with the given state and the axes for the operation.
Args:
state: The StabilizerStateChForm to act on. Operations are expected
to perform inplace edits of this object.
axes: The indices of axes corresponding to the qubits that the
operation is supposed to act upon.
prng: The pseudo random number generator to use for probabilistic
effects.
log_of_measurement_results: A mutable object that measurements are
being recorded into. Edit it easily by calling
`ActOnStabilizerCHFormArgs.record_measurement_result`.
"""
self.state = state
self.axes = tuple(axes)
self.prng = prng
self.log_of_measurement_results = log_of_measurement_results

def record_measurement_result(self, key: str, value: Any):
"""Adds a measurement result to the log.
Args:
key: The key the measurement result should be logged under. Note
that operations should only store results under keys they have
declared in a `_measurement_keys_` method.
value: The value to log for the measurement.
"""
if key in self.log_of_measurement_results:
raise ValueError(f"Measurement already logged to key {key!r}")
self.log_of_measurement_results[key] = value

def _act_on_fallback_(self, action: Any, allow_decompose: bool):
strats = []
Expand Down
42 changes: 36 additions & 6 deletions cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ def test_cannot_act():
class NoDetails(cirq.SingleQubitGate):
pass

args = cirq.ActOnStabilizerCHFormArgs(state=cirq.StabilizerStateChForm(num_qubits=3), axes=[1])
args = cirq.ActOnStabilizerCHFormArgs(
state=cirq.StabilizerStateChForm(num_qubits=3),
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)

with pytest.raises(TypeError, match="Failed to act"):
cirq.act_on(NoDetails(), args)
Expand All @@ -37,7 +42,12 @@ def _act_on_(self, args):
return True

state = cirq.StabilizerStateChForm(num_qubits=3)
args = cirq.ActOnStabilizerCHFormArgs(state=state, axes=[1])
args = cirq.ActOnStabilizerCHFormArgs(
state=state,
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)

cirq.act_on(CustomGate(), args)

Expand All @@ -54,9 +64,19 @@ def _unitary_(self):

original_state = cirq.StabilizerStateChForm(num_qubits=3)

args = cirq.ActOnStabilizerCHFormArgs(state=original_state.copy(), axes=[1])
args = cirq.ActOnStabilizerCHFormArgs(
state=original_state.copy(),
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(UnitaryYGate(), args)
expected_args = cirq.ActOnStabilizerCHFormArgs(state=original_state.copy(), axes=[1])
expected_args = cirq.ActOnStabilizerCHFormArgs(
state=original_state.copy(),
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(cirq.Y, expected_args)
np.testing.assert_allclose(args.state.state_vector(), expected_args.state.state_vector())

Expand All @@ -71,8 +91,18 @@ def _unitary_(self):

original_state = cirq.StabilizerStateChForm(num_qubits=3)

args = cirq.ActOnStabilizerCHFormArgs(state=original_state.copy(), axes=[1])
args = cirq.ActOnStabilizerCHFormArgs(
state=original_state.copy(),
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(UnitaryHGate(), args)
expected_args = cirq.ActOnStabilizerCHFormArgs(state=original_state.copy(), axes=[1])
expected_args = cirq.ActOnStabilizerCHFormArgs(
state=original_state.copy(),
axes=[1],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
cirq.act_on(cirq.H, expected_args)
np.testing.assert_allclose(args.state.state_vector(), expected_args.state.state_vector())
2 changes: 1 addition & 1 deletion cirq/sim/clifford/clifford_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def apply_unitary(self, op: 'cirq.Operation'):
self.tableau, [self.qubit_map[i] for i in op.qubits], np.random.RandomState(), {}
)
ch_form_args = clifford.ActOnStabilizerCHFormArgs(
self.ch_form, [self.qubit_map[i] for i in op.qubits]
self.ch_form, [self.qubit_map[i] for i in op.qubits], np.random.RandomState(), {}
)
try:
act_on(op, tableau_args)
Expand Down
5 changes: 4 additions & 1 deletion cirq/sim/clifford/stabilizer_state_ch_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def __init__(self, num_qubits: int, initial_state: int = 0) -> None:
big_endian_int_to_digits(initial_state, digit_count=num_qubits, base=2)
):
if val:
protocols.act_on(pauli_gates.X, clifford.ActOnStabilizerCHFormArgs(self, [i]))
protocols.act_on(
pauli_gates.X,
clifford.ActOnStabilizerCHFormArgs(self, [i], np.random.RandomState(), {}),
)

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['n', 'G', 'F', 'M', 'gamma', 'v', 's', 'omega'])
Expand Down
5 changes: 4 additions & 1 deletion cirq/testing/consistent_act_on.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ def _final_stabilizer_state_ch_form(
for op in circuit.all_operations():
try:
args = act_on_stabilizer_ch_form_args.ActOnStabilizerCHFormArgs(
state=stabilizer_ch_form, axes=[qubit_map[qid] for qid in op.qubits]
state=stabilizer_ch_form,
axes=[qubit_map[qid] for qid in op.qubits],
prng=np.random.RandomState(),
log_of_measurement_results={},
)
protocols.act_on(op, args, allow_decompose=True)
except TypeError:
Expand Down