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

Allow repeated measurement keys #4899

Merged
merged 102 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
b76950e
Allow sympy expressions as classical controls
daxfohl Dec 9, 2021
5fdff50
Format
daxfohl Dec 9, 2021
ef081d7
move Condition to value
daxfohl Dec 9, 2021
0dd430e
Condition subclasses
daxfohl Dec 9, 2021
3fb7f75
Fix sympy resolver
daxfohl Dec 9, 2021
9568ae0
lint
daxfohl Dec 9, 2021
04fcff7
fix CCO serialization
daxfohl Dec 9, 2021
b3c344e
fix CCO serialization
daxfohl Dec 9, 2021
b8ff20a
add json reprs for conditions
daxfohl Dec 9, 2021
aa99805
add support for qudits in conditions
daxfohl Dec 9, 2021
cbb029b
add test
daxfohl Dec 9, 2021
efce2f9
tests
daxfohl Dec 9, 2021
b34994b
tests
daxfohl Dec 9, 2021
5537397
test
daxfohl Dec 9, 2021
73ed74b
format
daxfohl Dec 9, 2021
a415235
docstrings
daxfohl Dec 9, 2021
3a0a56d
subop
daxfohl Dec 9, 2021
f930f6a
regex
daxfohl Dec 10, 2021
39a7a95
docs
daxfohl Dec 10, 2021
42bac3e
Make test_sympy more intuitive.
daxfohl Dec 10, 2021
f4ea9d8
Sympy str roundtrip
daxfohl Dec 14, 2021
71f61f5
Resolve some code review comments
daxfohl Dec 16, 2021
2261355
Add escape key to parse_sympy_condition
daxfohl Dec 16, 2021
6b36357
repr
daxfohl Dec 16, 2021
afbf3c9
coverage
daxfohl Dec 16, 2021
58fb2dc
coverage
daxfohl Dec 16, 2021
bd80c0b
parser
daxfohl Dec 17, 2021
c39a572
Improve sympy repr
daxfohl Dec 17, 2021
12d38ca
lint
daxfohl Dec 20, 2021
724febb
sympy.basic
daxfohl Dec 20, 2021
b598697
Add sympy json resolvers for comparators
daxfohl Dec 20, 2021
d167de7
_from_json_dict_
daxfohl Dec 20, 2021
72d82eb
lint
daxfohl Dec 20, 2021
b55188e
reduce fixed_tokens
daxfohl Dec 20, 2021
fd1fefb
Merge branch 'master' into sympy3
daxfohl Dec 20, 2021
f6a6645
Merge branch 'master' into sympymerge
daxfohl Dec 20, 2021
de3f887
Merge branch 'sympy3' of https://github.com/daxfohl/Cirq into sympy3
daxfohl Dec 20, 2021
ca56bd8
more tests
daxfohl Dec 20, 2021
6f8e344
format
daxfohl Dec 20, 2021
689719f
Key
daxfohl Dec 21, 2021
96ba4e9
combined test
daxfohl Dec 21, 2021
793c138
Merge remote-tracking branch 'origin/sympy3' into sympy3
daxfohl Dec 21, 2021
0b5526f
Merge branch 'sympy3' into qudits2
daxfohl Dec 21, 2021
8c17a3f
Merge branch 'master' into qudits2
daxfohl Dec 23, 2021
c56d6bb
lint
daxfohl Dec 23, 2021
681a008
Docstrings
daxfohl Dec 23, 2021
1bddb1c
Merge branch 'master' into qudits2
daxfohl Dec 23, 2021
47d8288
ClassicalData class
daxfohl Dec 24, 2021
f411cc3
add get_int, fix bugs
daxfohl Dec 24, 2021
f0b0016
fix ActOnArgsContainer.copy
daxfohl Dec 24, 2021
334e985
format
daxfohl Dec 24, 2021
048da05
lint, mypy
daxfohl Dec 24, 2021
64f22e6
json
daxfohl Dec 24, 2021
38d23e7
lint
daxfohl Dec 24, 2021
87ecd1c
mkey compare
daxfohl Dec 24, 2021
831e76a
test class
daxfohl Dec 24, 2021
e93ffd1
docstrings, create independent function for measuring channels
daxfohl Dec 24, 2021
721382a
KeyError
daxfohl Dec 24, 2021
c22ba2a
revert to ValueError
daxfohl Dec 24, 2021
9940014
Base class
daxfohl Dec 26, 2021
f55965f
json
daxfohl Dec 26, 2021
f84565a
mypy
daxfohl Dec 26, 2021
a8848fb
mypy
daxfohl Dec 26, 2021
6b400a4
Add a base class
daxfohl Dec 26, 2021
26ab128
rename
daxfohl Dec 26, 2021
5404892
rename
daxfohl Dec 26, 2021
70b4b5e
lint
daxfohl Dec 26, 2021
b014135
docstrings, simplify some logic
daxfohl Dec 29, 2021
15fdf99
Deprecate _create_partial_act_on_args
daxfohl Dec 30, 2021
be4efdb
Merge branch 'master' into qudits2
daxfohl Jan 21, 2022
07cb0d5
lint
daxfohl Jan 21, 2022
4dc6c26
test
daxfohl Jan 21, 2022
437a153
lint
daxfohl Jan 21, 2022
f190ffa
nits
daxfohl Jan 21, 2022
30f7e46
Code review cleanup
daxfohl Jan 26, 2022
102303a
More comparisons in measurement_key
daxfohl Jan 26, 2022
be0e5ff
lint
daxfohl Jan 26, 2022
ba7a1f5
Additional tests
daxfohl Jan 26, 2022
24abf52
Add extra dimension to classical_data.py
daxfohl Jan 26, 2022
c04f998
Revert ClassicalData to returning whole dict
daxfohl Jan 26, 2022
96e8eab
Allow repeated measurements
daxfohl Jan 26, 2022
4d35308
Merge branch 'master' into repeated
daxfohl Jan 28, 2022
45df9ff
update commutes
daxfohl Jan 28, 2022
cd5bf2b
Merge branch 'master' into repeatedm
daxfohl Feb 8, 2022
5261426
Fix merge
daxfohl Feb 8, 2022
d475e69
Remove rebinding conflict check in measurement_key.py
daxfohl Feb 8, 2022
0df173f
Remove outdated tests
daxfohl Feb 8, 2022
0b1d45f
coverage
daxfohl Feb 8, 2022
e24c048
Revert serialization changes, improve error messages
daxfohl Feb 11, 2022
3b022dc
Re-add removed tests, with new expectations
daxfohl Feb 11, 2022
8158ba0
Remove unused _verify_unique_measurement_keys
daxfohl Feb 11, 2022
a728dc3
Improve sampler key error messages
daxfohl Feb 11, 2022
269fcf6
format
daxfohl Feb 11, 2022
9df9f15
Remove unnecessary re.escape
daxfohl Feb 11, 2022
69036c9
Change "measurements" to "records"
daxfohl Feb 14, 2022
f3fd9fe
Simplify ch-form sampling
daxfohl Feb 14, 2022
b0dff64
Merge branch 'master' into repeated
daxfohl Feb 14, 2022
e700108
Push 3d samples into result
daxfohl Feb 15, 2022
3b18f76
Fix sampling
daxfohl Feb 15, 2022
ff7bc67
numpy
daxfohl Feb 15, 2022
4dcc2ea
Fix repetitions
daxfohl Feb 15, 2022
808e558
Merge branch 'master' into repeated
CirqBot Feb 15, 2022
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
6 changes: 3 additions & 3 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1980,11 +1980,11 @@ def earliest_available_moment(
while k > 0:
k -= 1
moment = self._moments[k]
# This should also validate that measurement keys are disjoint once we allow repeated
# measurements. Search for same message in raw_types.py.
moment_measurement_keys = protocols.measurement_key_objs(moment)
if (
moment.operates_on(op_qubits)
or not op_control_keys.isdisjoint(protocols.measurement_key_objs(moment))
or not op_measurement_keys.isdisjoint(moment_measurement_keys)
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
or not op_control_keys.isdisjoint(moment_measurement_keys)
or not protocols.control_keys(moment).isdisjoint(op_measurement_keys)
):
return last_available
Expand Down
22 changes: 13 additions & 9 deletions cirq-core/cirq/circuits/circuit_operation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,24 +834,28 @@ def test_mapped_circuit_keeps_keys_under_parent_path():
assert cirq.measurement_key_names(op2.mapped_circuit()) == {'X:A', 'X:B', 'X:C', 'X:D'}


def test_keys_conflict_no_repetitions():
def test_mapped_circuit_allows_repeated_keys():
q = cirq.LineQubit(0)
op1 = cirq.CircuitOperation(
cirq.FrozenCircuit(
cirq.measure(q, key='A'),
)
)
op2 = cirq.CircuitOperation(cirq.FrozenCircuit(op1, op1))
with pytest.raises(ValueError, match='Conflicting measurement keys found: A'):
_ = op2.mapped_circuit(deep=True)


def test_keys_conflict_locally():
q = cirq.LineQubit(0)
circuit = op2.mapped_circuit(deep=True)
cirq.testing.assert_has_diagram(
circuit,
"0: ───M('A')───M('A')───",
use_unicode_characters=True,
)
op1 = cirq.measure(q, key='A')
op2 = cirq.CircuitOperation(cirq.FrozenCircuit(op1, op1))
with pytest.raises(ValueError, match='Conflicting measurement keys found: A'):
_ = op2.mapped_circuit()
circuit = op2.mapped_circuit()
cirq.testing.assert_has_diagram(
circuit,
"0: ───M('A')───M('A')───",
use_unicode_characters=True,
)


# TODO: Operation has a "gate" property. What is this for a CircuitOperation?
39 changes: 39 additions & 0 deletions cirq-core/cirq/ops/classically_controlled_operation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,45 @@ def test_key_set(sim):
assert result.measurements['b'] == 1


@pytest.mark.parametrize('sim', ALL_SIMULATORS)
def test_repeated_measurement_unset(sim):
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.measure(q0, key='a'),
cirq.X(q0),
cirq.measure(q0, key='a'),
cirq.X(q1).with_classical_controls(cirq.KeyCondition(cirq.MeasurementKey('a'), index=-2)),
cirq.measure(q1, key='b'),
cirq.X(q1).with_classical_controls(cirq.KeyCondition(cirq.MeasurementKey('a'), index=-1)),
cirq.measure(q1, key='c'),
)
result = sim.run(circuit)
assert result.records['a'][0][0][0] == 0
assert result.records['a'][0][1][0] == 1
assert result.records['b'][0][0][0] == 0
assert result.records['c'][0][0][0] == 1


@pytest.mark.parametrize('sim', ALL_SIMULATORS)
def test_repeated_measurement_set(sim):
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.X(q0),
cirq.measure(q0, key='a'),
cirq.X(q0),
cirq.measure(q0, key='a'),
cirq.X(q1).with_classical_controls(cirq.KeyCondition(cirq.MeasurementKey('a'), index=-2)),
cirq.measure(q1, key='b'),
cirq.X(q1).with_classical_controls(cirq.KeyCondition(cirq.MeasurementKey('a'), index=-1)),
cirq.measure(q1, key='c'),
)
result = sim.run(circuit)
assert result.records['a'][0][0][0] == 1
assert result.records['a'][0][1][0] == 0
assert result.records['b'][0][0][0] == 1
assert result.records['c'][0][0][0] == 1


@pytest.mark.parametrize('sim', ALL_SIMULATORS)
def test_subcircuit_key_unset(sim):
q0, q1 = cirq.LineQubit.range(2)
Expand Down
28 changes: 19 additions & 9 deletions cirq-core/cirq/ops/measurement_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import cast
import numpy as np
import pytest

Expand Down Expand Up @@ -323,10 +324,13 @@ def test_act_on_state_vector():
dtype=np.complex64,
)
cirq.act_on(m, args)
datastore = cast(cirq.ClassicalDataDictionaryStore, args.classical_data)
out = cirq.MeasurementKey('out')
assert args.log_of_measurement_results == {'out': [0, 1]}

with pytest.raises(ValueError, match="already logged to key"):
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
cirq.act_on(m, args)
assert datastore.records[out] == [(0, 1)]
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [0, 1]}
assert datastore.records[out] == [(0, 1), (0, 1)]


def test_act_on_clifford_tableau():
Expand Down Expand Up @@ -361,10 +365,13 @@ def test_act_on_clifford_tableau():
log_of_measurement_results={},
)
cirq.act_on(m, args)
datastore = cast(cirq.ClassicalDataDictionaryStore, args.classical_data)
out = cirq.MeasurementKey('out')
assert args.log_of_measurement_results == {'out': [0, 1]}

with pytest.raises(ValueError, match="already logged to key"):
cirq.act_on(m, args)
assert datastore.records[out] == [(0, 1)]
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [0, 1]}
assert datastore.records[out] == [(0, 1), (0, 1)]


def test_act_on_stabilizer_ch_form():
Expand Down Expand Up @@ -399,10 +406,13 @@ def test_act_on_stabilizer_ch_form():
initial_state=10,
)
cirq.act_on(m, args)
datastore = cast(cirq.ClassicalDataDictionaryStore, args.classical_data)
out = cirq.MeasurementKey('out')
assert args.log_of_measurement_results == {'out': [0, 1]}

with pytest.raises(ValueError, match="already logged to key"):
cirq.act_on(m, args)
assert datastore.records[out] == [(0, 1)]
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [0, 1]}
assert datastore.records[out] == [(0, 1), (0, 1)]


def test_act_on_qutrit():
Expand Down
12 changes: 7 additions & 5 deletions cirq-core/cirq/ops/raw_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,11 +572,13 @@ def _commutes_(
if not isinstance(other, Operation):
return NotImplemented

# This should also validate that measurement keys are disjoint once we allow repeated
# measurements. Search for same message in circuit.py.
if not protocols.control_keys(self).isdisjoint(
protocols.measurement_key_objs(other)
) or not protocols.control_keys(other).isdisjoint(protocols.measurement_key_objs(self)):
self_keys = protocols.measurement_key_objs(self)
other_keys = protocols.measurement_key_objs(other)
if (
not self_keys.isdisjoint(other_keys)
or not protocols.control_keys(self).isdisjoint(other_keys)
or not protocols.control_keys(other).isdisjoint(self_keys)
):
return False

if hasattr(other, 'qubits') and set(self.qubits).isdisjoint(other.qubits):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"cirq_type": "ClassicalDataDictionaryStore",
"measurements": [
"records": [
[
{
"cirq_type": "MeasurementKey",
"name": "m",
"path": []
},
[0, 1]
[[0, 1]]
]
],
"measured_qubits": [
Expand All @@ -17,7 +17,7 @@
"name": "m",
"path": []
},
[
[[
{
"cirq_type": "LineQubit",
"x": 0
Expand All @@ -26,17 +26,17 @@
"cirq_type": "LineQubit",
"x": 1
}
]
]]
]
],
"channel_measurements": [
"channel_records": [
[
{
"cirq_type": "MeasurementKey",
"name": "c",
"path": []
},
3
[3]
]
],
"measurement_types": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cirq.ClassicalDataDictionaryStore(_measurements={cirq.MeasurementKey('m'): [0, 1]}, _measured_qubits={cirq.MeasurementKey('m'): [cirq.LineQubit(0), cirq.LineQubit(1)]}, _channel_measurements={cirq.MeasurementKey('c'): 3}, _measurement_types={cirq.MeasurementKey('m'): cirq.MeasurementType.MEASUREMENT, cirq.MeasurementKey('c'): cirq.MeasurementType.CHANNEL})
cirq.ClassicalDataDictionaryStore(_records={cirq.MeasurementKey('m'): [[0, 1]]}, _measured_qubits={cirq.MeasurementKey('m'): [[cirq.LineQubit(0), cirq.LineQubit(1)]]}, _channel_records={cirq.MeasurementKey('c'): [3]}, _measurement_types={cirq.MeasurementKey('m'): cirq.MeasurementType.MEASUREMENT, cirq.MeasurementKey('c'): cirq.MeasurementType.CHANNEL})
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
"cirq_type": "MeasurementKey",
"name": "a",
"path": []
}
},
"index": -1
},
{
"cirq_type": "KeyCondition",
"key": {
"cirq_type": "MeasurementKey",
"name": "b",
"path": []
}
},
"index": -1
}
],
"sub_operation": {
Expand Down
3 changes: 2 additions & 1 deletion cirq-core/cirq/protocols/json_test_data/KeyCondition.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"cirq_type": "MeasurementKey",
"name": "a",
"path": []
}
},
"index": -1
}
4 changes: 2 additions & 2 deletions cirq-core/cirq/sim/act_on_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ def __init__(
self._set_qubits(qubits)
self.prng = prng
self._classical_data = classical_data or value.ClassicalDataDictionaryStore(
_measurements={
value.MeasurementKey.parse_serialized(k): tuple(v)
_records={
value.MeasurementKey.parse_serialized(k): [tuple(v)]
for k, v in (log_of_measurement_results or {}).items()
}
)
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/sim/act_on_args_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ def __init__(
self._qubits = tuple(qubits)
self.split_untangled_states = split_untangled_states
self._classical_data = classical_data or value.ClassicalDataDictionaryStore(
_measurements={
value.MeasurementKey.parse_serialized(k): tuple(v)
_records={
value.MeasurementKey.parse_serialized(k): [tuple(v)]
for k, v in (log_of_measurement_results or {}).items()
}
)
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/sim/clifford/act_on_stabilizer_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

import abc
from typing import Any, Dict, Generic, Optional, Sequence, TYPE_CHECKING, TypeVar, Union
from typing import Any, Dict, Generic, List, Optional, Sequence, TYPE_CHECKING, TypeVar, Union

import numpy as np

Expand All @@ -38,7 +38,7 @@ def __init__(
self,
state: TStabilizerState,
prng: Optional[np.random.RandomState] = None,
log_of_measurement_results: Optional[Dict[str, Any]] = None,
log_of_measurement_results: Optional[Dict[str, List[int]]] = None,
qubits: Optional[Sequence['cirq.Qid']] = None,
classical_data: Optional['cirq.ClassicalDataStore'] = None,
):
Expand Down
22 changes: 8 additions & 14 deletions cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Dict, List, Optional, Sequence, TYPE_CHECKING, Union
from typing import Dict, List, Optional, Sequence, TYPE_CHECKING, Union

import numpy as np

from cirq import _compat, value, ops, protocols
from cirq import _compat, value
from cirq.sim.clifford import stabilizer_state_ch_form
from cirq.sim.clifford.act_on_stabilizer_args import ActOnStabilizerArgs

Expand All @@ -39,7 +39,7 @@ def __init__(
self,
state: Optional['cirq.StabilizerStateChForm'] = None,
prng: Optional[np.random.RandomState] = None,
log_of_measurement_results: Optional[Dict[str, Any]] = None,
log_of_measurement_results: Optional[Dict[str, List[int]]] = None,
qubits: Optional[Sequence['cirq.Qid']] = None,
initial_state: Union[int, 'cirq.StabilizerStateChForm'] = 0,
classical_data: Optional['cirq.ClassicalDataStore'] = None,
Expand Down Expand Up @@ -107,16 +107,10 @@ def sample(
repetitions: int = 1,
seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
) -> np.ndarray:
measurements = value.ClassicalDataDictionaryStore()
prng = value.parse_random_state(seed)
for i in range(repetitions):
op = ops.measure(*qubits, key=str(i))
axes = self.get_axes(qubits)
measurements = []
for _ in range(repetitions):
state = self.state.copy()
ch_form_args = ActOnStabilizerCHFormArgs(
classical_data=measurements,
prng=prng,
qubits=self.qubits,
initial_state=state,
)
protocols.act_on(op, ch_form_args)
return np.array(list(measurements.measurements.values()), dtype=bool)
measurements.append([state._measure(i, prng) for i in axes])
return np.array(measurements, dtype=bool)
2 changes: 1 addition & 1 deletion cirq-core/cirq/sim/clifford/clifford_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,4 @@ def apply_measurement(
initial_state=state.ch_form,
)
act_on(op, ch_form_args)
measurements.update({str(k): list(v) for k, v in classical_data.measurements.items()})
measurements.update({str(k): list(v[-1]) for k, v in classical_data.records.items()})
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@ def test_run():
initial_state=state,
)
cirq.act_on(op, args)
measurements = {str(k): list(v) for k, v in classical_data.measurements.items()}
measurements = {str(k): list(v[-1]) for k, v in classical_data.records.items()}
assert measurements['1'] == [1]
assert measurements['0'] != measurements['2']
3 changes: 1 addition & 2 deletions cirq-core/cirq/sim/operation_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"""An interface for quantum states as targets for operations."""
import abc
from typing import (
Any,
Dict,
Generic,
Iterator,
Expand Down Expand Up @@ -86,7 +85,7 @@ def qubits(self) -> Tuple['cirq.Qid', ...]:
"""Gets the qubit order maintained by this target."""

@property
def log_of_measurement_results(self) -> Dict[str, Any]:
def log_of_measurement_results(self) -> Dict[str, List[int]]:
"""Gets the log of measurement results."""
return {str(k): list(self.classical_data.get_digits(k)) for k in self.classical_data.keys()}

Expand Down
Loading