-
Notifications
You must be signed in to change notification settings - Fork 989
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 cirq.Simulator assuming a reset is safe to cache before sampling #3093
Conversation
Strilanc
commented
Jun 16, 2020
- This resulted in some circuits with random behavior giving long lists of identical samples when repeated
- Change the general strategy used by the simulator to one of extracting a unitary prefix that can be cached, and then looking at the rest to decide whether sampling can be done efficiently or not
- This resulted in some circuits with random behavior giving long lists of identical samples when repeated - Change the general strategy used by the simulator to one of extracting a unitary prefix that can be cached, and then looking at the rest to decide whether sampling can be done efficiently or not
…andom # Conflicts: # cirq/sim/sparse_simulator.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM only one minor comment.
general_ops = list(general_suffix.all_operations()) | ||
if all(isinstance(op.gate, ops.MeasurementGate) for op in general_ops): | ||
return step_result.sample_measurement_ops(measurement_ops=cast( | ||
List[ops.GateOperation], general_ops), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
autoformat is on drugs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot question it, we can only follow.
@@ -331,3 +336,27 @@ def sample(self, | |||
self, None), | |||
repetitions=repetitions, | |||
seed=seed) | |||
|
|||
|
|||
def _split_into_unitary_then_general(circuit: 'cirq.Circuit' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is probably a case to be made for a good way to handle this sort of splitting of circuits by disjoint filter as part of Cirq. No need to do now but maybe we should have an issue for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acknowledged.
initial_state=0, | ||
perform_measurements=False): | ||
qubit_order = sorted(resolved_circuit.all_qubits()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to make sure I understand: the bug was that intermediate channel ops that could be monte carlo sampled were allowed to not be terminal, and the new way this works it splits into the biggest possible unitary chunk and if then "others" to figure out whether this is possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that was the issue.
seen_qubits: Set[cirq.Qid] = set() | ||
for op in measurement_ops: | ||
for q in op.qubits: | ||
if q not in seen_qubits: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you just do something like this?
for op in measurement_ops:
for q in op.qubits:
seen_qubits.add(q)
measured_qubits = list(seen_qubits)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I initially did, and it resulted in several fixed-seed tests being flaky. The problem is it results in a non-deterministic ordering because set
doesn't guarantee insertion order ordering.
cirq/sim/sparse_simulator.py
Outdated
|
||
def _run_sweep_repeat(self, initial_state: np.ndarray, | ||
circuit: circuits.Circuit, | ||
qubit_order: 'cirq.QubitOrderOrList', | ||
repetitions: int) -> Dict[str, np.ndarray]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth adding a developer-centric docstring? It's a private method, so probably not mandatory, but might be nice to have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also renamed it.
|
||
|
||
def _split_into_unitary_then_general(circuit: 'cirq.Circuit' | ||
) -> Tuple['cirq.Circuit', 'cirq.Circuit']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto. I would either expand on the comment above "Simulate as many unitary operations as possible..." or write a docstring to explain what this is doing and returning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acknowledged. In this case I think the method name is pretty descriptive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a docstring.
@@ -147,7 +147,7 @@ def test_run_invert_mask_measure_not_terminal(dtype): | |||
np.testing.assert_equal(result.measurements, | |||
{'m': [[1 - b0, b1]] * 3}) | |||
assert result.repetitions == 3 | |||
assert mock_sim.call_count == 12 | |||
assert mock_sim.call_count > 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this and below, would it be worth a line comment to explain what the assertion is testing and/or why ">4" is expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comments
cirq/sim/sparse_simulator_test.py
Outdated
|
||
|
||
def test_entangled_reset_does_not_break_randomness(): | ||
# A previous version made the mistake of assuming that it was okay to cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this should use the triple quote doc string instead of the line comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh. I never considered putting a doc string on a test method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it a doc string.
…uantumlib#3093) - This resulted in some circuits with random behavior giving long lists of identical samples when repeated - Change the general strategy used by the simulator to one of extracting a unitary prefix that can be cached, and then looking at the rest to decide whether sampling can be done efficiently or not