Skip to content

Commit 680f897

Browse files
Revert "Support qubits in MeasurementKey and update JSON serialization of keys" (#4277)
* Revert "Support qubits in `MeasurementKey` and update JSON serialization of keys (#4123)" This reverts commit 81436fe. * Safeguard against repr-inequality. * assert_equivalent_repr
1 parent 1433621 commit 680f897

26 files changed

+83
-889
lines changed

cirq-core/cirq/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@
433433
canonicalize_half_turns,
434434
chosen_angle_to_canonical_half_turns,
435435
chosen_angle_to_half_turns,
436-
default_measurement_key_str,
437436
Duration,
438437
DURATION_LIKE,
439438
GenericMetaImplementAnyOneOf,

cirq-core/cirq/circuits/circuit_test.py

+2-10
Original file line numberDiff line numberDiff line change
@@ -1367,16 +1367,8 @@ def test_findall_operations_with_gate(circuit_cls):
13671367
(3, cirq.CZ(a, b), cirq.CZ),
13681368
]
13691369
assert list(c.findall_operations_with_gate_type(cirq.MeasurementGate)) == [
1370-
(
1371-
4,
1372-
cirq.MeasurementGate(1).on(a),
1373-
cirq.MeasurementGate(1, key=cirq.MeasurementKey(qubits=(a,))),
1374-
),
1375-
(
1376-
4,
1377-
cirq.MeasurementGate(1).on(b),
1378-
cirq.MeasurementGate(1, key=cirq.MeasurementKey(qubits=(b,))),
1379-
),
1370+
(4, cirq.MeasurementGate(1, key='a').on(a), cirq.MeasurementGate(1, key='a')),
1371+
(4, cirq.MeasurementGate(1, key='b').on(b), cirq.MeasurementGate(1, key='b')),
13801372
]
13811373

13821374

cirq-core/cirq/ops/gate_operation.py

-4
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,6 @@ def __str__(self) -> str:
123123
def _json_dict_(self) -> Dict[str, Any]:
124124
return protocols.obj_to_dict_helper(self, ['gate', 'qubits'])
125125

126-
@classmethod
127-
def _from_json_dict_(cls, gate, qubits, **kwargs):
128-
return gate.on(*qubits)
129-
130126
def _group_interchangeable_qubits(
131127
self,
132128
) -> Tuple[Union['cirq.Qid', Tuple[int, FrozenSet['cirq.Qid']]], ...]:

cirq-core/cirq/ops/measure_util.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Callable, List, Tuple, TYPE_CHECKING, Union
15+
from typing import Callable, Iterable, List, Optional, Tuple, TYPE_CHECKING, Union
1616

1717
import numpy as np
1818

@@ -24,9 +24,13 @@
2424
import cirq
2525

2626

27+
def _default_measurement_key(qubits: Iterable[raw_types.Qid]) -> str:
28+
return ','.join(str(q) for q in qubits)
29+
30+
2731
def measure(
2832
*target: 'cirq.Qid',
29-
key: Union[str, value.MeasurementKey] = '',
33+
key: Optional[Union[str, value.MeasurementKey]] = None,
3034
invert_mask: Tuple[bool, ...] = (),
3135
) -> raw_types.Operation:
3236
"""Returns a single MeasurementGate applied to all the given qubits.
@@ -35,7 +39,7 @@ def measure(
3539
3640
Args:
3741
*target: The qubits that the measurement gate should measure.
38-
key: The string key of the measurement. If this is empty, defaults
42+
key: The string key of the measurement. If this is None, it defaults
3943
to a comma-separated list of the target qubits' str values.
4044
invert_mask: A list of Truthy or Falsey values indicating whether
4145
the corresponding qubits should be flipped. None indicates no
@@ -56,12 +60,14 @@ def measure(
5660
elif not isinstance(qubit, raw_types.Qid):
5761
raise ValueError('measure() was called with type different than Qid.')
5862

63+
if key is None:
64+
key = _default_measurement_key(target)
5965
qid_shape = protocols.qid_shape(target)
6066
return MeasurementGate(len(target), key, invert_mask, qid_shape).on(*target)
6167

6268

6369
def measure_each(
64-
*qubits: 'cirq.Qid', key_func: Callable[[raw_types.Qid], str] = lambda x: ''
70+
*qubits: 'cirq.Qid', key_func: Callable[[raw_types.Qid], str] = str
6571
) -> List[raw_types.Operation]:
6672
"""Returns a list of operations individually measuring the given qubits.
6773
@@ -70,9 +76,7 @@ def measure_each(
7076
Args:
7177
*qubits: The qubits to measure.
7278
key_func: Determines the key of the measurements of each qubit. Takes
73-
the qubit and returns the key for that qubit. Defaults to empty string
74-
key which would result in a comma-separated list of the target qubits'
75-
str values.
79+
the qubit and returns the key for that qubit. Defaults to str.
7680
7781
Returns:
7882
A list of operations individually measuring the given qubits.

cirq-core/cirq/ops/measure_util_test.py

+4-14
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,12 @@ def test_measure_qubits():
2626
with pytest.raises(ValueError, match='empty set of qubits'):
2727
_ = cirq.measure()
2828

29-
assert cirq.measure(a) == cirq.MeasurementGate(num_qubits=1, key='').on(a)
30-
assert cirq.measure(a) != cirq.measure(a, key='a')
31-
assert cirq.measure(a) != cirq.MeasurementGate(num_qubits=1, key='').on(b)
32-
assert cirq.measure(a) != cirq.MeasurementGate(num_qubits=1, key='a').on(a)
33-
assert cirq.measure(a) == cirq.MeasurementGate(
34-
num_qubits=1, key=cirq.MeasurementKey(qubits=(a,))
35-
).on(a)
36-
assert cirq.measurement_key(cirq.measure(a)) == 'a'
37-
assert cirq.measure(a, b) == cirq.MeasurementGate(num_qubits=2, key='').on(a, b)
38-
assert cirq.measurement_key(cirq.measure(a, b)) == 'a,b'
39-
assert cirq.measure(b, a) == cirq.MeasurementGate(num_qubits=2, key='').on(b, a)
40-
assert cirq.measurement_key(cirq.measure(b, a)) == 'b,a'
29+
assert cirq.measure(a) == cirq.MeasurementGate(num_qubits=1, key='a').on(a)
30+
assert cirq.measure(a, b) == cirq.MeasurementGate(num_qubits=2, key='a,b').on(a, b)
31+
assert cirq.measure(b, a) == cirq.MeasurementGate(num_qubits=2, key='b,a').on(b, a)
4132
assert cirq.measure(a, key='b') == cirq.MeasurementGate(num_qubits=1, key='b').on(a)
42-
assert cirq.measure(a, key='b') != cirq.MeasurementGate(num_qubits=1, key='b').on(b)
4333
assert cirq.measure(a, invert_mask=(True,)) == cirq.MeasurementGate(
44-
num_qubits=1, invert_mask=(True,)
34+
num_qubits=1, key='a', invert_mask=(True,)
4535
).on(a)
4636
assert cirq.measure(*cirq.LineQid.for_qid_shape((1, 2, 3)), key='a') == cirq.MeasurementGate(
4737
num_qubits=3, key='a', qid_shape=(1, 2, 3)

cirq-core/cirq/ops/measurement_gate.py

+16-26
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Any, Dict, Optional, Tuple, Sequence, TYPE_CHECKING, Union
15+
from typing import Any, Dict, Iterable, Optional, Tuple, Sequence, TYPE_CHECKING, Union
1616

1717
import numpy as np
1818

1919
from cirq import protocols, value
20-
from cirq.ops import raw_types, gate_operation
20+
from cirq.ops import raw_types
2121

2222
if TYPE_CHECKING:
2323
import cirq
@@ -80,21 +80,12 @@ def key(self, key: Union[str, value.MeasurementKey]):
8080
else:
8181
self.mkey = value.MeasurementKey(name=key)
8282

83-
def on(self, *qubits: raw_types.Qid) -> raw_types.Operation:
84-
"""Returns an application of this gate to the given qubits.
85-
86-
Args:
87-
*qubits: The collection of qubits to potentially apply the gate to.
88-
"""
89-
maybe_rekeyed_gate = self.with_key(self.mkey.with_qubits(qubits))
90-
return gate_operation.GateOperation(maybe_rekeyed_gate, list(qubits))
91-
9283
def _qid_shape_(self) -> Tuple[int, ...]:
9384
return self._qid_shape
9485

9586
def with_key(self, key: Union[str, value.MeasurementKey]) -> 'MeasurementGate':
9687
"""Creates a measurement gate with a new key but otherwise identical."""
97-
if isinstance(key, value.MeasurementKey) and key == self.mkey:
88+
if key == self.key:
9889
return self
9990
return MeasurementGate(
10091
self.num_qubits(), key=key, invert_mask=self.invert_mask, qid_shape=self._qid_shape
@@ -114,7 +105,7 @@ def with_bits_flipped(self, *bit_positions: int) -> 'MeasurementGate':
114105
for b in bit_positions:
115106
new_mask[b] = not new_mask[b]
116107
return MeasurementGate(
117-
self.num_qubits(), key=self.mkey, invert_mask=tuple(new_mask), qid_shape=self._qid_shape
108+
self.num_qubits(), key=self.key, invert_mask=tuple(new_mask), qid_shape=self._qid_shape
118109
)
119110

120111
def full_invert_mask(self):
@@ -161,8 +152,8 @@ def _circuit_diagram_info_(
161152
if b:
162153
symbols[i] = '!M'
163154

164-
# Mention the measurement key if it is non-trivial or there are no known qubits.
165-
if self.mkey.name or self.mkey.path or not args.known_qubits:
155+
# Mention the measurement key.
156+
if not args.known_qubits or self.key != _default_measurement_key(args.known_qubits):
166157
symbols[0] += f"('{self.key}')"
167158

168159
return protocols.CircuitDiagramInfo(tuple(symbols))
@@ -200,13 +191,8 @@ def _quil_(
200191

201192
def _op_repr_(self, qubits: Sequence['cirq.Qid']) -> str:
202193
args = list(repr(q) for q in qubits)
203-
if self.mkey.name or self.mkey.path:
204-
if self.mkey == self.mkey.name:
205-
args.append(f'key={self.mkey.name!r}')
206-
else:
207-
# Remove qubits from the `MeasurementKey` representation since we already have
208-
# qubits from the op.
209-
args.append(f'key={self.mkey.with_qubits(tuple())!r}')
194+
if self.key != _default_measurement_key(qubits):
195+
args.append(f'key={self.key!r}')
210196
if self.invert_mask:
211197
args.append(f'invert_mask={self.invert_mask!r}')
212198
arg_list = ', '.join(args)
@@ -219,13 +205,13 @@ def __repr__(self):
219205
return (
220206
f'cirq.MeasurementGate('
221207
f'{self.num_qubits()!r}, '
222-
f'{self.mkey.name if self.mkey == self.mkey.name else self.mkey!r}, '
208+
f'{self.key!r}, '
223209
f'{self.invert_mask}'
224210
f'{qid_shape_arg})'
225211
)
226212

227213
def _value_equality_values_(self) -> Any:
228-
return self.mkey, self.invert_mask, self._qid_shape
214+
return self.key, self.invert_mask, self._qid_shape
229215

230216
def _json_dict_(self) -> Dict[str, Any]:
231217
other = {}
@@ -234,7 +220,7 @@ def _json_dict_(self) -> Dict[str, Any]:
234220
return {
235221
'cirq_type': self.__class__.__name__,
236222
'num_qubits': len(self._qid_shape),
237-
'key': self.mkey,
223+
'key': self.key,
238224
'invert_mask': self.invert_mask,
239225
**other,
240226
}
@@ -243,7 +229,7 @@ def _json_dict_(self) -> Dict[str, Any]:
243229
def _from_json_dict_(cls, num_qubits, key, invert_mask, qid_shape=None, **kwargs):
244230
return cls(
245231
num_qubits=num_qubits,
246-
key=value.MeasurementKey.parse_serialized(key) if isinstance(key, str) else key,
232+
key=value.MeasurementKey.parse_serialized(key),
247233
invert_mask=tuple(invert_mask),
248234
qid_shape=None if qid_shape is None else tuple(qid_shape),
249235
)
@@ -254,3 +240,7 @@ def _has_stabilizer_effect_(self) -> Optional[bool]:
254240
def _act_on_(self, args: 'cirq.ActOnArgs', qubits: Sequence['cirq.Qid']) -> bool:
255241
args.measure(qubits, self.key, self.full_invert_mask())
256242
return True
243+
244+
245+
def _default_measurement_key(qubits: Iterable[raw_types.Qid]) -> str:
246+
return ','.join(str(q) for q in qubits)

cirq-core/cirq/ops/measurement_gate_test.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
import cirq
1919

2020

21+
def test_eval_repr():
22+
# Basic safeguard against repr-inequality.
23+
op = cirq.GateOperation(
24+
gate=cirq.MeasurementGate(1, cirq.MeasurementKey(path=(), name='q0_1_0'), ()),
25+
qubits=[cirq.GridQubit(0, 1)],
26+
)
27+
cirq.testing.assert_equivalent_repr(op)
28+
29+
2130
@pytest.mark.parametrize('num_qubits', [1, 2, 4])
2231
def test_measure_init(num_qubits):
2332
assert cirq.MeasurementGate(num_qubits).num_qubits() == num_qubits
@@ -266,15 +275,6 @@ def test_op_repr():
266275
"key='out', "
267276
"invert_mask=(False, True))"
268277
)
269-
assert repr(
270-
cirq.measure(
271-
a, b, key=cirq.MeasurementKey(name='out', path=('a', 'b')), invert_mask=(False, True)
272-
)
273-
) == (
274-
"cirq.measure(cirq.LineQubit(0), cirq.LineQubit(1), "
275-
"key=cirq.MeasurementKey(path=('a', 'b'), name='out'), "
276-
"invert_mask=(False, True))"
277-
)
278278

279279

280280
def test_act_on_state_vector():

cirq-core/cirq/protocols/json_test_data/Circuit.json

+1-27
Original file line numberDiff line numberDiff line change
@@ -85,33 +85,7 @@
8585
"gate": {
8686
"cirq_type": "MeasurementGate",
8787
"num_qubits": 5,
88-
"key": {
89-
"cirq_type": "MeasurementKey",
90-
"name": "",
91-
"path": [],
92-
"qubits": [
93-
{
94-
"cirq_type": "LineQubit",
95-
"x": 0
96-
},
97-
{
98-
"cirq_type": "LineQubit",
99-
"x": 1
100-
},
101-
{
102-
"cirq_type": "LineQubit",
103-
"x": 2
104-
},
105-
{
106-
"cirq_type": "LineQubit",
107-
"x": 3
108-
},
109-
{
110-
"cirq_type": "LineQubit",
111-
"x": 4
112-
}
113-
]
114-
},
88+
"key": "0,1,2,3,4",
11589
"invert_mask": []
11690
},
11791
"qubits": [

0 commit comments

Comments
 (0)