Skip to content

Commit

Permalink
Remove public fields for measurement and controlled ops (quantumlib#5061
Browse files Browse the repository at this point in the history
)

Change public fields to private, and encapsulate with getters and deprecated setters.

xref quantumlib#4851
  • Loading branch information
daxfohl authored and rht committed May 1, 2023
1 parent fda214a commit ca471dd
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 17 deletions.
49 changes: 43 additions & 6 deletions cirq-core/cirq/ops/controlled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import cirq
from cirq import protocols, value
from cirq._compat import deprecated
from cirq.ops import raw_types, controlled_operation as cop
from cirq.type_workarounds import NotImplementedType

Expand Down Expand Up @@ -73,10 +74,10 @@ def __init__(
control_qid_shape = (2,) * num_controls
if num_controls != len(control_qid_shape):
raise ValueError('len(control_qid_shape) != num_controls')
self.control_qid_shape = tuple(control_qid_shape)
self._control_qid_shape = tuple(control_qid_shape)

# Convert to sorted tuples
self.control_values = cast(
self._control_values = cast(
Tuple[Tuple[int, ...], ...],
tuple((val,) if isinstance(val, int) else tuple(sorted(val)) for val in control_values),
)
Expand All @@ -90,11 +91,47 @@ def __init__(

# Flatten nested ControlledGates.
if isinstance(sub_gate, ControlledGate):
self.sub_gate = sub_gate.sub_gate # type: ignore
self.control_values += sub_gate.control_values
self.control_qid_shape += sub_gate.control_qid_shape
self._sub_gate = sub_gate.sub_gate # type: ignore
self._control_values += sub_gate.control_values
self._control_qid_shape += sub_gate.control_qid_shape
else:
self.sub_gate = sub_gate
self._sub_gate = sub_gate

@property
def control_qid_shape(self) -> Tuple[int, ...]:
return self._control_qid_shape

@control_qid_shape.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def control_qid_shape(self, control_qid_shape: Tuple[int, ...]):
self._control_qid_shape = control_qid_shape

@property
def control_values(self) -> Tuple[Tuple[int, ...], ...]:
return self._control_values

@control_values.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def control_values(self, control_values: Tuple[Tuple[int, ...], ...]):
self._control_values = control_values

@property
def sub_gate(self) -> 'cirq.Gate':
return self._sub_gate

@sub_gate.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def sub_gate(self, sub_gate: 'cirq.Gate'):
self._sub_gate = sub_gate

def num_controls(self) -> int:
return len(self.control_qid_shape)
Expand Down
13 changes: 13 additions & 0 deletions cirq-core/cirq/ops/controlled_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,16 @@ def num_qubits(self) -> int:
(0.25, cirq.unitary(cirq.CZ)),
],
)


def test_setters_deprecated():
gate = cirq.ControlledGate(cirq.Z)
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.sub_gate = cirq.X
assert gate.sub_gate == cirq.X
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.control_qid_shape = (3, 3)
assert gate.control_qid_shape == (3, 3)
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.control_values = ((3,), (3,))
assert gate.control_values == ((3,), (3,))
49 changes: 43 additions & 6 deletions cirq-core/cirq/ops/controlled_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import numpy as np

from cirq import protocols, qis, value
from cirq._compat import deprecated
from cirq.ops import raw_types, gate_operation, controlled_gate
from cirq.type_workarounds import NotImplementedType

Expand All @@ -54,7 +55,7 @@ def __init__(
if len(control_values) != len(controls):
raise ValueError('len(control_values) != len(controls)')
# Convert to sorted tuples
self.control_values = cast(
self._control_values = cast(
Tuple[Tuple[int, ...], ...],
tuple((val,) if isinstance(val, int) else tuple(sorted(val)) for val in control_values),
)
Expand All @@ -64,13 +65,49 @@ def __init__(
raise ValueError(f'Control values <{val!r}> outside of range for qubit <{q!r}>.')

if not isinstance(sub_operation, ControlledOperation):
self.controls = tuple(controls)
self.sub_operation = sub_operation
self._controls = tuple(controls)
self._sub_operation = sub_operation
else:
# Auto-flatten nested controlled operations.
self.controls = tuple(controls) + sub_operation.controls
self.sub_operation = sub_operation.sub_operation
self.control_values += sub_operation.control_values
self._controls = tuple(controls) + sub_operation.controls
self._sub_operation = sub_operation.sub_operation
self._control_values += sub_operation.control_values

@property
def controls(self) -> Tuple['cirq.Qid', ...]:
return self._controls

@controls.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def controls(self, controls: Tuple['cirq.Qid', ...]):
self._controls = controls

@property
def control_values(self) -> Tuple[Tuple[int, ...], ...]:
return self._control_values

@control_values.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def control_values(self, control_values: Tuple[Tuple[int, ...], ...]):
self._control_values = control_values

@property
def sub_operation(self) -> 'cirq.Operation':
return self._sub_operation

@sub_operation.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def sub_operation(self, sub_operation: 'cirq.Operation'):
self._sub_operation = sub_operation

@property
def gate(self) -> Optional['cirq.ControlledGate']:
Expand Down
14 changes: 14 additions & 0 deletions cirq-core/cirq/ops/controlled_operation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,17 @@ def with_qubits(self, *new_qubits):
(0.25, cirq.unitary(cirq.CZ)),
],
)


def test_setters_deprecated():
q0, q1, q2 = cirq.LineQubit.range(3)
op = cirq.ControlledOperation([q1], cirq.Z(q0))
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
op.sub_operation = cirq.X(q0)
assert op.sub_operation == cirq.X(q0)
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
op.controls = (q2,)
assert op.controls == (q2,)
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
op.control_values = ((3,), (3,))
assert op.control_values == ((3,), (3,))
41 changes: 36 additions & 5 deletions cirq-core/cirq/ops/measurement_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import numpy as np

from cirq import protocols, value
from cirq._compat import deprecated
from cirq.ops import raw_types

if TYPE_CHECKING:
Expand Down Expand Up @@ -65,21 +66,51 @@ def __init__(
self._qid_shape = qid_shape
if len(self._qid_shape) != num_qubits:
raise ValueError('len(qid_shape) != num_qubits')
self.key = key # type: ignore
self.invert_mask = invert_mask or ()
self._mkey = (
key if isinstance(key, value.MeasurementKey) else value.MeasurementKey(name=key)
)
self._invert_mask = invert_mask or ()
if self.invert_mask is not None and len(self.invert_mask) > self.num_qubits():
raise ValueError('len(invert_mask) > num_qubits')

@property
def key(self) -> str:
return str(self.mkey)

@key.setter
@key.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def key(self, key: Union[str, 'cirq.MeasurementKey']):
if isinstance(key, value.MeasurementKey):
self.mkey = key
self._mkey = key
else:
self.mkey = value.MeasurementKey(name=key)
self._mkey = value.MeasurementKey(name=key)

@property
def mkey(self) -> 'cirq.MeasurementKey':
return self._mkey

@mkey.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def mkey(self, mkey: 'cirq.MeasurementKey'):
self._mkey = mkey

@property
def invert_mask(self) -> Tuple[bool, ...]:
return self._invert_mask

@invert_mask.setter # type: ignore
@deprecated(
deadline="v0.15",
fix="The mutators of this class are deprecated, instantiate a new object instead.",
)
def invert_mask(self, invert_mask: Tuple[bool, ...]):
self._invert_mask = invert_mask

def _qid_shape_(self) -> Tuple[int, ...]:
return self._qid_shape
Expand Down
19 changes: 19 additions & 0 deletions cirq-core/cirq/ops/measurement_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,22 @@ def test_act_on_qutrit():
)
cirq.act_on(m, args)
assert args.log_of_measurement_results == {'out': [0, 2]}


def test_setters_deprecated():
gate = cirq.MeasurementGate(1, key='m', invert_mask=(False,))
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.key = 'n'
assert gate.key == 'n'
assert gate.mkey == cirq.MeasurementKey('n')
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.key = cirq.MeasurementKey('o')
assert gate.key == 'o'
assert gate.mkey == cirq.MeasurementKey('o')
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.mkey = cirq.MeasurementKey('p')
assert gate.key == 'p'
assert gate.mkey == cirq.MeasurementKey('p')
with cirq.testing.assert_deprecated('mutators', deadline='v0.15'):
gate.invert_mask = (True,)
assert gate.invert_mask == (True,)

0 comments on commit ca471dd

Please sign in to comment.