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

Fix ignore_measurement_results=True for subcircuits #4760

Merged
merged 3 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 19 additions & 2 deletions cirq-core/cirq/sim/act_on_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

import numpy as np

from cirq import protocols
from cirq import protocols, ops
from cirq.protocols.decompose_protocol import _try_decompose_into_operations_and_qubits
from cirq.sim.operation_target import OperationTarget

Expand All @@ -47,6 +47,7 @@ def __init__(
prng: np.random.RandomState = None,
qubits: Sequence['cirq.Qid'] = None,
log_of_measurement_results: Dict[str, List[int]] = None,
ignore_measurement_results: bool = False,
):
"""Inits ActOnArgs.
Expand All @@ -58,6 +59,10 @@ def __init__(
ordering of the computational basis states.
log_of_measurement_results: A mutable object that measurements are
being recorded into.
ignore_measurement_results: If True, then the simulation
will treat measurement as dephasing instead of collapsing
process, and not log the result. This is only applicable to
simulators that can represent mixed states.
"""
if prng is None:
prng = cast(np.random.RandomState, np.random)
Expand All @@ -68,13 +73,18 @@ def __init__(
self._set_qubits(qubits)
self.prng = prng
self._log_of_measurement_results = log_of_measurement_results
self._ignore_measurement_results = ignore_measurement_results

def _set_qubits(self, qubits: Sequence['cirq.Qid']):
self._qubits = tuple(qubits)
self.qubit_map = {q: i for i, q in enumerate(self.qubits)}

def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[bool]):
"""Adds a measurement result to the log.
"""Measures the qubits and records to `log_of_measurement_results`.
Any bitmasks will be applied to the measurement record. If
`self._ignore_measurement_results` is set, it dephases instead of
measuring, and no measurement result will be logged.
Args:
qubits: The qubits to measure.
Expand All @@ -86,6 +96,9 @@ def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[
Raises:
ValueError: If a measurement key has already been logged to a key.
"""
if self.ignore_measurement_results:
self._act_on_fallback_(ops.phase_damp(1), qubits)
return
bits = self._perform_measurement(qubits)
corrected = [bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask)]
if key in self._log_of_measurement_results:
Expand Down Expand Up @@ -184,6 +197,10 @@ def _on_transpose_to_qubit_order(self: TSelf, qubits: Sequence['cirq.Qid'], targ
def log_of_measurement_results(self) -> Dict[str, List[int]]:
return self._log_of_measurement_results

@property
def ignore_measurement_results(self) -> bool:
return self._ignore_measurement_results

@property
def qubits(self) -> Tuple['cirq.Qid', ...]:
return self._qubits
Expand Down
7 changes: 6 additions & 1 deletion cirq-core/cirq/sim/act_on_density_matrix_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(
prng: np.random.RandomState = None,
log_of_measurement_results: Dict[str, Any] = None,
qubits: Sequence['cirq.Qid'] = None,
ignore_measurement_results: bool = False,
):
"""Inits ActOnDensityMatrixArgs.

Expand All @@ -60,8 +61,12 @@ def __init__(
effects.
log_of_measurement_results: A mutable object that measurements are
being recorded into.
ignore_measurement_results: If True, then the simulation
will treat measurement as dephasing instead of collapsing
process. This is only applicable to simulators that can
model dephasing.
"""
super().__init__(prng, qubits, log_of_measurement_results)
super().__init__(prng, qubits, log_of_measurement_results, ignore_measurement_results)
self.target_tensor = target_tensor
self.available_buffer = available_buffer
self.qid_shape = qid_shape
Expand Down
1 change: 1 addition & 0 deletions cirq-core/cirq/sim/density_matrix_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def _create_partial_act_on_args(
qid_shape=qid_shape,
prng=self._prng,
log_of_measurement_results=logs,
ignore_measurement_results=self._ignore_measurement_results,
)

def _can_be_in_run_prefix(self, val: Any):
Expand Down
25 changes: 25 additions & 0 deletions cirq-core/cirq/sim/density_matrix_simulator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,31 @@ def test_simulate(dtype: Type[np.number], split: bool):
assert len(result.measurements) == 0


@pytest.mark.parametrize('split', [True, False])
def test_simulate_ignore_measurements(split: bool):
q0 = cirq.LineQubit(0)
simulator = cirq.DensityMatrixSimulator(
split_untangled_states=split, ignore_measurement_results=True
)
circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0))
result = simulator.simulate(circuit)
np.testing.assert_almost_equal(result.final_density_matrix, np.eye(2) * 0.5)
assert len(result.measurements) == 0


@pytest.mark.parametrize('split', [True, False])
def test_simulate_ignore_measurements_subcircuits(split: bool):
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
q0 = cirq.LineQubit(0)
simulator = cirq.DensityMatrixSimulator(
split_untangled_states=split, ignore_measurement_results=True
)
circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0))
circuit = cirq.Circuit(cirq.CircuitOperation(circuit.freeze()))
result = simulator.simulate(circuit)
np.testing.assert_almost_equal(result.final_density_matrix, np.eye(2) * 0.5)
assert len(result.measurements) == 0


@pytest.mark.parametrize('dtype', [np.complex64, np.complex128])
@pytest.mark.parametrize('split', [True, False])
def test_simulate_qudits(dtype: Type[np.number], split: bool):
Expand Down
5 changes: 0 additions & 5 deletions cirq-core/cirq/sim/simulator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,18 +207,13 @@ def _core_iterator(
for moment in noisy_moments:
for op in ops.flatten_to_ops(moment):
try:
# TODO: support more general measurements.
# Github issue: https://github.com/quantumlib/Cirq/issues/3566

# Preprocess measurements
if all_measurements_are_terminal and measured[op.qubits]:
continue
if isinstance(op.gate, ops.MeasurementGate):
measured[op.qubits] = True
if all_measurements_are_terminal:
continue
if self._ignore_measurement_results:
op = ops.phase_damp(1).on(*op.qubits)

# Simulate the operation
protocols.act_on(op, sim_state)
Expand Down