From 203035a72de8c299b8b0c66fe617a9bd03ed8300 Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Thu, 7 Apr 2022 10:13:42 -0700 Subject: [PATCH] Deprecate json_serializable_dataclass (#5208) Fixes #4460 --- .../experiments/cross_entropy_benchmarking.py | 5 ++- .../grid_parallel_two_qubit_xeb.py | 14 ++++--- .../cirq/protocols/json_serialization.py | 3 +- .../cirq/protocols/json_serialization_test.py | 38 ++++++++++++------- .../cirq_google/calibration/phased_fsim.py | 35 +++++++++++------ 5 files changed, 63 insertions(+), 32 deletions(-) diff --git a/cirq-core/cirq/experiments/cross_entropy_benchmarking.py b/cirq-core/cirq/experiments/cross_entropy_benchmarking.py index b14e428271d..4228ea6530a 100644 --- a/cirq-core/cirq/experiments/cross_entropy_benchmarking.py +++ b/cirq-core/cirq/experiments/cross_entropy_benchmarking.py @@ -98,7 +98,7 @@ def purity(self) -> float: return self.cycle_depolarization**2 -@protocols.json_serializable_dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class CrossEntropyResult: """Results from a cross-entropy benchmarking (XEB) experiment. @@ -208,6 +208,9 @@ def _from_json_dict_(cls, data, repetitions, **kwargs): purity_data=purity_data, ) + def _json_dict_(self): + return protocols.dataclass_json_dict(self) + def __repr__(self) -> str: args = f'data={[tuple(p) for p in self.data]!r}, repetitions={self.repetitions!r}' if self.purity_data is not None: diff --git a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb.py b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb.py index 60f87eff67a..0b87dc1ea2d 100644 --- a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb.py +++ b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb.py @@ -20,7 +20,7 @@ by executing circuits that act on many pairs simultaneously. """ -from typing import Any, Iterable, List, Optional, Sequence, TYPE_CHECKING, Tuple, cast +from typing import Any, Dict, Iterable, List, Optional, Sequence, TYPE_CHECKING, Tuple, cast import collections from concurrent.futures import ThreadPoolExecutor import dataclasses @@ -46,7 +46,6 @@ ) if TYPE_CHECKING: - from typing import Dict import cirq DEFAULT_BASE_DIR = os.path.expanduser( @@ -101,7 +100,7 @@ def load(params: Any, base_dir: str) -> Any: return protocols.read_json(filename) -@protocols.json_serializable_dataclass +@dataclasses.dataclass class GridParallelXEBMetadata: """Metadata for a grid parallel XEB experiment. @@ -109,14 +108,17 @@ class GridParallelXEBMetadata: data_collection_id: The data collection ID of the experiment. """ - qubits: List['cirq.Qid'] + qubits: Sequence['cirq.Qid'] two_qubit_gate: 'cirq.Gate' num_circuits: int repetitions: int - cycles: List[int] - layers: List[GridInteractionLayer] + cycles: Sequence[int] + layers: Sequence[GridInteractionLayer] seed: Optional[int] + def _json_dict_(self): + return protocols.dataclass_json_dict(self) + def __repr__(self) -> str: return ( 'cirq.experiments.grid_parallel_two_qubit_xeb.' diff --git a/cirq-core/cirq/protocols/json_serialization.py b/cirq-core/cirq/protocols/json_serialization.py index 1c81f70ebbd..b82b10576ae 100644 --- a/cirq-core/cirq/protocols/json_serialization.py +++ b/cirq-core/cirq/protocols/json_serialization.py @@ -39,7 +39,7 @@ import sympy from typing_extensions import Protocol -from cirq._compat import deprecated_parameter +from cirq._compat import deprecated, deprecated_parameter from cirq._doc import doc_private from cirq.type_workarounds import NotImplementedType @@ -190,6 +190,7 @@ class name via a dot (.) # Copying the Python API, whose usage of `repr` annoys pylint. # pylint: disable=redefined-builtin +@deprecated(deadline='v0.15', fix='Implement _json_dict_ using cirq.dataclass_json_dict()') def json_serializable_dataclass( _cls: Optional[Type] = None, *, diff --git a/cirq-core/cirq/protocols/json_serialization_test.py b/cirq-core/cirq/protocols/json_serialization_test.py index c1c5eb6d2e8..fa2c862df37 100644 --- a/cirq-core/cirq/protocols/json_serialization_test.py +++ b/cirq-core/cirq/protocols/json_serialization_test.py @@ -855,10 +855,14 @@ def test_pathlib_paths(tmpdir): def test_json_serializable_dataclass(): - @cirq.json_serializable_dataclass - class MyDC: - q: cirq.LineQubit - desc: str + with cirq.testing.assert_deprecated( + "Implement _json_dict_ using cirq.dataclass_json_dict()", deadline="v0.15" + ): + + @cirq.json_serializable_dataclass + class MyDC: + q: cirq.LineQubit + desc: str my_dc = MyDC(cirq.LineQubit(4), 'hi mom') @@ -885,10 +889,14 @@ def custom_resolver(name): def test_json_serializable_dataclass_parenthesis(): - @cirq.json_serializable_dataclass() - class MyDC: - q: cirq.LineQubit - desc: str + with cirq.testing.assert_deprecated( + "Implement _json_dict_ using cirq.dataclass_json_dict()", deadline="v0.15" + ): + + @cirq.json_serializable_dataclass() + class MyDC: + q: cirq.LineQubit + desc: str def custom_resolver(name): if name == 'MyDC': @@ -918,11 +926,15 @@ def custom_resolver(name): def test_json_serializable_dataclass_namespace(): - @cirq.json_serializable_dataclass(namespace='cirq.experiments') - class QuantumVolumeParams: - width: int - depth: int - circuit_i: int + with cirq.testing.assert_deprecated( + "Implement _json_dict_ using cirq.dataclass_json_dict()", deadline="v0.15" + ): + + @cirq.json_serializable_dataclass(namespace='cirq.experiments') + class QuantumVolumeParams: + width: int + depth: int + circuit_i: int qvp = QuantumVolumeParams(width=5, depth=5, circuit_i=0) diff --git a/cirq-google/cirq_google/calibration/phased_fsim.py b/cirq-google/cirq_google/calibration/phased_fsim.py index e0f03ca67e2..30f273dc8a9 100644 --- a/cirq-google/cirq_google/calibration/phased_fsim.py +++ b/cirq-google/cirq_google/calibration/phased_fsim.py @@ -47,11 +47,6 @@ if TYPE_CHECKING: import cirq_google - # Workaround for mypy custom dataclasses (python/mypy#5406) - from dataclasses import dataclass as json_serializable_dataclass -else: - from cirq.protocols import json_serializable_dataclass - _FLOQUET_PHASED_FSIM_HANDLER_NAME = 'floquet_phased_fsim_characterization' _XEB_PHASED_FSIM_HANDLER_NAME = 'xeb_phased_fsim_characterization' @@ -94,7 +89,7 @@ def _create_pairs_from_moment( return tuple(pairs), gate -@json_serializable_dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class PhasedFSimCharacterization: """Holder for the unitary angles of the cirq.PhasedFSimGate. @@ -202,6 +197,9 @@ def override_by(self, other: 'PhasedFSimCharacterization') -> 'PhasedFSimCharact """ return other.merge_with(self) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) + SQRT_ISWAP_INV_PARAMETERS = PhasedFSimCharacterization( theta=np.pi / 4, zeta=0.0, chi=0.0, gamma=0.0, phi=0.0 @@ -429,7 +427,7 @@ def parse_result( """Decodes the characterization result issued for this request.""" -@json_serializable_dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class XEBPhasedFSimCalibrationOptions(PhasedFSimCalibrationOptions): """Options for configuring a PhasedFSim calibration using XEB. @@ -496,8 +494,11 @@ def _from_json_dict_(cls, **kwargs): kwargs['cycle_depths'] = tuple(kwargs['cycle_depths']) return cls(**kwargs) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) -@json_serializable_dataclass(frozen=True) + +@dataclasses.dataclass(frozen=True) class LocalXEBPhasedFSimCalibrationOptions(XEBPhasedFSimCalibrationOptions): """Options for configuring a PhasedFSim calibration using a local version of XEB. @@ -542,8 +543,11 @@ def create_phased_fsim_request( ): return LocalXEBPhasedFSimCalibrationRequest(pairs=pairs, gate=gate, options=self) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) + -@json_serializable_dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class FloquetPhasedFSimCalibrationOptions(PhasedFSimCalibrationOptions): """Options specific to Floquet PhasedFSimCalibration. @@ -590,6 +594,9 @@ def create_phased_fsim_request( ) -> 'FloquetPhasedFSimCalibrationRequest': return FloquetPhasedFSimCalibrationRequest(pairs=pairs, gate=gate, options=self) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) + """Floquet PhasedFSimCalibrationOptions options with all angles characterization requests set to True.""" @@ -821,7 +828,7 @@ def _parse_characterized_angles( return dict(records) -@json_serializable_dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class LocalXEBPhasedFSimCalibrationRequest(PhasedFSimCalibrationRequest): """PhasedFSim characterization request for local cross entropy benchmarking (XEB) calibration. @@ -856,8 +863,11 @@ def _from_json_dict_( instantiation_pairs = tuple((q_a, q_b) for q_a, q_b in pairs) return cls(instantiation_pairs, gate, options) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) -@json_serializable_dataclass(frozen=True) + +@dataclasses.dataclass(frozen=True) class XEBPhasedFSimCalibrationRequest(PhasedFSimCalibrationRequest): """PhasedFSim characterization request for cross entropy benchmarking (XEB) calibration. @@ -915,6 +925,9 @@ def _from_json_dict_( instantiation_pairs = tuple((q_a, q_b) for q_a, q_b in pairs) return cls(instantiation_pairs, gate, options) + def _json_dict_(self): + return cirq.dataclass_json_dict(self) + class IncompatibleMomentError(Exception): """Error that occurs when a moment is not supported by a calibration routine."""