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 ClassicalDataStore class to keep track of qubits measured #4781

Merged
merged 84 commits into from
Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
84 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
dadfda9
Merge branch 'master' into qudits3
daxfohl Feb 3, 2022
c745e0f
make classical_data last arg everywhere
daxfohl Feb 3, 2022
642dc74
Merge branch 'master' into qudits2
daxfohl Feb 3, 2022
598ff8a
Merge branch 'master' into qudits2
daxfohl Feb 4, 2022
332e9dc
Merge branch 'master' into qudits2
daxfohl Feb 5, 2022
892fb3d
Merge branch 'master' into qudits2
CirqBot Feb 7, 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
4 changes: 4 additions & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,9 @@
canonicalize_half_turns,
chosen_angle_to_canonical_half_turns,
chosen_angle_to_half_turns,
ClassicalDataDictionaryStore,
ClassicalDataStore,
ClassicalDataStoreReader,
Condition,
Duration,
DURATION_LIKE,
Expand All @@ -491,6 +494,7 @@
LinearDict,
MEASUREMENT_KEY_SEPARATOR,
MeasurementKey,
MeasurementType,
PeriodicValue,
RANDOM_STATE_OR_SEED_LIKE,
state_vector_to_probabilities,
Expand Down
16 changes: 10 additions & 6 deletions cirq-core/cirq/contrib/quimb/mps_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import dataclasses
import math
from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Union
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, TYPE_CHECKING, Union

import numpy as np
import quimb.tensor as qtn
Expand Down Expand Up @@ -87,11 +87,11 @@ def __init__(
seed=seed,
)

def _create_partial_act_on_args(
def _create_partial_act_on_args_ex(
self,
initial_state: Union[int, 'MPSState'],
qubits: Sequence['cirq.Qid'],
logs: Dict[str, Any],
classical_data: 'cirq.ClassicalDataStore',
) -> 'MPSState':
"""Creates MPSState args for simulating the Circuit.

Expand All @@ -101,7 +101,8 @@ def _create_partial_act_on_args(
qubits: Determines the canonical ordering of the qubits. This
is often used in specifying the initial state, i.e. the
ordering of the computational basis states.
logs: A mutable object that measurements are recorded into.
classical_data: The shared classical data container for this
simulation.

Returns:
MPSState args for simulating the Circuit.
Expand All @@ -115,7 +116,7 @@ def _create_partial_act_on_args(
simulation_options=self.simulation_options,
grouping=self.grouping,
initial_state=initial_state,
log_of_measurement_results=logs,
classical_data=classical_data,
)

def _create_step_result(
Expand Down Expand Up @@ -229,6 +230,7 @@ def __init__(
grouping: Optional[Dict['cirq.Qid', int]] = None,
initial_state: int = 0,
log_of_measurement_results: Dict[str, Any] = None,
classical_data: 'cirq.ClassicalDataStore' = None,
):
"""Creates and MPSState

Expand All @@ -242,11 +244,13 @@ def __init__(
initial_state: An integer representing the initial state.
log_of_measurement_results: A mutable object that measurements are
being recorded into.
classical_data: The shared classical data container for this
simulation.

Raises:
ValueError: If the grouping does not cover the qubits.
"""
super().__init__(prng, qubits, log_of_measurement_results)
super().__init__(prng, qubits, log_of_measurement_results, classical_data)
qubit_map = self.qubit_map
self.grouping = qubit_map if grouping is None else grouping
if self.grouping.keys() != self.qubit_map.keys():
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/contrib/quimb/mps_simulator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,10 @@ def test_state_act_on_args_initializer():
s = ccq.mps_simulator.MPSState(
qubits=(cirq.LineQubit(0),),
prng=np.random.RandomState(0),
log_of_measurement_results={'test': 4},
log_of_measurement_results={'test': [4]},
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
)
assert s.qubits == (cirq.LineQubit(0),)
assert s.log_of_measurement_results == {'test': 4}
assert s.log_of_measurement_results == {'test': [4]}


def test_act_on_gate():
Expand Down
2 changes: 2 additions & 0 deletions cirq-core/cirq/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def _parallel_gate_op(gate, qubits):
'Circuit': cirq.Circuit,
'CircuitOperation': cirq.CircuitOperation,
'ClassicallyControlledOperation': cirq.ClassicallyControlledOperation,
'ClassicalDataDictionaryStore': cirq.ClassicalDataDictionaryStore,
'CliffordState': cirq.CliffordState,
'CliffordTableau': cirq.CliffordTableau,
'CNotPowGate': cirq.CNotPowGate,
Expand Down Expand Up @@ -104,6 +105,7 @@ def _parallel_gate_op(gate, qubits):
'MixedUnitaryChannel': cirq.MixedUnitaryChannel,
'MeasurementKey': cirq.MeasurementKey,
'MeasurementGate': cirq.MeasurementGate,
'MeasurementType': cirq.MeasurementType,
'_MeasurementSpec': cirq.work._MeasurementSpec,
'Moment': cirq.Moment,
'MutableDensePauliString': cirq.MutableDensePauliString,
Expand Down
5 changes: 2 additions & 3 deletions cirq-core/cirq/ops/classically_controlled_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ def _circuit_diagram_info_(
sub_info = protocols.circuit_diagram_info(self._sub_operation, sub_args, None)
if sub_info is None:
return NotImplemented # coverage: ignore

control_count = len({k for c in self._conditions for k in c.keys})
wire_symbols = sub_info.wire_symbols + ('^',) * control_count
if any(not isinstance(c, value.KeyCondition) for c in self._conditions):
Expand Down Expand Up @@ -176,8 +175,8 @@ def _json_dict_(self) -> Dict[str, Any]:
'sub_operation': self._sub_operation,
}

def _act_on_(self, args: 'cirq.ActOnArgs') -> bool:
if all(c.resolve(args.log_of_measurement_results) for c in self._conditions):
def _act_on_(self, args: 'cirq.OperationTarget') -> bool:
if all(c.resolve(args.classical_data) for c in self._conditions):
protocols.act_on(self._sub_operation, args)
return True

Expand Down
36 changes: 36 additions & 0 deletions cirq-core/cirq/ops/classically_controlled_operation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import pytest
import sympy
from sympy.parsing import sympy_parser
Expand Down Expand Up @@ -702,6 +704,40 @@ def test_sympy():
assert result.measurements['m_result'][0][0] == (j > i)


def test_sympy_qudits():
q0 = cirq.LineQid(0, 3)
q1 = cirq.LineQid(1, 5)
q_result = cirq.LineQubit(2)

class PlusGate(cirq.Gate):
def __init__(self, dimension, increment=1):
self.dimension = dimension
self.increment = increment % dimension

def _qid_shape_(self):
return (self.dimension,)

def _unitary_(self):
inc = (self.increment - 1) % self.dimension + 1
u = np.empty((self.dimension, self.dimension))
u[inc:] = np.eye(self.dimension)[:-inc]
u[:inc] = np.eye(self.dimension)[-inc:]
return u

for i in range(15):
digits = cirq.big_endian_int_to_digits(i, digit_count=2, base=(3, 5))
circuit = cirq.Circuit(
PlusGate(3, digits[0]).on(q0),
PlusGate(5, digits[1]).on(q1),
cirq.measure(q0, q1, key='m'),
cirq.X(q_result).with_classical_controls(sympy_parser.parse_expr('m % 4 <= 1')),
cirq.measure(q_result, key='m_result'),
)

result = cirq.Simulator().run(circuit)
assert result.measurements['m_result'][0][0] == (i % 4 <= 1)


def test_sympy_path_prefix():
q = cirq.LineQubit(0)
op = cirq.X(q).with_classical_controls(sympy.Symbol('b'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"cirq_type": "ClassicalDataDictionaryStore",
"measurements": [
[
{
"cirq_type": "MeasurementKey",
"name": "m",
"path": []
},
[0, 1]
]
],
"measured_qubits": [
[
{
"cirq_type": "MeasurementKey",
"name": "m",
"path": []
},
[
{
"cirq_type": "LineQubit",
"x": 0
},
{
"cirq_type": "LineQubit",
"x": 1
}
]
]
],
"channel_measurements": [
[
{
"cirq_type": "MeasurementKey",
"name": "c",
"path": []
},
3
]
],
"measurement_types": [
[
{
"cirq_type": "MeasurementKey",
"name": "m",
"path": []
},
1
],
[
{
"cirq_type": "MeasurementKey",
"name": "c",
"path": []
},
2
]
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +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})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1, 2]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[cirq.MeasurementType.MEASUREMENT, cirq.MeasurementType.CHANNEL]
3 changes: 0 additions & 3 deletions cirq-core/cirq/protocols/measurement_key_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
from cirq import value
from cirq._doc import doc_private

if TYPE_CHECKING:
import cirq

if TYPE_CHECKING:
import cirq

Expand Down
23 changes: 13 additions & 10 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, ops
from cirq import protocols, ops, value
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,
classical_data: 'cirq.ClassicalDataStore' = None,
ignore_measurement_results: bool = False,
):
"""Inits ActOnArgs.
Expand All @@ -59,6 +60,8 @@ def __init__(
ordering of the computational basis states.
log_of_measurement_results: A mutable object that measurements are
being recorded into.
classical_data: The shared classical data container for this
simulation.
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
Expand All @@ -68,11 +71,11 @@ def __init__(
prng = cast(np.random.RandomState, np.random)
if qubits is None:
qubits = ()
if log_of_measurement_results is None:
log_of_measurement_results = {}
self._set_qubits(qubits)
self.prng = prng
self._log_of_measurement_results = log_of_measurement_results
# pylint: disable=line-too-long
self._classical_data = classical_data or value.ClassicalDataDictionaryStore(_measurements=log_of_measurement_results) # type: ignore
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
# pylint: enable=line-too-long
self._ignore_measurement_results = ignore_measurement_results

def _set_qubits(self, qubits: Sequence['cirq.Qid']):
Expand Down Expand Up @@ -101,9 +104,9 @@ def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[
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:
raise ValueError(f"Measurement already logged to key {key!r}")
self._log_of_measurement_results[key] = corrected
self._classical_data.record_measurement(
value.MeasurementKey.parse_serialized(key), corrected, qubits
daxfohl marked this conversation as resolved.
Show resolved Hide resolved
)

def get_axes(self, qubits: Sequence['cirq.Qid']) -> List[int]:
return [self.qubit_map[q] for q in qubits]
Expand All @@ -117,7 +120,7 @@ def copy(self: TSelf) -> TSelf:
"""Creates a copy of the object."""
args = copy.copy(self)
self._on_copy(args)
args._log_of_measurement_results = self.log_of_measurement_results.copy()
args._classical_data = self._classical_data.copy()
return args

def _on_copy(self: TSelf, args: TSelf):
Expand Down Expand Up @@ -194,8 +197,8 @@ def _on_transpose_to_qubit_order(self: TSelf, qubits: Sequence['cirq.Qid'], targ
functionality, if supported."""

@property
def log_of_measurement_results(self) -> Dict[str, List[int]]:
return self._log_of_measurement_results
def classical_data(self) -> 'cirq.ClassicalDataStoreReader':
return self._classical_data
daxfohl marked this conversation as resolved.
Show resolved Hide resolved

@property
def ignore_measurement_results(self) -> bool:
Expand Down
Loading